【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信

这篇具有很好参考价值的文章主要介绍了【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言

手把手教你从0开始编写TCP服务器程序,体验开局一块砖,大厦全靠垒

为了避免篇幅过长使读者感到乏味,对【TCP服务器的开发】进行分阶段实现,一步步进行优化升级。
【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信,C/C++技术干货,tcp/ip,服务器,网络协议,网络,linux,udp,后端

二、需要使用到的API

2.1、socket()函数

函数原型:

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);

这个函数建立一个协议族、协议类型、协议编号的socket文件描述符。如果函数调用成功,会返回一个标识这个套接字的文件描述符,失败的时候返回-1并设置了errno。

domain参数值含义:

名称 含义
PF_UNIX,PF_LOCAL 本地通信
AF_INET,PF_INET IPv4协议
PF_INET6 IPv6协议
PF_NETLINK 内核用户界面设备
PF_PACKET 底层包访问

type参数值含义:

名称 含义
SOCK_STREAM TCP连接,提供序列化的、可靠的、双向连接的字节流。支持带外数据传输
SOCK_DGRAM UDP连接
SOCK_SEQPACKET 序列化包,提供一个序列化的、可靠的、双向的数据传输通道,数据长度定常。每次调用读系统调用时数据需要将全部数据读出
:SOCK_PACKET 专用类型
SOCK_RDM 提供可靠的数据报文,不保证数据有序
SOCK_RAW 提供原始网络协议访问

protocol参数含义:
通常某协议中只有一种特定类型,这样protocol参数仅能设置为0;如果协议有多种特定的类型,就需要设置这个参数来选择特定的类型。

2.2、bind()函数

函数原型:

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);

参数说明:

  • 第1个参数sockfd是用socket()函数创建的文件描述符。
  • 第2个参数my_addr是指向一个结构为sockaddr参数的指针,sockaddr中包含了地址、端口和IP地址的信息。
  • 第3个参数addrlen是my_addr结构的长度,可以设置成sizeof(struct sockaddr)。

bind()函数的返回值为0时表示绑定成功,-1表示绑定失败并设置了errno。

2.3、listen()函数

函数原型:

#include<sys/socket.h>
int listen(int sockfd, int backlog);

参数说明:

  • 第1个参数sockfd是用socket()函数创建的文件描述符。
  • 第2个参数backlog规定了内核应该为相应套接字排队的最大连接个数。

2.4、accept()函数

函数原型:

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

参数说明:

  • sockefd:套接字描述符,该套接字在listen() 后监听连接。
  • addr:(可选)指针。指向一个缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。
  • addrlen:(可选)指针。输入参数,配合addr一起使用,指向存有addr地址长度的整形数。

2.5、recv()函数

函数原型:

#include<sys/types.h>
#include<sys/socket.h>
int recv( int fd, char *buf, int len, int flags);

参数说明:

  • 第一个参数指定接收端套接字描述符;
  • 第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
  • 第三个参数指明buf的长度;
  • 第四个参数一般置0。

返回值:

  • 返回大于0的数,表示介绍到的数据大小。
  • 返回0,表示连接断开。
  • 返回-1,表示接受数据错误。

2.6、send()函数

函数原型:

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数说明:

  • sockfd:向套接字中发送数据
  • buf:要发送的数据的首地址
  • len:要发送的数据的字节
  • int flags:设置为MSG_DONTWAITMSG 时 表示非阻塞,设置为0时 功能和write一样。

返回值:成功返回实际发送的字节数,失败返回 -1并设置了errno。

2.7、strerror()函数

strerror()函数返回一个指向字符串的指针,该字符串描述参数errnum中传递的错误代码,可能使用当前语言环境的LC_MESSAGES部分来选择适当的语言。(例如,如果errnum为EINVAL,则返回的描述将为“无效参数”。)应用程序不能修改此字符串,但可以通过随后调用strerror()strerror_l()来修改。任何其他库函数,包括perror(),都不会修改此字符串。

函数原型:

#include <string.h>

char *strerror(int errnum);

