博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多路I/O转接之select模型
阅读量:4700 次
发布时间:2019-06-09

本文共 5383 字,大约阅读时间需要 17 分钟。

I/O复用使得程序可以同一时候监听多个文件描写叙述符。这对提高程序的性能至关重要。通常,网络程序同一时候处理或者监听多个socket文件描写叙述符的时候可以考虑使用I/O复用模型。

值得强调的是。I/O复用尽管可以同一时候监听多个文件描写叙述符。但它本身是堵塞的。当有多个文件描写叙述符就绪的时候,假设不採取额外的措施,程序就仅仅能按顺序依次处理当中的每个文件描写叙述符,这使得server程序看起来像串行工作的。

假设要实现并发。仅仅可以使用多进程或者多线程的手段。

select 系统调用的用途是:在一段指定的时间内,监听用户感兴趣的文件描写叙述符上的可读,可写和异常等事件。

值得说明的是:

1.select 能监听的文件描写叙述符个数受限于FD_SETSIZE。一般默觉得1024,单纯改变进程打开的文件符个数并不能改变select监听的文件描写叙述符的个数

2.解决1024一下client时候使用select非常合适(比方局域网中)。可是假设连接client过多,select採用是轮询模型(程序中会有所体现)。这样会大大减少server的响应效率。

相关系统调用函数介绍 

#include 
//所需头文件int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);1.nfds 监控的文件描写叙述符集里最大文件描写叙述符加1,由于此參数会告诉内核检測前多少个文件描写叙述符的状态,之所以加1 由于文件描写叙述符是从0開始编号的2.readfds 监控有读数据到达文件描写叙述符集合,传入传出參数 3.writefds监控有写数据到达的文件描写叙述符集合,传入传出參数4.exceptfds 监控异常发生达文件描写叙述符集合,如带外数据到达异常,传入传出參数timeout:定时堵塞监控时间,3种情况1.NULL,永远等下去,堵塞在这里2.设置timeval,等待固定时间3.设置timeval里时间均为0。检查描写叙述字后马上返回。轮询struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */};void FD_CLR(int fd, fd_set *set);//将fd_set集中相应的文件描写叙述符清0int FD_ISSET(int fd, fd_set *set);//測试文件描写叙述符集合里fd是否置1void FD_SET(int fd, fd_set *set);//设置文件描写叙述符集合李fd为1void FD_ZERO(fd_set *set); //把文件描写叙述符集合里全部位清0

select 监听程序:

