文章目录
- select多路IO转接:
- 思路分析
- 代码实现
- select优缺点:
select多路IO转接:
-
原理:借助内核,select来监听,客户端连接、数据通信事件。
void FD_ZERO(fd_set *set); ---清空一个文件描述符集合fd_set rset;FD_ZERO(&rset);void FD_SET(int fd, fd_set *set); ---将待监听的文件描述符,添加到监听集合中FD_SET(3,&rset);FD_SET(5,&rset) ;FD_SET(6,&rset);void FD_CLR(int fd, fd_set *set); --将一个文件描述符从监听集合中移除。FD_CLR(4,&rset); int FD_ISSET(int fd, fd_set *set); ---判断一个文件描述符是否在监听集合中。返回值:在:1;不在。0;FD_ISSET (4,&rset) ;
-
int select(int nfds,fd_set *readfds,fd_set *writefds, fd_set *exceptfds,struct timeva1 *timeout);
nfds:监听的所有文件描述符中,最大文件描述符+1
readfds: 读文件描述符监听集合。传入、传出参数
writefds: 写文件描述符监听集合。传入、传出参数 NULL
exceptfds: 异常文件描述符监听集合。传入、传出参数NULL
timeout: >0:设置监听超时时长。NULL:阻塞监听。0:非阻塞监听,轮询
返回值:>0:所有监听集合(3个)中,满足对应事件的总数。0:没有满足监听条件的文件描述符。-1:errnol
思路分析
lfd = socket() ; 创建套接字
bind() ; 绑定地址结构
listen(); 设置监听上限
fd_set rset,allset; 创建r监听集合
FD_ZERO(&allset); 将r监听集合清空
FD_SET(lfd, &allset); 将lfd 添加至读集合中。
while (1){rset = a11set; 保存监听集合ret = select(lfd+1,&rset,NULL,NULL,NULL); 监听文件描述符集合对应事件。if (ret > 0){ 有监听的描述符满足对应事件if (FD_ISSET(lfd,&rset)){ // 1在。0不在。cfd = accept () ﹔ 建立连接,返回用于通信的文件描述符FD_SET(cfd,,&allset); 添加到监听通信描述符集合中。}for (i = lfd+1;i <=最大文件描述符; i++){FD_ISSET(i,&allset) 有read、write事件read ()小-大write();}}
}
代码实现
服务端:
#define SERV_PORT 6666
int main(int argc,char *argv[]){int i, j,n,nready;int maxfd = 0;int listenfd,connfd;char buf[BUFSIZ]; /*#define INET_ADDRSTRLEN 16*/struct sockaddr_in clie_addr , serv_addr;socklen_t clie_addr_len;listenfd = Socket(AF_INET,SOCK_STREAM,0);int opt = 1;setsockopt(listenfd,soL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));bzero(&serv_addr , sizeof(serv_addr));serv_addr.sin_fanily= AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port= htons(SERV_PORT);Bind(listenfd,(struct sockaddr * )&serv_addr , sizeof(serv_addr));Listen(listenfd,128);fd_set rset,allset; /*set 读事件文件描述符集合allset用来暂存*/maxfd = listenfd;FD_ZERO(&allset);FD_SET(listenfd,&allset); /*构造select监控文件描述符集*/while (1){rset = allset; /*每次循环时都从新设置select监控信号集*/nready = select(naxfd+1,&rset,NULL,NULL,NULL);if (nready < 0)perr_exit( "select error");if (FD_ISSET(listenfd,&rset)) { /*说明有新的客户端链接请求*/clie_addr_len = sizeof(clie_addr);connfd = Accept(listenfd,(struct sockaddr * )&clie_addr,&clie_addr_len); /* Accept不会阻塞*/FD_SET(connfd,&allset); /*向监控文件描述符集合allset添加新的文件描述符connfd*/if (naxfd < connfd)maxfd = connfd;if (0 == --nready) /*只有listenfd有事件,后续的for不需执行*/continue;}for (i = listenfd+1; i <= naxfd; i++) { /*检测哪个clients有数据就绪*/if (FD_ISSET(i,, &rset)) {if ((n = Read(i,buf,sizeof(buf))) == 0) /*当client关闭链接时,服务器端也关闭对应链接*/close(i);FD_CLR(i, &allset); /*解除select对此文件描述符的监控*/}else if (n > 0) {for (j = 0; j <n; j++)buf[j] = toupper(buf[j]);write(i, buf, n);}}}}return 0;
}
客户端:
int main(int argc,char *argv[]){struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd,n;sockfd = Socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr ,sizeof(servaddr ));servaddr.sin_fanily = AF_INET;inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr.s_addr);servaddr.sin_port = htons(SERV_PORT);Connect(sockfd,(struct sockaddr *)&servaddr ,sizeof(servaddr));printf("---------- --connect ok----------------\n");while (fgets(buf,MAXLINE,stdin) != NULL){write(sockfd,buf, strlen(buf));n = Read(sockfd,buf,MAXLINE);if (n == 0) {printf( "the other side has been closed.\n");break;}elsewrite(STDOUT_FILENO,buf,n);}close(sockfd);return 0;
}
select优缺点:
缺点:
监听上限受文件描述符限制。最大1024.
检测满足条件的fd,自己添加业务逻辑提高小。提高了编码难度。
优点:
跨平台。win、linux、macOS、Unix、类Unix、mips