int strerror_r(int errnum, char *buf, size_t buflen);
/* XSI-compliant */
char *strerror_r(int errnum, char *buf, size_t buflen);
/* GNU-specific */
char *strerror_l(int errnum, locale_t locale);

三、实现步骤

一对一服务器设计:
【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信,C/C++技术干货,tcp/ip,服务器,网络协议,网络,linux,udp,后端

(1)创建socket。

int listenfd=socket(AF_INET,SOCK_STREAM,0);
if(listenfd==-1){
    printf("errno = %d, %s\n",errno,strerror(errno));
    return SOCKET_CREATE_FAILED;
}

(2)绑定地址。

struct sockaddr_in server;
memset(&server,0,sizeof(server));

server.sin_family=AF_INET;
server.sin_addr.s_addr=htonl(INADDR_ANY);
server.sin_port=htons(LISTEN_PORT);

if(-1==bind(listenfd,(struct sockaddr*)&server,sizeof(server))){
    printf("errno = %d, %s\n",errno,strerror(errno));
    close(listenfd);
    return SOCKET_BIND_FAILED;
}

(3)设置监听。

if(-1==listen(listenfd,BLOCK_SIZE)){
   printf("errno = %d, %s\n",errno,strerror(errno));
   close(listenfd);
   return SOCKET_LISTEN_FAILED;
}

(4)接收连接。

struct sockaddr_in client;
memset(&client,0,sizeof(client));
socklen_t len=sizeof(client);
    
int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
if(clientfd==-1){
    printf("errno = %d, %s\n",errno,strerror(errno));
    close(listenfd);
    return SOCKET_ACCEPT_FAILED;
}

(5)接收数据。

char buf[BUFFER_LENGTH]={0};
ret=recv(clientfd,buf,BUFFER_LENGTH,0);
if(ret==0) {
     printf("connection dropped\n");

}
printf("recv --> %s\n",buf);

(6)发送数据。

if(-1==send(clientfd,buf,ret,0))
{
    printf("errno = %d, %s\n",errno,strerror(errno));
}

(7)关闭文件描述符。

close(clientfd);
close(listenfd);

四、完整代码

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>

#include <errno.h>
#include <string.h>
#include <unistd.h>


#define LISTEN_PORT     8888
#define BLOCK_SIZE      10
#define BUFFER_LENGTH   1024

enum ERROR_CODE{
    SOCKET_CREATE_FAILED=-1,
    SOCKET_BIND_FAILED=-2,
    SOCKET_LISTEN_FAILED=-3,
    SOCKET_ACCEPT_FAILED=-4
};



int main(int argc,char **argv)
{
    // 1.
    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    if(listenfd==-1){
        printf("errno = %d, %s\n",errno,strerror(errno));
        return SOCKET_CREATE_FAILED;
    }

    // 2.
    struct sockaddr_in server;
    memset(&server,0,sizeof(server));

    server.sin_family=AF_INET;
    server.sin_addr.s_addr=htonl(INADDR_ANY);
    server.sin_port=htons(LISTEN_PORT);

    if(-1==bind(listenfd,(struct sockaddr*)&server,sizeof(server))){
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(listenfd);
        return SOCKET_BIND_FAILED;
    }

    // 3.
    if(-1==listen(listenfd,BLOCK_SIZE)){
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(listenfd);
        return SOCKET_LISTEN_FAILED;
    }

    // 4.
    struct sockaddr_in client;
    memset(&client,0,sizeof(client));
    socklen_t len=sizeof(client);
    
    int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
    if(clientfd==-1){
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(listenfd);
        return SOCKET_ACCEPT_FAILED;
    }

    printf("client fd = %d\n",clientfd);
    int ret=1;
    while(ret>0){
        // 5.
        char buf[BUFFER_LENGTH]={0};
        ret=recv(clientfd,buf,BUFFER_LENGTH,0);
        if(ret==0) {
            printf("connection dropped\n");
            break;

        }
        
        printf("recv --> %s\n",buf);
        if(-1==send(clientfd,buf,ret,0))
        {
            printf("errno = %d, %s\n",errno,strerror(errno));
        }
        
    }
    close(clientfd);
    close(listenfd);

    return 0;
}

