小目标1:编写一个基本的TCP服务器程序

发布时间 2023-10-05 11:07:13作者: †CS.Renascence

小目标1:编写一个基本的TCP服务器程序

头文件

1  #include<cstdio>//C++标准库的头文件
2  #include<unistd.h>//Unix标准头文件
3  #include<sys/types.h>//这个头文件定义了各种系统相关的数据类型
4  #include<sys/socket.h>//这个头文件用于网络编程,包含了与套接字(socket)相关的函数和数据结构的声明
5  #include<arpa/inet.h>//通常用于处理IP地址和套接字地址的转换
6  #include<string.h>//字符串头文件,因为后面有用到memset

第一步:创建套接字描述符

PS:相当于要买一部手机

1  //创建一个套接字描述符
2  int server_socket;//这是一个唯一标识套接字的整数
3  server_socket=socket(AF_INET,SOCK_STREAM,0);
4   /*
5  创建了一个套接字,并将其文件描述符存储在 server_socket 变量中
6  AF_INET 表示IPv4地址族
7  SOCK_STREAM: 这是套接字类型,表示创建的套接字将使用面向连接的TCP协议
8  0: 这是套接字的协议参数,通常设置为0
9  */

 

第二步:要告诉这个服务器我们的IP地址和端口号

PS:相当于购买一个电话卡

我们要有一个保存IP地址和端口的变量,引入#include<arpa/inet.h>头文件

 struct sockaddr_in server_addr://存储套接字信息的变量
 server_addr.sin_family=AF_INET;//指定了地址族为 AF_INET
 server_addr.sin_addr.s_addr=INADDR_ANY;//表示服务器将接受来自任何可用网络接口的连接请求
 server_addr.sin_port=htons(6666);//端口号不可以直接用数字赋值,htons将主机字节序(通常是小端字节序)的端口号转换为网络字节序(大端字节序)
 ​

 

第三步:把我们设置好的ip地址和端口号绑定到我们的server_socket描述符上

PS:相当于把电话卡插到手机上

bind 函数将服务器套接字 server_socket 绑定到特定的地址,成功绑定返回0

bind的参数解释如下:

  • server_socket: 它将被绑定到特定的地址和端口。

  • &server_addr: bind 函数需要一个指向 struct sockaddr 类型的指针

  • sizeof(server_addr): 要绑定的地址结构体的大小,通常使用 sizeof 运算符来获取。

perror 函数用于打印与上一个系统调用相关的错误信息。在这里,它将打印错误信息并指明是因为绑定失败而导致的。

 if(bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))<0){
     perror("server bind error:");
     return 0;
 }

第四步:调用listen开始监听程序

PS:相当于把电话放在身上,电话铃响了我们就可以听到声音

listen 函数的参数如下:

  • server_socket: 服务器套接字

  • 10: 这是指定服务器套接字可以排队等待的连接请求的最大数量。这个数字通常被称为待处理连接队列的长度。在这里,设置为10表示服务器可以同时处理的最大连接请求数为10个。

如果 listen 函数成功,它将返回0,否则将返回-1。

 if(listen(server_socket,10)<0){
     perror("server listen error:");
     return 0;
 }

第五步:等待服务器连接

accept函数的特点,我们的程序调用这个函数的时候,如果没有客户端连接到我们的服务器上,那么这个函数将会堵塞(停在这里不走了),直到有客户端链接到服务器上,这个函数将解堵塞,并且返回一个新的套接字描述符,那么后期和客户端的通讯都交给这个新的套接字描述符来负责

 

 printf("TCP服务器准备完成,等待客户端的连接");
 int accept_socket;//创建一个存储接受到的客户端连接的套接字文件描述符。
 char buffer[50]={0};//定义缓冲区,用于暂时存储接收和发送的数据
 int res=0;//后续用到
 accept_socket=accept(server_socket,NULL,NULL);
 printf("有客户端连接到服务器!\n");
 while(1)//服务器将持续接收和发送数据,直到手动停止程序。
 {
     //read函数就是接受客户端发来的数据,存储到buffer里面,返回值表示实际上从accept_socket那边读取到的字节数
     
     res=read(accept_socket,buffer,sizeof(buffer));
     printf("client read %s\n",buffer);
     //向accept_socket写入buffer中数据,写的数据的字节数为res
     write(accept_socket,buffer,res);
     memset(buffer,0,sizeof(buffer));//缓冲区清零,便于接收下一次的数据
     //服务器收到数据之后原封不动地返回客户端
 }
 ​

完整代码如下:

 #include<cstdio>//C++标准库的头文件
 #include<unistd.h>//Unix标准头文件
 #include<sys/types.h>//这个头文件定义了各种系统相关的数据类型
 #include<sys/socket.h>//这个头文件用于网络编程,包含了与套接字(socket)相关的函数和数据结构的声明
 #include<arpa/inet.h>//通常用于处理IP地址和套接字地址的转换
 #include<string.h>//字符串头文件,因为后面有用到memset
 int main() {
     //创建一个套接字描述符
     int server_socket;//这是一个唯一标识套接字的整数
     server_socket = socket(AF_INET, SOCK_STREAM, 0);
     /*
    创建了一个套接字,并将其文件描述符存储在 server_socket 变量中
    AF_INET 表示IPv4地址族
    SOCK_STREAM: 这是套接字类型,表示创建的套接字将使用面向连接的TCP协议
    0: 这是套接字的协议参数,通常设置为0
    */
     struct sockaddr_in server_addr;//存储套接字信息的变量
     server_addr.sin_family = AF_INET;//指定了地址族为 AF_INET
     server_addr.sin_addr.s_addr = INADDR_ANY;//表示服务器将接受来自任何可用网络接口的连接请求
     server_addr.sin_port = htons(6666);//端口号不可以直接用数字赋值,htons将主机字节序(通常是小端字节序)的端口号转换为网络字节序(大端字节序)
     if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
         perror("server bind error:");
         return 0;
     }
     if (listen(server_socket, 10) < 0) {
         perror("server listen error:");
         return 0;
     }
     printf("TCP服务器准备完成,等待客户端的连接");
     int accept_socket;//创建一个存储接受到的客户端连接的套接字文件描述符。
     int res = 0;//后续用到
     char buffer[50] = { 0 };//定义缓冲区,用于暂时存储接收和发送的数据
     accept_socket = accept(server_socket, NULL, NULL);
     printf("有客户端连接到服务器!\n");
     while (1) //服务器将持续接收和发送数据,直到手动停止程序。
     {
         //read函数就是接受客户端发来的数据,存储到buffer里面,返回值表示实际上从accept_socket那边读取到的字节数
 ​
         res = read(accept_socket, buffer, sizeof(buffer));
         printf("client read %s\n", buffer);
         //向accept_socket写入buffer中数据,写的数据的字节数为res
         write(accept_socket, buffer, res);
         memset(buffer, 0, sizeof(buffer));//缓冲区清零,便于接收下一次的数据
         //服务器收到数据之后原封不动地返回客户端
     }
     return 0;
 }

运行结果如下