IO多路复用可以在一个线程中处理多个文件描述符的读、写、异常事件。将需要处理的文件描述符或者套接字,调用select、poll或者epoll_wait函数就可以委托内核去监听这些文件描述符或者套接字的事件,当事件触发时就能设计相应的处理动作。
一、select
使用select实现TcpServer如下:
基本流程:准备好一个用于监听的socket_fd(设置套接字,绑定端口,开始监听),将socket_fd加入一个fd_set中,调用select监听,将socket_fd的连接事件添加进监听集合,将其他client_fd的读事件处理,循环往复。
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
int main()
{
int socket_fd=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in servaddr;
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//0.0.0.0
servaddr.sin_port=htons(2000);//0-1023
if(-1==bind(socket_fd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr))){
printf("bind failed: %s\n",strerror(errno));
}
listen(socket_fd,10);
printf("lisent finished\n");
fd_set rfds,rset;//一个bit位数组集合 大小为1024
FD_ZERO(&rfds);
FD_SET(socket_fd,&rfds);
int maxfd=socket_fd;//最大fd
while(1){
rset=rfds;//监听集合
int nready=select(maxfd+1,&rset,NULL,NULL,NULL);
if(FD_ISSET(socket_fd,&rset)){//accept
struct sockaddr_in client_addr;
socklen_t len=sizeof(client_addr);
int client_fd=accept(socket_fd,(struct sockaddr*)&client_addr,&len);
FD_SET(client_fd,&rfds);
maxfd=maxfd>client_fd?maxfd:client_fd;//更新最大fd
}
//recv
int i=0;
for(i=socket_fd+1;i<=maxfd;i++){//client_fd事件
if(FD_ISSET(i,&rset)){
char buffer[1024]={0};
int count=recv(i,buffer,1024,0);
if(count==0) {//disconnect
printf("client disconnect:%d\n",i);
FD_CLR(i,&rfds);//将client_fd移除监听集合