编译命令:

gcc -o server server.c

五、TCP客户端

5.1、自己实现一个TCP客户端

自己实现一个TCP客户端连接TCP服务器的代码:

#include <stdio.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <errno.h>
#include <string.h>

#include <unistd.h>
#include <stdlib.h>

#define BUFFER_LENGTH   1024

enum ERROR_CODE{
    SOCKET_CREATE_FAILED=-1,
    SOCKET_CONN_FAILED=-2,
    SOCKET_LISTEN_FAILED=-3,
    SOCKET_ACCEPT_FAILED=-4
};

int main(int argc,char** argv)
{
    if(argc<3)
    {
        printf("Please enter the server IP and port.");
        return 0;
    }
    printf("connect to %s, port=%s\n",argv[1],argv[2]);

    int connfd=socket(AF_INET,SOCK_STREAM,0);
    if(connfd==-1)
    {
        printf("errno = %d, %s\n",errno,strerror(errno));
        return SOCKET_CREATE_FAILED;

    }
    struct sockaddr_in serv;
    serv.sin_family=AF_INET;
    serv.sin_addr.s_addr=inet_addr(argv[1]);
    serv.sin_port=htons(atoi(argv[2]));
    socklen_t len=sizeof(serv);
    int rwfd=connect(connfd,(struct sockaddr*)&serv,len);
    if(rwfd==-1)
    {
        printf("errno = %d, %s\n",errno,strerror(errno));
        close(rwfd);
        return SOCKET_CONN_FAILED;
    }
    int ret=1;
    while(ret>0)
    {
        char buf[BUFFER_LENGTH]={0};
        printf("Please enter the string to send:\n");
        scanf("%s",buf);
        send(connfd,buf,strlen(buf),0);

        memset(buf,0,BUFFER_LENGTH);
        printf("recv:\n");
        ret=recv(connfd,buf,BUFFER_LENGTH,0);
        printf("%s\n",buf);
        
    }
    close(rwfd);
    return 0;
}

编译:

gcc -o client client.c

5.2、Windows下可以使用NetAssist的网络助手工具

【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信,C/C++技术干货,tcp/ip,服务器,网络协议,网络,linux,udp,后端

下载地址:http://old.tpyboard.com/downloads/NetAssist.exe

小结

至此,我们实现了一个一对一的服务器连接,这阶段的TCP服务器代码只能接收一个客户端接入,如果客户端断开了就会直接退出。重点是掌握开发TCP服务器的基本流程,下一章节将介绍在此基础上进行升级,实现可以接受多个客户端的同时接入。
【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信,C/C++技术干货,tcp/ip,服务器,网络协议,网络,linux,udp,后端文章来源地址https://www.toymoban.com/news/detail-773513.html

