使用epoll編寫TCP服務器端

epoll:結合了select與poll的優勢,以及優化了它們的不足,來實現同時控制多個句柄,以此來實現多路複用。它也是使用文件系統的相關信息來實現的
數組

它所使用的三個系統調用函數
瀏覽器

1.epoll_create函數
服務器

wKiom1dL_Kyy1JyqAAAmFwxZyUc455.png-wh_50

建立一個句柄,size大小可不關心,該句柄會佔用一個文件描述符位置
網絡

2.epoll_ctl函數,它須要使用一個結構體告訴內核需監聽什麼事件socket

wKiom1dL_SSAqsrJAAA7e5jYBHQ514.png-wh_50

它爲一個事件註冊函數,先將要監聽的何種事件進行註冊,不一樣於select函數,它是在監聽的時候就要告訴是何種事件ide

op指要對某個描述符進行何種操做(添加,刪除,更改)
函數


wKioL1dL_iDiFA9SAABJihYQRjQ947.png-wh_50

wKiom1dL_0fg0IkuAAAqrjcH65M882.png-wh_50

epoll_event該結構體第一個參數爲要監聽的事件類型,第二個參數爲一個聯合體類型,可爲一個fd,也可爲一個指針優化

3.使用epoll_wait函數spa

wKioL1dL_-mixataAABW1JDBy4o251.png-wh_50

使用該函數它只需檢查哪一個就緒隊列(鏈表)是否爲空,用它能夠得到已經就緒的描述符
指針

maxevents表示一次最多容許返回多少個就緒事件個數

events是已經分配好的結構體數組(須要用戶本身分配),不能爲空

避免讓客戶端等待2MSL時間

    當客戶端要進行四次揮手時,發送FIN後,會進入一個TIME-WAIT狀態等待2MSL,此時它與服務器端的鏈接還未真正斷開,所以在該時間內該用戶的端口號等信息不能被其它用戶所使用。所以爲了不等待2MSL,要使用setsockopt函數來實現端口複用功能

wKioL1dMAxiRVydaAAAshGijmS0014.png-wh_50 


epoll的兩種工做方式:

LT:它支持阻塞套接字和非阻塞套接字,它讀取緩衝區中的數據時能夠讀一部分,由於當一個事件對應的套接字的緩衝區中還有內容時,內核會每次都告知你某個描述符已經就緒,能夠進行IO操做

ET(相對高效):數據只有從網絡上到達時纔會通知你一次,若是沒有任何的狀態改變,若一次沒有將某事件對應的套接字緩衝區內容處理完,它會一直在讀或寫時組等等待,這樣會使後續的多個描述符一直等待。所以必須將套接字設置爲非阻塞的,這樣當讀或寫返回一個EAGAIN時才須要等待,即當讀到的數據小於請求數據長度時說明緩衝區中的數據已經讀完。

