C语言socket编程-select
前言
在早期的Unix系统中,每个进程都有一个单独的I/O流,这种方式效率低下,因为每个I/O操作都需要等待数据的到来。为了提高效率,Unix引入了select函数,它可以同时监听多个文件描述符,随着网络通信的发展,select函数也被广泛应用于网络编程中,它可以同时监听多个网络连接,当其中任意一个连接有数据到来时,就可以进行相应的处理,从而实现高效的网络通信。
一、简介
在socket编程中,select是一种多路复用 I/O 模型,它可以同时监视多个文件描述符的读写状态,当某个文件描述符就绪时,select会返回该文件描述符,从而实现异步 I/O。
二、select的工作原理
1.工作原理:
- 服务端初始化socket,绑定端口,监听端口
- 将sockfd和clientpool中有效的句柄添加到fdset中,调用select等待句柄就绪
- 客户端初始化socket,发送建链请求
- 服务端select中感知到sockfd已就绪,函数返回,调用accept与客户端完成建链,并生成connfd,再将connfd添加到clientpool中
- 客户端向服务端发送数据
- 服务端遍历clientpool,接收数据,并向客户端返回响应
- 客户端传输数据完成后断开连接
- 服务端收到断链事件后,将句柄从clientpool中移除
2.fd_set数据模型
- 假设fd_set长度为1字节(实际使用时根据操作系统而定),fd_set中每一位对应一个句柄fd,1字节长度的fd_set可以表示8个句柄
- 初始化fd_set set; FD_ZERO(&set),set可以表示为0000 0000
- 若有fd = 1;执行FD_SET(fd, &set)后,set可以表示为0000 0010
- 若再加入fd = 3, fd = 5; 执行FD_SET后,set可以表示为0010 1010
- 执行select(sockFd, &set, NULL, NULL, NULL)后代码会阻塞在这里等待句柄就绪
- 若此时句柄1和句柄5都发生可读事件,则select函数返回,set此时为0010 0010。没有事件发生的句柄3被清空
三、常用函数及说明
void FD_ZERO(fd_set *set); // 初始化集合,使所有位都置为0
void FD_SET(int fd, fd_set *set); // 将句柄fd添加到集合中
void FD_CLR(int fd, fd_set *set); // 从集合中删除句柄fd
int FD_ISSET(int fd, fd_set *set); // 判断句柄fd是否在集合中
四、代码示例
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <errno.h>
#define ERR 1
#define OK 0
#define ADDR "127.0.0.1" // 本地IP
#define PORT 23 // 本地端口
#define BACKLOG 1024 // 监听队列长度
#define MAX_SIZE 1024 // 接收数据缓冲区长度
#define CLIENT_NUM 10 // 可以同时连接的客户端数量
int g_clientFds[CLIENT_NUM] = {
0};
/**
* @brief 输出fd_set当前存储状态
*/
void PrintFdSet(fd_set *