到了这里,关于【TCP服务器的演变过程】编写第一个TCP服务器:实现一对一的连接通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • TCP服务器的演变过程:使用epoll构建reactor网络模型实现百万级并发(详细代码)

    手把手教你从0开始编写TCP服务器程序,体验开局一块砖,大厦全靠垒。 为了避免篇幅过长使读者感到乏味,对【TCP服务器的开发】进行分阶段实现,一步步进行优化升级。 本节,在上一章节介绍了如何使用epoll开发高效的服务器,本节将介绍使用epoll构建reactor网络模型,实

    2024年02月01日
    浏览(50)
  • TCP服务器的演变过程:C++使用libevent库开发服务器程序

    手把手教你从0开始编写TCP服务器程序,体验开局一块砖,大厦全靠垒。 为了避免篇幅过长使读者感到乏味,对【TCP服务器的开发】进行分阶段实现,一步步进行优化升级。 在上一章节介绍了如何使用epoll构建reactor网络模型开发高效的服务器,有了上一节的基础,本节将介绍

    2024年01月23日
    浏览(37)
  • 用Rust设计一个并发的Web服务:常用Rust库如Tokio、Hyper等,基于TCP/IP协议栈,实现了一个简单的并发Web服务器,并结合具体的代码讲解如何编写并发Web服务器的程序

    作者:禅与计算机程序设计艺术 1994年,互联网泡沫破裂,一批优秀的程序员、工程师纷纷加入到web开发领域。而其中的Rust语言却备受瞩目,它是一种现代系统编程语言,专注于安全和并发。因此,Rust在当下成为最流行的编程语言之一,很多框架也开始使用Rust重构,这使得

    2024年02月06日
    浏览(43)
  • 用netty轻松实现一个高效稳定的TCP服务器

              随着物联网的发展,很多项目都开始涉及到了tcp连接这块,在这里我们轻松用netty去实现,站在巨人的肩膀上。 关于netty包引用: 实现TCP服务器代码 依赖netty只需几行代码tcp服务:  业务处理代码(参考) 以下是处理报文业务类可参考,注意代码未优化:  运行

    2024年02月22日
    浏览(31)
  • 【网络编程】实现一个简单多线程版本TCP服务器(附源码)

    accept 函数是在服务器端用于接受客户端连接请求的函数,它在监听套接字上等待客户端的连接,并在有新的连接请求到来时创建一个新的套接字用于与该客户端通信。 下面是 accept 函数的详细介绍以及各个参数的意义: sockfd: 是服务器监听套接字的文件描述符,通常是使用

    2024年02月13日
    浏览(38)
  • FPGA实现 TCP/IP 协议栈 服务器 纯VHDL代码编写 提供4套vivado工程源码和技术支持

    FPGA实现 TCP/IP 协议栈 服务器 纯VHDL代码编写 提供4套vivado工程源码和技术支持 没玩过TCP网络通信都不好意思说自己玩儿过FPGA,这是CSDN某大佬说过的一句话,鄙人深信不疑。。。目前网上fpga实现udp协议的源码满天飞,我这里也有不少,但用FPGA纯源码实现TCP的项目却很少,能上

    2024年02月04日
    浏览(38)
  • 配置web服务器+编写简单页面+分析交互过程

    目录 配置web服务器 IIS配置web服务器 C++搭建简单的web服务器 编写web页面 程序测试 IIS配置测试 C++程序测试 Wireshark捕获交互过程及分析 三次握手 请求报文 请求行 请求头 请求体 响应报文 响应行 响应头 响应体 四次挥手 实验中遇到的问题及分析 参考资料 使用Windows自带的II

    2024年02月08日
    浏览(33)
  • FPGA实现10G万兆网TCP/IP 协议栈,纯VHDL代码编写,提供服务器和客户端2套工程源码和技术支持

    目前网上fpga实现udp协议的源码满天飞,我这里也有不少,但用FPGA纯源码实现TCP的项目却很少,能上板调试跑通的项目更是少之又少,甚至可以说是凤毛菱角,但很不巧,本人这儿就有一个; 本设采用纯VHDL实现了10G万兆网TCP/IP协议栈,该协议栈分为TCP服务器核客户端,没有使

    2024年02月09日
    浏览(49)
  • 网络通信(13)-C#TCP服务器和客户端同时在一个进程实现的实例

    有时项目需求中需要服务器和客户端同时在一个进程实现,一边需要现场接收多个客户端的数据,一边需要将数据汇总后发送给远程服务器。下面通过实例演示此项需求。 C#TCP服务器和客户端同时在一个进程实现的实例如下: 界面设计 UI文件代码

    2024年01月22日
    浏览(47)
  • TCP/IP客户端和服务器端建立通信过程

    使用Qt提供的类进行基于 TCP 的套接字通信需要用到两个类: QTcpServer 类用于监听客户端连接以及和客户端建立连接,在使用之前先介绍一下这个类提供的一些常用API函数: 构造函数 给监听的套接字设置监听 listen() 函数 在代码中 通过启动监听按钮 设置监听 参数: address :

    2024年02月07日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包