socket多路复用

1、函数介绍

系统调用select()提供轮询等待的方式,从多个文件描述符中获取状态变化后的情况。该函数声明如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

此函数第2,3,4三个参数类型都是fd_set*,即文件描述符类型,类似于信号集合的概念,即多个文件描述符一起),具体如下所示:

  • readfds,包含所有可能因状态改变成可读而触发select()函数返回的文件描述符。
  • writefds,包含所有可能因状态变成可写而触发select()函数返回的文件描述符。
  • exceptfds,包含所有可能因状态发生特殊异常,如带外数据到来,而触发select()函数返回的文件描述符。

第一个参数限制以上要检测的文件描述符的范围。测试范围再0到最大文件描述符之间,因此,这个参数值为最大文件描述符值-1.

最后一个参数timeout标识阻塞超时时限,其类型是struct timeval*。

在之际应用可以针对select函数的返回值进行选择性执行处理。

  • 如果函数执行错误,将返回-1.
  • 如果因超时而返回,即再timeout所描述的时间范围内没有任何文件描述符有需要的操作,则返回0,并且将该时间结构体清空为0.
  • 如果因一个或多个文件描述符需要处理而返回,其返回值为产生异常的文件描述符数量,并在相应文件描述符集合中清除那些不需要处理的文件描述符,因此返回后,可以根据文件描述符集合的记录值判断哪些文件描述符需要处理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
socket编程中的多路复用是指通过一种机制,使一个进程可以监视多个文件描述符,一旦某个文件描述符就绪(一般是读写操作准备就绪),能够通知程序进行相应的读写操作。在Linux中,常用的多路复用机制有select、poll和epoll。其中,select是最古老的多路复用机制,poll是select的改进版,而epoll是最新、最高效的多路复用机制。多路复用机制可以大大提高程序的并发性能,使得程序可以同时处理多个客户端请求。 下面是一个简单的使用select实现多路复用的流程图和代码示例: 流程图: ``` 1. 创建socket并绑定端口 2. 将socket设置为非阻塞模式 3. 创建fd_set集合,并将socket加入集合 4. 进入循环,调用select函数,等待文件描述符就绪 5. 如果socket就绪,表示有新的客户端连接请求,调用accept函数接受连接 6. 如果其他文件描述符就绪,表示有客户端发送数据,调用recv函数接收数据并处理 7. 回到步骤4,继续等待文件描述符就绪 ``` 代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/select.h> #define MAX_CLIENTS 10 #define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { int server_fd, client_fd, max_fd, activity, i, valread, sd; struct sockaddr_in address; char buffer[BUFFER_SIZE] = {0}; fd_set readfds; // 创建socket并绑定端口 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(atoi(argv[1])); if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } if (listen(server_fd, MAX_CLIENTS) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } // 将socket设置为非阻塞模式 int flags = fcntl(server_fd, F_GETFL, 0); fcntl(server_fd, F_SETFL, flags | O_NONBLOCK); // 创建fd_set集合,并将socket加入集合 FD_ZERO(&readfds); FD_SET(server_fd, &readfds); max_fd = server_fd; // 进入循环,调用select函数,等待文件描述符就绪 while (1) { activity = select(max_fd + 1, &readfds, NULL, NULL, NULL); if (activity < 0) { perror("select error"); exit(EXIT_FAILURE); } // 如果socket就绪,表示有新的客户端连接请求,调用accept函数接受连接 if (FD_ISSET(server_fd, &readfds)) { if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("accept error"); exit(EXIT_FAILURE); } printf("New connection, socket fd is %d, ip is : %s, port : %d\n", client_fd, inet_ntoa(address.sin_addr), ntohs(address.sin_port)); // 将新的客户端socket加入集合 FD_SET(client_fd, &readfds); if (client_fd > max_fd) { max_fd = client_fd; } } // 如果其他文件描述符就绪,表示有客户端发送数据,调用recv函数接收数据并处理 for (i = server_fd + 1; i <= max_fd; i++) { sd = i; if (FD_ISSET(sd, &readfds)) { if ((valread = recv(sd, buffer, BUFFER_SIZE, 0)) == 0) { // 客户端关闭连接 printf("Client disconnected, socket fd is %d\n", sd); close(sd); FD_CLR(sd, &readfds); } else { // 处理客户端发送的数据 printf("Received message from client, socket fd is %d, message is %s\n", sd, buffer); memset(buffer, 0, BUFFER_SIZE); } } } } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值