select()函数_笔记

来源:互联网 发布:网络管理培训 编辑:程序博客网 时间:2024/06/05 12:42

参考文章:http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html


IO多路复用:内核一旦发现进程指定的一个或者多个IO条件准备读取,就通知该进程。优势是系统不需要创建进程/线程,也不必维护,减少开销

适用场景:

1)当处理多个描述符是(一般是交互式输入和网络socket接口),必须使用IO复用

2)当同时处理多个socket接口

3)TCP服务器既要监听连接请求,又要处理已连接socket

4)既要处理TCP,又要处理UDP

5)服务器需要处理多个服务或者协议


#include <sys/select.h>#include <sys/time.h>int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)返回值:就绪描述符的数目,超时返回0,出错返回-1


void FD_ZERO(fd_set *fdset);           //清空集合void FD_SET(int fd, fd_set *fdset);   //将一个给定的文件描述符加入集合之中void FD_CLR(int fd, fd_set *fdset);   //将一个给定的文件描述符从集合中删除int FD_ISSET(int fd, fd_set *fdset);   // 检查集合中指定的文件描述符是否可以读写 



Server.c

#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 <sys/types.h>#include <arpa/inet.h>#include <unistd.h>#include <assert.h>#define IPADDR"127.0.0.1"#define PORT8787#define MAXLINE1024//接收buffer的最大长度#define LISTENQ5    //指定能同时处理的最大连接数#define SIZE10//限制客户端个数typedef struct server_context_st{int cli_cnt;// 客户端个数int clifds[SIZE];// 客户端,保存客户端FDSfd_set allfds;// 句柄集合int maxfd;// 句柄最大值}server_context_st;static server_context_st *s_srv_ctx= NULL;//client连接请求static int accept_client_proc(int srvfd){int clifd = -1;int i;struct sockaddr_in cliaddr;socklen_t cliaddrlen;cliaddrlen = sizeof(cliaddr);// 接受client连接请求clifd = accept(srvfd, (struct sockaddr*)&cliaddr, &cliaddrlen);if(clifd == -1){printf("accept failed ,errno = %d, %s\r\n", errno, strerror(errno));return -1;}printf("accept new client: %s  %d\r\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);// 将客户端描述符保存,在select函数中使用for(i = 0 ; i < SIZE; i++){if(s_srv_ctx->clifds[i] == -1){s_srv_ctx->clifds[i] = clifd;s_srv_ctx->cli_cnt++;break;}}if(i == SIZE){printf("client full up\r\n");return -2;}return 0;}//接收client消息static void recv_client_msg(fd_set *readfds){int i;char recv_buf[MAXLINE];int len;int clifd;for(i = 0 ; i < s_srv_ctx->cli_cnt; i++){clifd = s_srv_ctx->clifds[i];if(clifd < 0)continue;// 判断是哪个client 描述符 发送消息if(FD_ISSET(clifd, readfds)){//读len = read(clifd, recv_buf, MAXLINE);if(len <= 0){// n=0 可能client连接已中断,将对应client 描述符从集合中删除FD_CLR(clifd, &s_srv_ctx->allfds);s_srv_ctx->clifds[i] = -1;// 关闭TCP连接close(clifd);printf("close connection ID = %d\r\n", i);continue;}else{// 将接收到的消息重新返回clientprintf("server recv %dByte msg : %s\r\n", len, recv_buf);write(clifd, recv_buf, len);}}}}int main(int agrc , char **agrv){int i;int srvfd;int ret;/* Step1 初始化 s_srv_ctx 结构体,将所有client成员设置为无效-1*/s_srv_ctx = (server_context_st *)malloc(sizeof(server_context_st));if(s_srv_ctx == NULL){return -1;}memset(s_srv_ctx, 0, sizeof(server_context_st));for(i = 0 ; i < SIZE; i++){s_srv_ctx->clifds[i] = -1;}/* Step2 创建server socket  bind ip和port  监听客户端连接 */srvfd = socket(AF_INET, SOCK_STREAM, 0);if(srvfd == -1){printf("creat socket failed, errno = %d, reason: %s\r\n", errno, strerror(errno));goto s_exit;}// SO_REUSEADDR 设置端口释放后可以立即再次使用int reuse = 1;if(setsockopt(srvfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){printf("setsockopt failed \r\n");goto s_exit;}struct sockaddr_in servaddr;bzero(&servaddr, sizeof(struct sockaddr_in));servaddr.sin_family = AF_INET;inet_pton(AF_INET, IPADDR, &servaddr.sin_addr);servaddr.sin_port = htons(PORT);if(bind(srvfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){printf("bind error\r\n");goto s_exit;}listen(srvfd, LISTENQ);/* Step3 接收并处理客户端请求 */struct timeval tv;while(1){// 每次调用select之前都要重新设置文件描述符和时间,因为事件发生后文件描述符和时间被系统修改// 添加描述符srvfd,用于监听是否有客户端连接FD_ZERO(&s_srv_ctx->allfds);FD_SET(srvfd, &s_srv_ctx->allfds);s_srv_ctx->maxfd = srvfd;// 轮询client列表,判断客户端是否有效,并添加有效的客户端描述符clifds[i]for(i = 0 ; i < s_srv_ctx->cli_cnt; i++){if(s_srv_ctx->clifds[i] != -1){FD_SET(s_srv_ctx->clifds[i], &s_srv_ctx->allfds);}//设置最大描述符if(s_srv_ctx->clifds[i] > s_srv_ctx->maxfd){s_srv_ctx->maxfd = s_srv_ctx->clifds[i];}}tv.tv_sec = 20;tv.tv_usec = 0;ret = select(s_srv_ctx->maxfd+1, &s_srv_ctx->allfds, NULL, NULL, &tv);if(ret == -1){printf("select error = %d %s\r\n", errno, strerror(errno));break;}else if(ret == 0){printf("time out\r\n");continue;}if(FD_ISSET(srvfd, &s_srv_ctx->allfds)){//printf("client request\r\n");accept_client_proc(srvfd);      // 处理client请求连接}else{//printf("client msg proc\r\n");recv_client_msg(&s_srv_ctx->allfds);   // 处理client发送消息}}// 退出,释放资源s_exit:free(s_srv_ctx);s_srv_ctx = NULL;}


