先貼一段代碼,代碼很簡單要看過epoll如何使用,都應該能看懂。python
這是服務端程序:socket
#include <sys/socket.h> #include <sys/epoll.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> #define MAXLINE 10 #define OPEN_MAX 100 #define LISTENQ 20 #define SERV_PORT 5555 #define INFTIM 1000 void setnonblocking(int sock) { int opts; opts = fcntl(sock, F_GETFL); if(opts < 0) { perror("fcntl(sock, GETFL)"); exit(1); } opts = opts | O_NONBLOCK; if(fcntl(sock, F_SETFL, opts) < 0) { perror("fcntl(sock, SETFL, opts)"); exit(1); } } int main(int argc, char *argv[]) { printf("epoll socket begins.\n"); int i, maxi, listenfd, connfd, sockfd, epfd, nfds, on = 1; ssize_t n; char line[MAXLINE]; socklen_t clilen; struct epoll_event ev, events[20]; epfd = epoll_create(256); struct sockaddr_in clientaddr; struct sockaddr_in serveraddr; listenfd = socket(AF_INET, SOCK_STREAM, 0); setnonblocking(listenfd); ev.data.fd = listenfd; ev.events = EPOLLIN | EPOLLET; epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev); setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int)); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; char *local_addr = "0.0.0.0"; inet_aton(local_addr, &(serveraddr.sin_addr)); serveraddr.sin_port = htons(SERV_PORT); int ret = bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); if(ret < 0) { printf("%s\n", "bind error!"); exit(-1); } listen(listenfd, LISTENQ); maxi = 0; for(; ;) { nfds = epoll_wait(epfd, events, 20, 500); for(i = 0; i < nfds; ++i) { if(events[i].data.fd == listenfd) { printf("accept connection, fd is %d\n", listenfd); connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen); if(connfd < 0) { perror("connfd < 0"); exit(1); } setnonblocking(connfd); char *str = inet_ntoa(clientaddr.sin_addr); printf("connect from %s\n", str); ev.data.fd = connfd; ev.events = EPOLLIN | EPOLLET; epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev); } else if(events[i].events & EPOLLIN) { sockfd = events[i].data.fd; n = read(sockfd, line, MAXLINE); if(n < 0 && errno == EAGAIN) { printf("%s\n", "數據已經讀完,沒有更多的數據能夠讀了。"); } else if(n == 0) //errno { printf("%s\n", "客戶端斷開鏈接。"); epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, &ev); } else if(n <= 0) { printf("%s\n錯誤代碼爲%d", "讀出錯。", errno); epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, &ev); } else { printf("%s\n", line); memset(line, 0, MAXLINE); n = write(sockfd, "hello client!", strlen("hello client!")); if(n < 0 && errno == EAGAIN) { printf("%s\n", "系統緩衝區數據已寫滿。"); } else if(n <= 0) { printf("%s\n錯誤代碼爲%d", "客戶端斷開鏈接或出錯。", errno); } else { ev.data.fd = sockfd; ev.events = EPOLLIN | EPOLLET; epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev); } } } else if(events[i].events & EPOLLOUT) { } } } }
客戶端程序:測試
能夠不用代碼寫客戶端,用telnet鏈接就行。code
telnet localhost 5555
#!/usr/bin/python from socket import * from time import sleep host = gethostname() addr = (host, 5555) sockCli = socket() sockCli.connect(addr) while True: sockCli.sendall("sadf") data = sockCli.recv(1024) print data sleep(1) sockCli.close()
這段服務端的代碼展現的是用,epoll寫的一個簡單的服務程序, ET非阻塞模式。server
關於read的返回值:隊列
一、返回值n=-1, errno = EAGAIN;當返回值等於你要讀取的數據時,說明你還有數據要讀。 當數據量比較大,屢次讀數據的時候非阻塞read的返回值爲-1,errno值爲11(EAGAIN)時,這個 並非發生了錯誤,而是數據已經被讀完,這個時候中止讀數據就行了。阻塞模式不會這樣,由於當沒有數據的時候,read就阻塞了。事件
二、返回值n=0, errno = 0。說明客戶端已經關閉了。通過測試即便沒有數據的時候,阻塞的read就阻塞,非阻塞的read時爲第一種狀況。只有客戶端關閉時返回0(socket fd是這樣其餘io流爲測試) 。這個時候就就不要再註冊事件了,從隊列中刪除這個fd吧。並且當客戶端斷開時,epoll會主動通知一個EPOLLIN事件。get
三、返回值n=-1,errno != EAGAIN;時是錯了,看一下錯誤碼是多吧。string
關於write的返回值:it
一、返回值n=-1,errno = EAGAIN時; 說明系統緩衝區已經滿了,不能再寫進去了。當write爲阻塞時不會返回這個錯誤,會阻塞掉。
二、返回值n<=0時;客戶端斷開鏈接或出錯。
什麼時候系統會通知一個寫事件:
對於LT 模式,若是註冊了EPOLLOUT,只要該socket可寫(發送緩衝區)未滿,那麼就會觸發EPOLLOUT。
對於ET模式,若是註冊了EPOLLOUT,只有當socket從不可寫變爲可寫的時候,會觸發EPOLLOUT。
上面server端的例子,當你要寫入的數據比較大的時候,可能就會有困擾了,如何寫入的數據量較大,把系統緩衝區寫滿了,若是write設置是阻塞的,那就會阻塞了。這可能不是咱們想要的。
這個時候就須要註冊寫事件,本身在應用層加個發送緩衝區,須要發送數據的時候,就寫到應用層的發送緩衝區。觸發write事件的時候,從緩衝區寫入socket。