epoll - 其一
头文件:#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
浙公网安备 33010602011771号