Client.c

#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 <sys/types.h>#include <unistd.h>#include <assert.h>#define MAXLINE1024#define IPADDRESS"127.0.0.1"#define SERV_PORT 8787static char recv_buf[MAXLINE];static char hello_server[] = {"hello server"};int main(int argc , char **argv){int sockfd;int ret;struct sockaddr_inservaddr;// Step1 创建client socketsockfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(struct sockaddr_in));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, IPADDRESS, &servaddr.sin_addr);// Step2 请求连接serverret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));if(ret < 0){printf("connect failed ,errno = %d, %s\r\n", errno, strerror(errno));return -1;}// 发送消息 hellowrite(sockfd, hello_server, sizeof(hello_server));int len;fd_set readfds;struct timeval tv;while(1){FD_ZERO(&readfds);FD_SET(sockfd, &readfds);//tv.tv_sec = 5;tv.tv_usec = 0;ret = select(sockfd+1, &readfds, NULL, NULL, &tv);if(ret == -1){printf("client select error = %d, %s\r\n", errno, strerror(errno));return -2;}else if(ret == 0){printf("client time out\r\n");continue;}if(FD_ISSET(sockfd, &readfds)){len = read(sockfd, recv_buf, MAXLINE);if(len <= 0){printf("server connection closed\r\n");goto c_exit;}else{// 将server发送的消息返回server ,其实是第一次从client发送的helloprintf("client recv %dByte msg : %s\r\n", len, recv_buf);sleep(5);write(sockfd, recv_buf, len);}}}c_exit:close(sockfd);FD_CLR(sockfd, &readfds);return 0;}



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 积分兑换手机被骗了怎么办 老板对你的上级不满怎么办 如果老板不给工资怎么办 手机号码被标记为其他公司怎么办? 被标记为骚扰电话怎么办 手机被标记骚扰电话怎么办 360摄像头不支持5g怎么办 摄像头不支持5g网络怎么办 家里的wifi卡了怎么办 办信用卡没有座机号码怎么办 拨打电话时显示号码有误怎么办 个体营业执照怎么办企业支付宝 没满16岁怎么办电话卡 我的电话卡丢了怎么办 公司注销地税没有补齐怎么办 公司没有固定电话怎么办信用卡 申请信用卡公司没有固定电话怎么办 办理信用卡公司没有固定电话怎么办 江门市固话欠费停机后怎么办 物业交钥匙地面有裂缝怎么办 深圳交通卡丢了怎么办 没装etc走etc怎么办 找不到过敏源身体痒怎么办 微信客服电话打不通怎么办 华为手机网络信号不好怎么办 酷我好友动态加载不了怎么办? 米兔电话手表停机了怎么办 糖猫电话手表停机了怎么办 360电话手表停机了怎么办 小天才电话手表停机怎么办 移动手机卡不知道号码怎么办 天猫退款手机号换了怎么办 科目二考试脚抖怎么办 面试新工作没打电话怎么办 怀孕5个月胎位低怎么办 做业务很害怕打电话怎么办 固定电话总是接到骚扰电话怎么办 电话卡通话被限制了怎么办? 手机名单拉黑了怎么办 被苹果6s被拉黑怎么办 重庆福利企业解聘残疾职工怎么办