頭文件:#include <sys/epoll.h>
一.eopll相關的函數:
1.int epoll_create(int size);
返回一個epoll句柄,參數size是可監聽的最大個數
2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
參數一epfd是epoll_create()出來到句柄。
參數二op可以取三個宏中到其一:
EPOLL_CTL_ADD:注冊新的fd到epfd中;
EPOLL_CTL_MOD:修改已經注冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd;
參數三fd是要監聽的fd。
參數四event是設置監聽到模式(ET模式,LT模式,write事件,read事件,accept事件)
相關結構體如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events可以是以下幾個宏的集合:
EPOLLIN :表示對應的文件描述符可以讀(包括對端SOCKET正常關閉);
EPOLLOUT:表示對應的文件描述符可以寫;
EPOLLPRI:表示對應的文件描述符有緊急的數據可讀(這裡應該表示有帶外數據到來);
EPOLLERR:表示對應的文件描述符發生錯誤;
EPOLLHUP:表示對應的文件描述符被掛斷;
EPOLLET: 將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對於水平觸發(Level Triggered)來說的。
EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列裡。
epoll_event.data.fd是要監聽到fd,一般設置epoll_event.data.fd和epoll_event.events就足夠了。
3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
返回值是觸發事件到個數,如返回0表示已超時。
參數一epfd是epoll_create()出來到句柄。
參數二events是一個epoll_event類型的數組。
參數三maxevents的值不能大於創建epoll_create()時的size。
參數四timeout是超時時間(毫秒,0會立即返回,-1將不確定,永久阻塞)。
4.close(int fd)
關閉句柄。
二.代碼
1.服務器
1 #include <sys/socket.h> 2 #include <sys/epoll.h> 3 #include <netinet/in.h> 4 #include <arpa/inet.h> 5 #include <fcntl.h> 6 #include <unistd.h> 7 #include <stdio.h> 8 #include <errno.h> 9 #include <string.h> 10 11 12 13 #define bool int 14 #define false 0 15 #define true 1 16 17 #define MAXLINE 5 18 #define OPEN_MAX 100 19 #define LISTENQ 20 20 #define SERV_PORT 5000 21 #define INFTIM 1000 22 23 void setnonblocking(int sock) 24 { 25 int opts; 26 opts=fcntl(sock,F_GETFL); 27 if(opts<0) 28 { 29 perror("fcntl(sock,GETFL)"); 30 return; 31 } 32 opts = opts|O_NONBLOCK; 33 if(fcntl(sock,F_SETFL,opts)<0) 34 { 35 perror("fcntl(sock,SETFL,opts)"); 36 return; 37 } 38 } 39 40 void CloseAndDisable(int sockid, struct epoll_event ee) 41 { 42 close(sockid); 43 ee.data.fd = -1; 44 } 45 46 int main() 47 { 48 int i, maxi, listenfd, connfd, sockfd,epfd,nfds, portnumber; 49 char line[MAXLINE]; 50 socklen_t clilen; 51 52 struct sockaddr_in clientaddr; 53 struct sockaddr_in serveraddr; 54 55 56 portnumber = 5000; 57 58 //聲明epoll_event結構體的變量,ev用於注冊事件,數組用於回傳要處理的事件 59 60 struct epoll_event ev,events[20]; 61 //生成用於處理accept的epoll專用的文件描述符 62 63 epfd=epoll_create(256); 64 65 listenfd = socket(AF_INET, SOCK_STREAM, 0); 66 67 memset(&serveraddr, 0, sizeof(serveraddr)); 68 serveraddr.sin_family = AF_INET; 69 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 70 serveraddr.sin_port=htons(portnumber); 71 72 // bind and listen 73 //no-blocking 74 setnonblocking(listenfd); 75 bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr)); 76 if(0 != listen(listenfd, LISTENQ)){ 77 perror("listen !=0"); 78 } 79 80 81 //設置與要處理的事件相關的文件描述符 82 ev.data.fd=listenfd; 83 //設置要處理的事件類型 84 ev.events=EPOLLIN|EPOLLET; 85 //ev.events=EPOLLIN; 86 87 //注冊epoll事件 88 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); 89 90 maxi = 0; 91 92 int bOut = 0; 93 for ( ; ; ) 94 { 95 if (bOut == 1) 96 break; 97 //等待epoll事件的發生 98 99 nfds=epoll_wait(epfd,events,20,-1); 100 //處理所發生的所有事件 101 102 printf("\nepoll_wait returns\n"); 103 104 for(i=0;i<nfds;++i) 105 { 106 if(events[i].data.fd==listenfd)//如果新監測到一個SOCKET用戶連接到了綁定的SOCKET端口,建立新的連接。 107 { 108 connfd = accept(listenfd,(struct sockaddr *)&clientaddr, &clilen); 109 if(connfd<0){ 110 perror("connfd<0"); 111 return (1); 112 } 113 114 115 char *str = inet_ntoa(clientaddr.sin_addr); 116 117 printf("accapt a connection from %s\n",str); 118 //設置用於讀操作的文件描述符 119 120 setnonblocking(connfd); 121 ev.data.fd=connfd; 122 //設置用於注測的讀操作事件 123 124 ev.events=EPOLLIN | EPOLLET; 125 //ev.events=EPOLLIN; 126 127 //注冊ev 128 epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); 129 } 130 else if(events[i].events & EPOLLIN)//如果是已經連接的用戶,並且收到數據,那麼進行讀入。 131 { 132 133 printf("EPOLLIN\n"); 134 if ( (sockfd = events[i].data.fd) < 0) 135 continue; 136 137 char * head = line; 138 int recvNum = 0; 139 int count = 0; 140 bool bReadOk = false; 141 while(1) 142 { 143 // 確保sockfd是nonblocking的 144 recvNum = recv(sockfd, head + count, MAXLINE, 0); 145 if(recvNum < 0) 146 { 147 if(errno == EAGAIN) 148 { 149 // 由於是非阻塞的模式,所以當errno為EAGAIN時,表示當前緩沖區已無數據可讀 150 // 在這裡就當作是該次事件已處理處. 151 bReadOk = true; 152 break; 153 } 154 else if (errno == ECONNRESET) 155 { 156 // 對方發送了RST 157 CloseAndDisable(sockfd, events[i]); 158 159 printf("counterpart send out RST\n"); 160 break; 161 } 162 else if (errno == EINTR) 163 { 164 // 被信號中斷 165 continue; 166 } 167 else 168 { 169 //其他不可彌補的錯誤 170 CloseAndDisable(sockfd, events[i]); 171 // cout << "unrecovable error\n"; 172 printf("unrecovable error\n"); 173 break; 174 } 175 } 176 else if( recvNum == 0) 177 { 178 // 這裡表示對端的socket已正常關閉.發送過FIN了。 179 CloseAndDisable(sockfd, events[i]); 180 181 printf("counterpart has shut off\n"); 182 break; 183 } 184 185 // recvNum > 0 186 count += recvNum; 187 if ( recvNum == MAXLINE) 188 { 189 continue; // 需要再次讀取 190 } 191 else // 0 < recvNum < MAXLINE 192 { 193 // 安全讀完 194 bReadOk = true; 195 break; // 退出while(1),表示已經全部讀完數據 196 } 197 } 198 199 if (bReadOk == true) 200 { 201 // 安全讀完了數據 202 line[count] = '\0'; 203 204 // cout << "we have read from the client : " << line; 205 printf("we have read from the client : %s",line); 206 //設置用於寫操作的文件描述符 207 208 ev.data.fd=sockfd; 209 //設置用於注測的寫操作事件 210 211 ev.events = EPOLLOUT | EPOLLET; 212 //修改sockfd上要處理的事件為EPOLLOUT 213 214 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); 215 } 216 } 217 else if(events[i].events & EPOLLOUT) // 如果有數據發送 218 { 219 const char str[100] = "hello from epoll : this is a long string which may be cut by the net\n"; 220 int strlen = sizeof(str); 221 printf("Write %s\n",str); 222 sockfd = events[i].data.fd; 223 224 bool bWritten = false; 225 int writenLen = 0; 226 int count = 0; 227 char * head = line; 228 while(1) 229 { 230 // 確保sockfd是非阻塞的 231 writenLen = send(sockfd, str + count, strlen - count/*MAXLINE*/, 0); 232 if (writenLen == -1) 233 { 234 if (errno == EAGAIN) 235 { 236 // 對於nonblocking 的socket而言,這裡說明了已經全部發送成功了 237 bWritten = true; 238 break; 239 } 240 else if(errno == ECONNRESET) 241 { 242 // 對端重置,對方發送了RST 243 CloseAndDisable(sockfd, events[i]); 244 printf("counterpart send out RST\n"); 245 break; 246 } 247 else if (errno == EINTR) 248 { 249 // 被信號中斷 250 continue; 251 } 252 else 253 { 254 // 其他錯誤 255 } 256 } 257 258 if (writenLen == 0) 259 { 260 // 這裡表示對端的socket已正常關閉. 261 CloseAndDisable(sockfd, events[i]); 262 263 printf("counterpart has shut off\n"); 264 break; 265 } 266 267 // 以下的情況是writenLen > 0 268 count += writenLen; 269 if(count < strlen) 270 { 271 // 可能還沒有寫完 272 continue; 273 } 274 else // 0 < writenLen < MAXLINE 275 { 276 // 已經寫完了 277 bWritten = true; 278 break; // 退出while(1) 279 } 280 } 281 282 if (bWritten == true) 283 { 284 //設置用於讀操作的文件描述符 285 ev.data.fd=sockfd; 286 287 //設置用於注測的讀操作事件 288 ev.events=EPOLLIN | EPOLLET; 289 290 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); 291 } 292 } 293 } 294 } 295 return 0; 296 }
2.客戶端
1 #include <sys/socket.h> 2 #include <sys/epoll.h> 3 #include <netinet/in.h> 4 #include <arpa/inet.h> 5 #include <fcntl.h> 6 #include <unistd.h> 7 #include <stdio.h> 8 #include <errno.h> 9 #include <string.h> 10 #include <stdlib.h> 11 #include <pthread.h> 12 13 14 15 16 #define bool int 17 #define false 0 18 #define true 1 19 20 21 #define MAXLINE 100 22 #define OPEN_MAX 100 23 #define LISTENQ 20 24 #define SERV_PORT 5000 25 #define INFTIM 1000 26 27 28 29 30 int sockfd; 31 char recvline[MAXLINE + 1] = "\0"; 32 pthread_t threadid; 33 34 void *m_thread(){ 35 int len=0; 36 while(1){ 37 len = recv(sockfd,recvline,30,0); 38 recvline[len] = '\0'; 39 printf("%s",recvline); 40 if(len == -1) 41 return; 42 } 43 } 44 45 int 46 main(int argc, char **argv) 47 { 48 49 50 struct sockaddr_in servaddr; 51 52 if (argc != 2){ 53 printf("usage: a.out <IPaddress>\n"); 54 return 0; 55 } 56 57 if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ 58 59 printf("socket error\n"); 60 return 0; 61 } 62 63 64 65 66 bzero(&servaddr, sizeof(servaddr)); 67 servaddr.sin_family = AF_INET; 68 servaddr.sin_port = htons(5000); 69 if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) 70 printf("inet_pton error for %s\n", argv[1]); 71 72 if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) 73 printf("connect error\n"); 74 if(pthread_create(&threadid, NULL, m_thread, NULL) != 0) 75 perror("create Thread:"); 76 77 char input[100]; 78 while (fgets(input, 100, stdin) != EOF) 79 { 80 write(sockfd, input, strlen(input)); 81 82 int n = 0; 83 int count = 0; 84 85 } 86 exit(0); 87 }
參考:
blog.csdn.net/ljx0305/article/details/4065058
http://www.cnblogs.com/aicro/archive/2012/12/27/2836170.html
http://blog.csdn.net/dodo_check/article/details/8553265