网工干货知识

超全学习笔记
当前位置:首页 > 干货知识

使用 `select()` 函数的 TCP 和 UDP 服务器

更新时间:2026年03月27日   作者:spoto   标签(Tag):

先决条件:TCP、UDP

在之前的文章中,我们已经了解到了一些情况。TCP服务器和一台设备UDP不过现在,我们可以将这个同时处理TCP协议的回声服务器与迭代式UDP服务器的功能整合在一起,从而创建一个单一的服务器。该服务器能够利用select机制来同时处理TCP和UDP协议的数据传输。

那个选择/挑选该函数用于在TCP和UDP套接字之间进行选择。该函数向内核发出指令,让内核等待多个事件的任意一个发生。只有当一个或多个事件发生,或者经过指定的时间后,该进程才会被唤醒。

示例/例子内核只会在满足其中一项条件时才会返回结果。

  • 来自{1, 2, 3}中的任何描述符都已准备好被读取。
  • 来自{4, 5, 6}中的任何描述符都已准备好进行写入操作。
  • 已经过去了5秒。

整个过程可以分解为以下几个步骤:

服务器:

  1. 创建TCP连接,即设置监听套接字。
  2. 创建一个UDP套接字
  3. 将两个套接字都绑定到服务器地址上。
  4. 为所选的 descriptor 集初始化一个描述符集合,并计算出最多 2 个描述符的值。我们将等待这些描述符的返回。
  5. 请选择相应的选项,以获取准备好的描述符(TCP或UDP)。
  6. 如果“ready descriptor”是TCP类型的,那么需要处理新的连接;如果“ready descriptor”是UDP类型的,那么就需要接收数据报。

UDP客户端:

  1. 创建一个UDP套接字。
  2. 向服务器发送一条消息。
  3. 请等待,直到收到服务器的回复。
  4. 关闭套接字描述符,然后退出。

TCP客户端:

  1. 创建一个TCP套接字。
  2. 调用“connect”函数来与服务器建立连接。
  3. 当连接被接受后,需要向服务器发送一条消息。
  4. 请阅读服务器的响应内容。
  5. 关闭套接字描述符,然后退出。

必要的功能:

int select(int maxfd, fd_set *readsset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);Returns: positive count of descriptors ready, 0 on timeout, -1 error

论点/论据:

  • maxfd:已准备好的描述符的最大数量。
  • 超时:需要等待多长时间才能让“Select”功能再次可用呢?
struct timeval{long tv_sec;long tv_usec;};if timeout==NULL then wait foreverif timeout == fixed_amount_time then wait until specified timeif timeout == 0 return immediately.
  • 阅读集:我们希望内核能够检测到的描述符集合。
  • 写入集:我们希望内核能够检测到的描述符集合。
  • 除了…之外:我们希望内核能够检测这些异常情况的描述符集合。
int read(int sockfd, void * buff, size_t nbytes);返回值: number of bytes read from the descriptor. -1 on error

论点/论据:

  1. sockfd:接收数据的描述符。
  2. 增益/增强:应用程序的缓冲套接字描述符数据会被复制到这个缓冲区中。
  3. 字节数:需要复制到应用程序缓冲区的字节数。

Server.c

