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;}
阅读全文
0 0
- select()函数_笔记
- winsocket select函数笔记
- 【学习笔记】select函数
- jQuery 操作 select radio checkbox _笔记
- python笔记_函数
- Python笔记_函数
- Oracle笔记_内置函数
- Oracle笔记_单行函数
- Oracle笔记_分组函数
- mysql学习笔记_函数
- python_笔记6_函数
- Python学习笔记_函数
- 01_基本SQL SELECT语句 - Oracle学习笔记
- 套接字 学习笔记三 select函数
- [知了堂学习笔记]_MySQL函数_字符串函数_数学函数_日期函数
- C语言学习笔记:10_函数_高级
- C++笔记_函数的定义
- JavaScript_个人笔记6_再说函数
- ios-自动布局的基本原理
- 清晰的思考艺术
- final关键字、抽象类和接口
- Java知识复习(异常处理)
- Android基础总结八:Fragment的使用
- select()函数_笔记
- iOS横竖屏切换
- html5学习笔记3 HTML简介(二)
- 【ssm框架】枚举类Enum的应用
- Java基础之java介绍及jdk配置
- Vue 实例暴露了一些有用的实例属性与方法
- vb.net注册表基本操作
- [effectiv c++]条款39:明智而审慎地使用private继承
- 文档对象模型DOM