#include
#include
#include
#include
#include
#include
#include
#include
#include
int create_listen(int port){ int listen_st,on; struct sockaddr_in s_addr; listen_st =socket(AF_INET,SOCK_STREAM,0); if(listen_st==-1) { perror("socket error "); return -1; } if(setsockopt(listen_st,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))==-1) { perror("setsockopt error"); return -1; } s_addr.sin_port=htons(port); s_addr.sin_family=AF_INET; s_addr.sin_addr.s_addr=htonl(INADDR_ANY); if(bind(listen_st,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in))==-1) { perror("bind error"); return -1; } if (listen(listen_st, 5) == -1) // 设置文件描写叙述符具有监听的功能 { perror("listen error"); return -1; } return listen_st; }int run_server(int port){ int i,maxi,maxfd,listen_st,conn_st,sockaddr_len; int nready,client[FD_SETSIZE]; char buf[1024]; struct sockaddr_in c_addr; fd_set rset,allset; listen_st=create_listen(port); if(listen_st==-1) { return -1; } for(i=0;i
maxi) //记录最大下标 { maxi=i; } if(conn_st>maxfd)//记录最大文件描写叙述符用于select用 { maxfd=conn_st; } } if(--nready==0) continue; } for(i=0;i<=maxi;i++) { if((conn_st=client[i])<0) { continue; } if(FD_ISSET(conn_st,&rset)) { memset(buf,0,sizeof(buf)); if(read(conn_st,buf,sizeof(buf))==0) { printf("close client \n"); close(conn_st); FD_CLR(conn_st,&allset); client[i]=-1; } else { printf("recv from client:%s \n",buf); write(conn_st,buf,strlen(buf)); } if (--nready == 0) break; //就绪个数减一 } } sleep(1); } close(listen_st); return 0;}int main(int argc,char *argv[]){ if(argc<2) { printf("usage:%s port \n",argv[0]); return 0; } int port=atoi(argv[1]); if(port==0) { printf("port error \n"); return 0; } printf("start server \n"); run_server(port); return 0;}
client程序

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFSIZE 1024#define ERRORCODE -1static void *thread_send(void *arg){ char buf[BUFFSIZE]; int sd = *(int *) arg; while (1) { memset(buf, 0, sizeof(buf)); read(STDIN_FILENO, buf, sizeof(buf)); if (send(sd, buf, strlen(buf), 0) == -1) { printf("send error:%s \n", strerror(errno)); break; } } return NULL;}static void* thread_recv(void *arg){ char buf[BUFFSIZE]; int sd = *(int *) arg; while (1) { memset(buf, 0, sizeof(buf)); int rv = recv(sd, buf, sizeof(buf), 0); if (rv <= 0) { if(rv == 0) //server socket关闭情况 { printf("server have already full !\n"); exit(0);//退出整个客服端 } printf("recv error:%s \n", strerror(errno)); break; } printf("%s", buf);//输出接收到的内容 } return NULL;}int run_client(char *ip_str, int port){ int client_sd; int con_rv; pthread_t thrd1, thrd2; struct sockaddr_in client_sockaddr; //定义IP地址结构 client_sd = socket(AF_INET, SOCK_STREAM, 0); if (client_sd == -1) { printf("socket create error:%s \n", strerror(errno)); return ERRORCODE; } memset(&client_sockaddr, 0, sizeof(client_sockaddr)); client_sockaddr.sin_port = htons(port); //指定一个端口号并将hosts字节型传化成Inet型字节型(大端或或者小端问题) client_sockaddr.sin_family = AF_INET; //设置结构类型为TCP/IP client_sockaddr.sin_addr.s_addr = inet_addr(ip_str); //将字符串的ip地址转换成int型,客服端要连接的ip地址 con_rv = connect(client_sd, (struct sockaddr*) &client_sockaddr, sizeof(client_sockaddr)); //struct sockaddr 是非常早曾经定义的 struct sockaddr_in 是后定义的,眼下用的比較多 //调用connect连接到指定的ip地址和端口号,建立连接后通过socket描写叙述符通信 if (con_rv == -1) { printf("connect error:%s \n", strerror(errno)); return ERRORCODE; } if (pthread_create(&thrd1, NULL, thread_send, &client_sd) != 0) { printf("thread error:%s \n", strerror(errno)); return ERRORCODE; } if (pthread_create(&thrd2, NULL, thread_recv, &client_sd) != 0) { printf("thread error:%s \n", strerror(errno)); return ERRORCODE; } pthread_join(thrd2, NULL); pthread_join(thrd1, NULL); close(client_sd); return 0;}int main(int argc, char *argv[]){ if (argc < 3) { printf("Usage:ip port,example:127.0.0.1 8080 \n"); return ERRORCODE; } int port = atoi(argv[2]); char *ip_str = argv[1]; run_client(ip_str,port); return 0;}

转载于:https://www.cnblogs.com/gccbuaa/p/6949565.html

你可能感兴趣的文章
git常用操作
查看>>
京东SSO单点登陆实现分析
查看>>
u-boot启动第一阶段
查看>>
MySQL批量SQL插入性能优化
查看>>
定义列属性:null,default,PK,auto_increment
查看>>
用户画像展示
查看>>
C#中StreamReader读取中文出现乱码
查看>>
使用BufferedReader的时候出现的问题
查看>>
批处理文件中的路径问题
查看>>
hibernate出现No row with the given identifier exists问题
查看>>
为什么wait()和notify()属于Object类
查看>>
配置NRPE的通讯
查看>>
匹配两个空格之间的字符。。。
查看>>
CSS 文字溢出 变成省略号 ...
查看>>
Spring事务
查看>>
java编程基础(三)流程控制语句
查看>>
让数据库跑的更快的7个MySQL优化建议
查看>>
jquery 取id模糊查询
查看>>
解决在vue中,自用mask模态框出来后,下层的元素依旧可以滑动的问题
查看>>
修改node节点名称
查看>>