C
// 服务器程序#include<arpa/inet.h>#include<errno.h>#include<netinet/in.h>#include<signal.h>#include<stdio.h>#include<stdlib.h>#include<strings.h>#include<sys/socket.h>#include<sys/types.h>#include<unistd.h>#define PORT 5000定义 MAXLINE 为 1024整数最大值(整数x,整数y){if(x>y)返回x;否则返回y;}整数主要/核心(){整数listenfd,connfd,udpfd,无需额外处理/无需进一步操作,maxfdp1;字符/字母缓冲区[MAXLINE];pid_tchildpid;fd_setrset;ssize_tn;socklen_t长度;常量整数on=1;结构体sockaddr_incliaddr,servaddr;字符/字母*信息/消息=“您好,客户。”;无效/无意义sig_chld(整数);/* 创建用于监听的TCP套接字 */listenfd=插座(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=htonl(INADDR_ANY);servaddr.sin_port=htons(港口);// 将服务器地址结构绑定到listenfd上绑定(listenfd,(结构体sockaddr*)&servaddr,sizeof(servaddr));(listenfd,10);/* 创建UDP套接字 */udpfd=插座(AF_INET,SOCK_DGRAM,0);// 将服务器地址结构绑定到UDP套接字上绑定(udpfd,(结构体sockaddr*)&servaddr,sizeof(servaddr));// 清除描述符集FD_ZERO(&rset);// 获取maxfd的值maxfdp1=最大值(listenfd,udpfd)+1;为了(;;){// 将 listenfd 和 udpfd 设置为 readset 中的内容FD_SET(listenfd,&rset);FD_SET(udpfd,&rset);// 选择处于“准备就绪”状态的描述符nready=选择/挑选(maxfdp1,&rset,空值,空值,空值);// 如果 TCP 套接字可以读取数据,则进行处理。// 通过接受连接来启动它if(FD_ISSET(listenfd,&rset)){长度=sizeof(cliaddr);connfd=接受(listenfd,(结构体sockaddr*)&cliaddr,&长度);if((childpid=叉子()))==0){关闭(listenfd);bzero(缓冲区,sizeof(缓冲区));printf(TCP客户端发送的消息:“”);阅读(connfd,缓冲区,sizeof(缓冲区));放置/安置(缓冲区);书写/写作(connfd,(常量字符/字母*)消息/信息,sizeof(缓冲区));关闭(connfd);退出/离开(0);}关闭(connfd);}// 如果UDP套接字可读取数据,则接收该消息。if(FD_ISSET(udpfd,&rset)){长度=sizeof(cliaddr);bzero(缓冲区,sizeof(缓冲区));printf("\nUDP客户端的消息:“”);n=接收数据(udpfd,缓冲区,sizeof(缓冲区),0,(结构体sockaddr*)&cliaddr,&长度);放置/安置(缓冲区);发送到(udpfd,(const字符/字母*)信息/消息,sizeof(缓冲区),0,(结构体sockaddr*)&cliaddr,sizeof(cliaddr));}}}

TCP_Client.c

C
// TCP客户端程序#include<netinet/in.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/socket.h>#include<sys/types.h>#define PORT 5000#define MAXLINE 1024整数主要/核心(){整数sockfd;字符/字母缓冲区[MAXLINE];字符/字母*信息/消息=“你好,服务器”;结构体sockaddr_inservaddr;整数n,长度;// 创建套接字文件描述符if((sockfd=插座(AF_INET,SOCK_STREAM,0))<0){printf(“插座创建失败”);退出/离开(0);}memset(&servaddr,0,sizeof(servaddr));// 填充服务器信息servaddr.sin_family=AF_INET;servaddr.sin_port=htons(港口);servaddr.sin_addr.s_addr=inet_addr(127.0.0.1);if(连接(sockfd,(结构体sockaddr*)&servaddr,sizeof(servaddr))<0){printf("\n错误:连接失败。\n");}memset(缓冲区,0,sizeof(缓冲区));strcpy(缓冲区,“你好,服务器”);写作/书写(sockfd,缓冲区,sizeof(缓冲区));printf(来自服务器的消息:“”);阅读(sockfd,缓冲区,sizeof(缓冲区));放置/安置(缓冲区);关闭(sockfd);}

UDP_client.c

C
// UDP客户端程序#include<arpa/inet.h>#include<netinet/in.h>#include<stdio.h>#include<stdlib.h>#include<strings.h>#include<sys/socket.h>#include<sys/types.h>#define PORT 5000#define MAXLINE 1024整数主要/核心(){整数sockfd;字符/字母缓冲区[
              马上抢免费试听资格
意向课程:*必选
姓名:*必填
联系方式:*必填
QQ:
思博SPOTO在线咨询

相关资讯

即刻预约

免费试听-咨询课程-获取免费资料