服務器端:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <sys/types.h>
  5 #include <sys/socket.h>
  6 #include <errno.h>
  7 #include <arpa/inet.h>
  8 #include <netinet/in.h>
  9 #include <assert.h>
 10 #include <unistd.h>
 11 #include <sys/epoll.h>
 12 #include<fcntl.h>
 13 #define _MAX_ 64
 14 #define _MAX_SIZE_ 1024
 15 #define BACK_LOG 5
 16 typedef struct epoll_buf
 17 {
 18     int fd;
 19     char buf[_MAX_SIZE_];
 20 }epoll_t,*epoll_data_p;
 21 static void Usage(const char* proc)
 22 {
 23     assert(proc);
 24     printf("Usage:%s [ip] [port]\n",proc);
 25 }
 26 
 27 static void SetNonBlock(int fd)
 28 {   //set fd to nonblock
 29     int fds=0;
 30     if(fds=fcntl(fd,F_GETFL)<0)
 31     {
 32         perror("fcntl");
 33         exit(1);
 34     }
 35     if(fcntl(fd,F_SETFL,fds|O_NONBLOCK)<0)
 36     {
 37         perror("fcntl");
 38         exit(2);
 39     }
 40  
 41 }
 42 static int startup(char *ip,int port)
 43 {
 44     assert(ip);
 45     int listen_sock=socket(AF_INET,SOCK_STREAM,0);
 46     if(listen_sock < 0)
 47     {
 48         perror("socket");
 49         exit(1);
 50     }
 51     struct sockaddr_in local;
 52     local.sin_family=AF_INET;
 53     local.sin_port=htons(port);
 54     local.sin_addr.s_addr=inet_addr(ip);
 55     socklen_t len=sizeof(local);
 56     int opt=1; //讓客戶端避免2MSL時間,實現端口複用
 57     setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
 58     if(bind(listen_sock,(struct sockaddr*)&local,len)<0)
 59     {
 60         perror("bind");
 61         exit(2);
 62     }
 63     if(listen(listen_sock,BACK_LOG)<0)
 64     {
 65         perror("listen");
 66         exit(3);
 67     }
 68     return listen_sock;
 69 }
 70 int read_data(int fd,char *buf,int size)
 71 {
 72     assert(buf);
 73     int ret=0;
 74     int index=0;
 75     memset(buf,'\0',sizeof(buf));
 76     while((ret=read(fd,buf+index,size-index))<size)
 77     {
 78         if(errno==EAGAIN)  //客戶端已無數據發送
 79         {
 80             break;
 81         }
 82         index+=ret;
 83     }
 84     return index;
 85 
 86 }
 87 int write_data(int fd,char* buf,int size)
 88 {
 89     assert(buf);
 90     int ret=0;
 91     int index=0;
 92     while((ret=write(fd,buf+index,size-index))<size)
 93     {
 94         if(errno==EAGAIN)
 95             break;
 96         index+=ret;
 97     }
 98     return index;
 99 }
100 static int epoll_server(int listen_sock)
101 {
102     int epoll_fd=epoll_create(256);
103     if(epoll_fd < 0)
104     {
105         perror("epoll_create");
106         exit(1);
107     }
108     int ready_num=-1;
109     struct epoll_event ev;
110     ev.events=EPOLLIN|EPOLLET;
111     ev.data.fd=listen_sock;
112     SetNonBlock(listen_sock);//將套接字設置爲非阻塞,因使用ET工做模式
113     struct epoll_event ret_ev[_MAX_];
114     int num=_MAX_;
115     int timeout=5000;
116     struct sockaddr_in client;
117     socklen_t size=sizeof(client);
118     int i=0;
119     if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listen_sock,&ev)<0)
120     {
121         perror("epoll_ctl");
122 
123         exit(1);
124     }
125     int done=0;
126     while(!done)
127     {
128         switch(ready_num=epoll_wait(epoll_fd,ret_ev,num,timeout))
129         {//只需檢查就緒隊列(就緒鏈表)是否爲空
130             case 0:
131                 printf("timeout...\n");
132                 break;
133             case -1:
134                 perror("epoll_wait");
135                 break;
136             default:
137                 {
138                     for(i=0;i<ready_num;++i)
139                     {
140                         if(ret_ev[i].data.fd==listen_sock &&\
141                         (ret_ev[i].events &EPOLLIN))
142                         {//判斷是否處於監聽狀態且所關心的事件類型
143                             int fd=ret_ev[i].data.fd;
144                             int new_sock=accept(fd,(struct sockaddr*)&client    ,&size);
145                             if(new_sock < 0)
146                             {
147                                 perror("accept");
148                                 exit(1);
149                             }
150                             printf("get a new connect...\n");
151                             SetNonBlock(new_sock);
152                             ev.data.fd=new_sock;
153                             ev.events=EPOLLIN|EPOLLET;
154                             if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,new_sock,&ev    )<0)
155                             {//將new_sock添加到epoll中
156                                 perror("epoll_ctl");
157                                 exit(2);
158                             }
159                         }else if(ret_ev[i].events&EPOLLIN)
160                         {
161                             int fd=ret_ev[i].data.fd;
162    
163                             epoll_data_p msg ;
164                             msg=(epoll_data_p)malloc(sizeof(epoll_t));
165                             if(!msg)
166                             {
167                                 perror("malloc");
168                                 exit(1);
169                             }
170                             msg->fd=fd;
171                         //  ssize_t _s=read(msg->fd,msg->buf,\
172                             sizeof((msg->buf))-1);
173                             int _s=read_data(msg->fd,msg->buf,\
174                             sizeof(msg->buf)-1);
175                             if(_s > 0)
176                             {
177                                 msg->buf[_s]='\0';
178                                 printf("client:%s",msg->buf);
179                                 ev.data.ptr=msg;
180                                 ev.events=EPOLLOUT|EPOLLET;
181                                 if(epoll_ctl(epoll_fd,EPOLL_CTL_MOD,msg->fd,    &ev)<0)
182                                 {
183                                     perror("epoll_ctl");
184                                     return -1;
185                                 }
186                             }else if(_s==0)
187                             {
188                                 printf("client is closed...\n");
189                                 free(msg);
190                                 close(msg->fd);
191                                 epoll_ctl(epoll_fd,EPOLL_CTL_DEL,fd,NULL);
192 
193                             }
194                         }else if(ret_ev[i].events& EPOLLOUT)
195                          { //write ready
196                              epoll_data_p msg=(epoll_data_p)ret_ev[i].data.p    tr;
197                             // write(msg->fd,msg->buf,strlen(msg->buf));
198                              write_data(msg->fd,msg->buf,sizeof(msg->buf));
199                             // close(msg->fd);
200                             // char* mem="HTTP/1.0 200 OK\r\n\r\n hello worl    d\r\n";
201 
202                              //write(msg->fd,mem,strlen(mem));
203                              ev.data.fd=msg->fd;
204                              ev.events=EPOLLIN|EPOLLET;
205                              epoll_ctl(epoll_fd,EPOLL_CTL_MOD,msg->fd,&ev);
206                             //close(msg->fd);                            
207                             // epoll_ctl(epoll_fd,EPOLL_CTL_DEL,msg->fd,NULL    );                            
208                              //free(msg);
209                          }
210    
211                     }
212    
213                 }
214                 break;
215         }
216     }
217     return epoll_fd;
218 
219 }
220 int main(int argc,char* argv[])
221 {
222     if(argc!=3)
223     {
224         Usage(argv[0]);
225         exit(1);
226     }
227     char *_ip=argv[1];
228     int _port=atoi(argv[2]);
229     int listen_sock=startup(_ip,_port);
230     epoll_server(listen_sock);
231     return 0;
232 }

