IO多路复用之select
来源:互联网 发布:大量收购淘宝买家信息 编辑:程序博客网 时间:2024/06/05 04:07
前言:
select系统调用的用途是:在指定的一段时间内,监听用户感兴趣的文件描述符上的可读、可写和异常事件。
select API:
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
FD_ZERO(fd_set *fd_set); //清除fdset的所有位
FD_SET(int fd, fd_set *fd_set); //设置fdset的位fd
FD_CLR(int fd, fd_set *fd_set): //清除fdset的位fd
int FD_ISSET(int fd, fd_set *fdset); //测试fdset的位fd是否被设置(文件描述符fd是否就绪)
文件描述符的就绪条件:
--可读
1、socket内核接收缓冲区中的字节数大于或等于其低水位标记SO_RCVLOWAT。此时我们可以无阻塞地读取该文件描述符,并且读操作返回的字节数大于0。
2、socket通信的对方关闭连接。此时对该socket的读操作将返回0。
3、socket上有未处理的错误。
--可写
1、socket内核发送缓冲区中的可用字节数大于或等于其低水位标记SO_SNDLOWAT,此时我们可以无阻塞地写该socket,并且写操作返回的字节数大于0。
2、socket的写操作被关闭。
3、socket使用非阻塞connect连接成功或者失败(超时)之后。
4、socket上有未处理的错误。
编码实例
服务端select_s.c
/* *单进程IO多路复用select模型 * */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <netinet/in.h>#include <sys/socket.h>#include <sys/select.h>#include <unistd.h>#include <sys/types.h>#define IPADDRESS "127.0.0.1"#define PORT 8787#define MAXLINE 1024#define LISTENQ 5//创建套接字并进行绑定static int socket_bind(const char* ip,int port);//IO多路复用selectstatic void do_select(int listenfd);//处理多个连接static void handle_connection(int *connfds,int num,fd_set *prset,fd_set *pallset);int main(int argc,char *argv[]){ int listenfd,connfd,sockfd; struct sockaddr_in cliaddr; socklen_t cliaddrlen; listenfd = socket_bind(IPADDRESS,PORT); listen(listenfd,LISTENQ); do_select(listenfd); return 0;}static int socket_bind(const char* ip,int port){ int listenfd; struct sockaddr_in servaddr; listenfd = socket(AF_INET,SOCK_STREAM,0); if (listenfd == -1) { perror("socket error:"); exit(1); } bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET,ip,&servaddr.sin_addr); servaddr.sin_port = htons(port); if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1) { perror("bind error: "); exit(1); } return listenfd;}static void do_select(int listenfd){ int connfd,sockfd; struct sockaddr_in cliaddr; socklen_t cliaddrlen; fd_set rset,allset; int maxfd,maxi; int i; int clientfds[FD_SETSIZE]; //保存客户连接描述符 int nready; //初始化客户连接描述符 for (i = 0;i < FD_SETSIZE;i++) clientfds[i] = -1; maxi = -1; FD_ZERO(&allset); //添加监听描述符 FD_SET(listenfd,&allset); maxfd = listenfd; //循环处理 for ( ; ; ) { rset = allset; //获取可用描述符的个数 nready = select(maxfd+1,&rset,NULL,NULL,NULL); if (nready == -1) { perror("select error:"); exit(1); } //测试监听描述符是否准备好,如果就绪的是listenfd,则接收新连接。 if (FD_ISSET(listenfd,&rset)) { cliaddrlen = sizeof(cliaddr); //接收新的连接 if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1) { if (errno == EINTR) continue; else { perror("accept error:"); exit(1); } } fprintf(stdout,"accept a new client: %d:%d\n", inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port); //将新的连接描述符添加到数组中 for (i = 0;i <FD_SETSIZE;i++) { if (clientfds[i] < 0) { clientfds[i] = connfd; break; } } if (i == FD_SETSIZE) { fprintf(stderr,"too many clients.\n"); exit(1); } //将新的描述符添加到读描述符集合中 FD_SET(connfd,&allset); //描述符个数 maxfd = (connfd > maxfd ? connfd : maxfd); //记录客户连接套接字的个数 maxi = (i > maxi ? i : maxi); if (--nready <= 0) continue; } //如果不是连接监听套接字,则处理已连接客户请求 handle_connection(clientfds,maxi,&rset,&allset); }}static void handle_connection(int *connfds,int num,fd_set *prset,fd_set *pallset){ int i,n; char buf[MAXLINE]; memset(buf,0,MAXLINE); for (i = 0;i <= num;i++) { if (connfds[i] < 0) continue; //测试客户描述符是否准备好,即是否有新的消息可读。 if (FD_ISSET(connfds[i],prset)) { //接收客户端发送的信息 n = read(connfds[i],buf,MAXLINE); if (n == 0) { close(connfds[i]); FD_CLR(connfds[i],pallset); connfds[i] = -1; continue; } printf("read msg is: "); write(STDOUT_FILENO,buf,n); //向客户端发送buf write(connfds[i],buf,n); } }}
客户端select_c.c
#include <netinet/in.h>#include <sys/socket.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/select.h>#include <time.h>#include <unistd.h>#include <sys/types.h>#define MAXLINE 1024#define IPADDRESS "127.0.0.1"#define SERV_PORT 8787#define max(a,b) (a > b) ? a : bstatic void handle_connection(int sockfd);int main(int argc,char *argv[]){ int sockfd; struct sockaddr_in servaddr; sockfd = socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr); connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); //处理连接描述符 handle_connection(sockfd); return 0;}static void handle_connection(int sockfd){ char sendline[MAXLINE],recvline[MAXLINE]; int maxfdp,stdineof; fd_set rset; int n; FD_ZERO(&rset); for (; ;) { //添加标准输入描述符 FD_SET(STDIN_FILENO,&rset); //添加连接描述符 FD_SET(sockfd,&rset); maxfdp = max(STDIN_FILENO,sockfd); //进行轮询 select(maxfdp+1,&rset,NULL,NULL,NULL); //测试连接套接字是否准备好 if (FD_ISSET(sockfd,&rset)) { n = read(sockfd,recvline,MAXLINE); if (n == 0) { fprintf(stderr,"client: server is closed.\n"); close(sockfd); FD_CLR(sockfd,&rset); } write(STDOUT_FILENO,recvline,n); } //测试标准输入是否准备好 if (FD_ISSET(STDIN_FILENO,&rset)) { n = read(STDIN_FILENO,sendline,MAXLINE); if (n == 0) { FD_CLR(STDIN_FILENO,&rset); continue; } write(sockfd,sendline,n); } }}
0 0
- IO多路复用之select
- IO多路复用之select
- IO多路复用之select
- IO多路复用之select
- IO多路复用之select
- IO多路复用之select总结
- IO多路复用之select篇
- IO多路复用之select总结
- IO多路复用之select总结
- IO多路复用之select总结
- IO多路复用之select总结
- IO多路复用之select总结
- IO 多路复用之select(理解)
- IO多路复用之select总结
- IO多路复用之select总结
- IO多路复用之select总结
- IO多路复用之select总结
- IO多路复用之select总结
- 网络摄像机架构
- Lisp学习1
- unity简单设计模式---CoroutineScheduler
- CodeForces 327D Block Tower(DFS)
- android利用闪光灯实现手机电筒
- IO多路复用之select
- ARN [main-SendThread(db99:2222)] zookeeper.ClientCnxn: Session 0x0 for server null, unexpected erro
- listview item中图片的回收
- 软件安全性测试测试项目列表
- 从零开始学戏cocos2d-x (一):cocos2d-x for mac基本开发环境配置
- json_encode 数据 || ajax 数据||jquery selected 数据
- PRML1.2 probability theory
- 对于eclipse快捷使用
- (算法设计技巧与分析)prim