select实现IO多路转接服务器 基于该视频完成
15-select实现多路IO转接设计思路_哔哩哔哩_bilibili
通过响应式–多路IO转接实现
1.思路&功能 **功能:**客户端输入小写字符串,服务器转成大写返回给客户端
思路:
allset是用来更新rset的,因为rest是传入传出参数,allset是记录传出的rest的,因为rest传出以后,监听列表就变了,可能不会原来的了
2.代码实现 warp.h 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #ifndef __WRAP_H_ #define __WRAP_H_ #include <sys/epoll.h> #include <sys/select.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <dirent.h> #include <sys/stat.h> #include <wait.h> #include <sys/mman.h> #include <signal.h> #include <pthread.h> #include <sys/socket.h> #include <arpa/inet.h> #include <ctype.h> #include <strings.h> #include <netinet/ip.h> #define SRV_PORT 1234 void perr_exit (const char *s) ;int Accept (int fd,struct sockaddr *sa,socklen_t * salenptr) ;int Bind (int fd, const struct sockaddr *sa, socklen_t salen) ;int Connect (int fd, const struct sockaddr *sa, socklen_t addrlen) ;int Listen (int fd, int backlog) ;int Socket (int family, int type, int protocol) ;size_t Read (int fd, void *ptr, size_t nbytes) ;ssize_t Write (int fd,const void *ptr,size_t nbytes) ;int Close (int fd) ;ssize_t Readn (int fd, void *vptr, size_t n) ;ssize_t Writen (int fd, const void *vptr, size_t n) ;ssize_t my_read (int fd, char *ptr) ;ssize_t Readline (int fd, void *vptr, size_t maxlen) ;#endif
warp.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 #include "warp.h" void perr_exit (const char *s) { perror(s); exit (1 ); } int Accept (int fd,struct sockaddr *sa,socklen_t * salenptr) { int n; again: if ((n=accept(fd,sa,salenptr))<0 ) { if ((errno==ECONNABORTED)||(errno==EINTR)) goto again; else perr_exit("accept error" ); } return n; } int Bind (int fd, const struct sockaddr *sa, socklen_t salen) { int n; if ((n=bind(fd,sa,salen))<0 ) perr_exit("bind error" ); return n; } int Connect (int fd, const struct sockaddr *sa, socklen_t addrlen) { int n; n=connect(fd,sa,addrlen); if (n<0 ) { perr_exit("connect error" ); } return n; } int Listen (int fd, int backlog) { int n; if ((n=listen(fd,backlog))<0 ) perr_exit("listen error" ); return n; } int Socket (int family, int type, int protocol) { int n; if ((n=socket(family,type,protocol))<0 ) perr_exit("socket error" ); return n; } size_t Read (int fd, void *ptr, size_t nbytes) { ssize_t n; again: if ((n=read(fd,ptr,nbytes))==-1 ) { if (errno==EINTR) goto again; else return -1 ; } return n; } ssize_t Write (int fd,const void *ptr,size_t nbytes) { ssize_t n; again: if ((n=write(fd,ptr,nbytes))==-1 ) { if (errno==EINTR) goto again; else return -1 ; } return 0 ; } int Close (int fd) { int n; if ((n=close(fd))==-1 ) perr_exit("close error" ); return n; } ssize_t Readn (int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr=vptr; nleft=n; while (nleft>0 ) { if ((nread=read(fd,ptr,nleft))<0 ) { if (errno==EINTR) nread=0 ; else return -1 ; } else if (nread==0 ) break ; } } ssize_t Writen (int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; char *ptr; ptr=(char *)vptr; nleft=n; while (nleft>0 ) { if ((nwritten=write(fd,ptr,nleft))<=0 ) { if (nwritten<0 &&errno==EINTR) nwritten=0 ; else return -1 ; } nleft-=nwritten; ptr+=nwritten; } return n; } ssize_t my_read (int fd, char *ptr) { static int read_cnt; static char *read_ptr; static char read_buf[100 ]; if (read_cnt<=0 ) { again: if ((read_cnt=read(fd,read_buf,sizeof (read_buf)))<0 ) { if (errno==EINTR) goto again; return -1 ; }else if (read_cnt==0 ) return 0 ; read_ptr=read_buf; } read_cnt--; *ptr=*read_ptr++; return 1 ; } ssize_t Readline (int fd, void *vptr, size_t maxlen) { ssize_t n,rc; char c,*ptr; ptr=vptr; for (n=1 ;n<maxlen;n++) { if ((rc=my_read(fd,&c))==1 ) { *ptr++=c; if (c=='\n' ) break ; }else if (rc==0 ) { *ptr=0 ; return n-1 ; } else return -1 ; } *ptr=0 ; return n; }
multi_select_sever.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include "warp.h" #define SERV_PORT 1234 int main (int argc,char * argv[]) { int listenfd=0 ,connfd=0 ; int ret,maxfd=0 ,i,n,j; char buf[4096 ]; listenfd=Socket(AF_INET,SOCK_STREAM,0 ); struct sockaddr_in serv_addr ,clie_addr ; socklen_t clie_addr_len; serv_addr.sin_family=AF_INET; serv_addr.sin_port=htons(SERV_PORT); serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); Bind(listenfd,(struct sockaddr *)&serv_addr,sizeof (serv_addr)); Listen(listenfd,128 ); fd_set rset,allset; FD_ZERO(&allset); FD_SET(listenfd,&allset); maxfd=listenfd; while (1 ){ rset=allset; ret=select(maxfd+1 ,&rset,NULL ,NULL ,NULL ); if (ret<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); FD_SET(connfd,&allset); if (maxfd<connfd) maxfd=connfd; if (ret==1 ) continue ; } for (i=listenfd+1 ;i<=maxfd;i++) { if (FD_ISSET(i,&rset)) { n=read(i,buf,sizeof (buf)); if (n==0 ) { close(i); FD_CLR(i,&allset); } else if (n==-1 ) perr_exit("read error" ); } for (j=0 ;j<n;j++) buf[j]=toupper (buf[j]); Write(i,buf,n); Write(STDOUT_FILENO,buf,n); } } Close(listenfd); return 0 ; }
1 gcc warp.c multi_select_sever.c -o multi_select_sever
运行图 两个客户端访问服务器端
3.代码解释(细节) 1.allset作用
更新rest,因为rest是传入传出参数,传出的是需要监听的集合
而需要监听的集合可能会改变,就是上一次监听的这一次来了可能就不需要监听了
2.监听套接字收到连接请求是属于读行为的,也就是说监听套接字要放到读集合中,有请求要连接的时候,传出的集合中会有监听套接字
3.如果有监听套接字,那说明有连接请求,我们就要处理连接
同时还要看传出的需要监听的描述符集合的大小,如果就是1,说明就只有一个连接请求,我们也就不需要执行下面的代码直接continue就行
4.如果传出的传出的需要监听的描述符集合的大小>1
那我们就循环遍历,从监听套接字开始,到最大
我们监听的是读集合,那如果文件描述在读集合中,那就读取数据然后小写转大写
如果对端关闭的话(read返回0),那就关闭连接同时清除掉该文件描述符
5.我们会发现我们没有用fork也没有用pthread就完成了多并发服务器
4.改进版 如果只有两个描述符,一个3,一个1023,那么效率会很低,我们现在用一个数组来把我们要监听的文件描述符存起来,然后遍历这个数组就行
其实就是比原来来说,避免了3和1023这种情况导致的效率降低的问题,总体效率并没有提升很多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #include "warp.h" #define SERV_PORT 1234 int main (int argc,char * argv[]) { int listenfd=0 ,connfd=0 ,maxfd,sockfd; int ret,i,n,j,maxi; char buf[4096 ],str[INET_ADDRSTRLEN]; int nready,client[FD_SETSIZE]; listenfd=Socket(AF_INET,SOCK_STREAM,0 ); int opt=1 ; setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof (opt)); struct sockaddr_in serv_addr ,clie_addr ; socklen_t clie_addr_len; serv_addr.sin_family=AF_INET; serv_addr.sin_port=htons(SERV_PORT); serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); Bind(listenfd,(struct sockaddr *)&serv_addr,sizeof (serv_addr)); Listen(listenfd,128 ); fd_set rset,allset; FD_ZERO(&allset); FD_SET(listenfd,&allset); maxfd=listenfd; maxi=-1 ; for (i=0 ;i<FD_SETSIZE;i++) client[i]=-1 ; while (1 ){ rset=allset; nready=select(maxfd+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); printf ("received from %s at PORT %d\n" , inet_ntop(AF_INET,&clie_addr,str,sizeof (str)), ntohs(clie_addr.sin_port)); for (i=0 ;i<FD_SETSIZE;i++) if (client[i]<0 ) { client[i]=connfd; break ; } if (i==FD_SETSIZE) { fputs ("too many clients\n" ,stderr ); exit (1 ); } FD_SET(connfd,&allset); if (maxfd<connfd) maxfd=connfd; if (i>maxi) maxi=i; if (--nready==0 ) continue ; } for (i=0 ;i<=maxi;i++) { if ((sockfd=client[i])<0 ) continue ; if (FD_ISSET(sockfd,&rset)) { n=read(sockfd,buf,sizeof (buf)); if (n==0 ) { close(sockfd); FD_CLR(sockfd,&allset); client[i]=-1 ; } else if (n>0 ) { for (j=0 ;j<n;j++) buf[j]=toupper (buf[j]); Write(sockfd,buf,n); Write(STDOUT_FILENO,buf,n); } if (--nready==0 ) break ; } } } Close(listenfd); return 0 ; }