客戶端代碼

 1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <assert.h>
  5 #include <sys/types.h>
  6 #include <errno.h>
  7 #include <arpa/inet.h>
  8 #include <netinet/in.h>
  9 #include <sys/socket.h>
 10 
 11 static void Usage(const char* proc)
 12 {
 13     assert(proc);
 14     printf("Usage:%s [ip] [port]\n",proc);
 15 }
 16 
 17 int main(int argc,char* argv[])
 18 {
 19     if(argc!=3)
 20     {
 21         Usage(argv[0]);
 22         exit(1);
 23     } 
 24     int sock=socket(AF_INET,SOCK_STREAM,0);
 25     if(sock < 0)
 26     {
 27         perror("socket");
 28         exit(2);
 29     }
 30     char *_ip=argv[1];
 31     int _port=atoi(argv[2]);
 32     struct sockaddr_in remote;
 33     remote.sin_family=AF_INET;
 34     remote.sin_port=htons(_port);
 35     remote.sin_addr.s_addr=inet_addr(_ip);
 36     socklen_t len=sizeof(remote);
 37     if(connect(sock,(struct sockaddr*)&remote,len)<0)
 38     {
 39         perror("connect");
 40         return -1;
 41     }
 42 
 43     char buf[1024];
 44     while(1)
 45     {
 46         memset(buf,'\0',sizeof(buf));
 47         printf("please input:");
 48         fflush(stdout);
 49         ssize_t _size=read(0,buf,sizeof(buf)-1);
 50         if(_size >0)
 51         {
 52             buf[_size]='\0';
 53             write(sock,buf,strlen(buf));
 54         }
 55 
 56         _size=read(sock,buf,sizeof(buf)-1);
 57         if(_size>0)
 58         {
 59             printf("server---client:%s\n",buf);
 60         }
 61     }
 62     close(sock);
 63     return 0;
 64 }


運行結果:

wKioL1dRdTjQWLAMAABN-sAVVEI292.png-wh_50


使用瀏覽器爲客戶端:


wKioL1dRdWaQdtSAAACWG0Yv2BE656.png-wh_50

相關文章
相關標籤/搜索