多路复用:
使用一个进程(且只有主线程)同时监控若干个文件描述符的读写情况,这种读写模式称为多路复用
多用于TCP的服务端,用于监控客户端的连接和数据的发送
优点:不需要频繁的创建、销毁进程,从而节约了内存资源、时间资源,也避免了进程之间的竞争、等待
缺点:要求单个客户端的任务不能太多于耗时,否则其他客户端就会感知到卡顿
适合并发量高、但是任务量短小的情景,例如:Web服务器
相关函数:
select:
fd_set 是文件描述符的集合,使用以下函数操作
void FD_CLR(int fd, fd_set *set);
功能:从集合set中删除fd文件描述符
int FD_ISSET(int fd, fd_set *set);
功能:判断集合set中是否存在fd文件描述符
void FD_SET(int fd, fd_set *set);
功能:向集合set中添加fd文件描述符
void FD_ZERO(fd_set *set);
功能:清空集合set
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能:同时监控多个文件描述的读、写、异常操作
nfds:被监控的文件描述符中的最大值+1
readfds:监控读操作的文件描述符集合
writefds:监控写操作的文件描述符集合
exceptfds:监控异常操作的文件描述符集合
timeout:设置超时时间
NULL 一直阻塞,直到某个文件描述符发生了变化
0秒0微秒 非阻塞
大于0秒 等待超时时间,超时返回0
struct timeval {
time_t tv_sec; //秒
suseconds_t tv_usec; //微秒
};
返回值:监控到发生相关操作的文件描述符的个数,超时返回0
错误返回-1
注意:
readfds、writefds、exceptfds 这三个集合参数既是输入也是输出,
调用select时这三个集合需要存储被监控的见闻描述符,当由于有文件描述符发生了相应的操作而导致函数返回时,这三个集合中
存储了这些文件描述符并返回给调用者
select设计不合理的地方:
1、每次调用select都需要向它重新传递被监控的文件描述符集合
2、调用结束后如果想知道具体是哪个文件描述符发生了相关操作,必须把所有被监控的文件描述符进行一遍测试
select的优点:
它是最早的多路复用函数,几乎所有的操作系统都支持,兼容性很高
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
// TCP server
typedef struct sockaddr* SP;
int main(int argc,const char* argv[])
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(0 > sockfd)
{
perror("socket");
return EXIT_FAILURE;
}
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(5566);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t addrlen = sizeof(addr);
if(bind(sockfd,(SP)&addr,addrlen))
{
perror("bind");
return EXIT_FAILURE;
}
if(listen(sockfd,10))
{
perror("listen");
return EXIT_FAILURE;
}
// 定义读操作文件描述符集合
fd_set reads;
FD_ZERO(&reads);
// 把等待连接的sockfd添加到集合
FD_SET(sockfd,&reads);
// 定义超时时间
struct timeval timeout = {5,0};
// 定义记录集合中最大值fd的变量
int max_fd = sockfd;
char buf[4096] = {};
size_t buf_size = sizeof(buf);