《Unix網路編程》這本書附帶了許多短小精美的小程式,我在閱讀此書的時候,將書上的代碼按照自己的理解重寫了一遍(大部分是抄書上的),加深一下自己的理解(純看書太困了,呵呵)。此例子在Ubuntu10.04上測試通過。
PS:程式裡使用了包裹函數(首字母是大寫的函數)和常量(所有字母都是大寫的常量)的聲明在my_unp.h檔案中,定義在unp_base.c和unp_thread.c中,地址:http://blog.csdn.net/aaa20090987/article/details/8096701
程式簡介:這是一個運用epoll系列函數進行IO複用的伺服器模型。它是目前UNIX與LINUX平台上效率最高,最受歡迎的IO複用傳輸模型。
上代碼:
#include "my_unp.h" int main(void) { int listenfd, connfd, sockfd, epfd; int i, maxi, nfds; ssize_t n; char buf[MAXLINE]; socklen_t clilen; struct sockaddr_in cliaddr; struct sockaddr_in servaddr; //聲明epoll_event結構體的變數,ev用於註冊事件,數組用於回傳要處理的事件 struct epoll_event ev, events[256]; //建立一個epoll的控制代碼,size用來告訴核心這個監聽的數目一共有多大 epfd = Epoll_create(256); //建立用於TCP協議的通訊端 listenfd = Socket(AF_INET, SOCK_STREAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); //把socket和socket地址結構聯絡起來 Bind(listenfd, (SA*)&servaddr, sizeof(servaddr)); //開始監聽LISTENQ連接埠 Listen(listenfd, LISTENQ); //設定與要處理的事件相關的檔案描述符和事件 ev.data.fd = listenfd; ev.events = EPOLLIN|EPOLLET; //註冊epoll事件 Epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd,&ev); maxi = 0; while(1) { //等待epoll事件的發生 //返回需要處理的事件數目nfds,如返回0表示已逾時。 nfds = Epoll_wait(epfd, events, 20, 500); //處理所發生的所有事件 for(i=0; i < nfds; ++i) { //如果新監測到一個SOCKET使用者串連到了綁定的SOCKET連接埠,建立新的串連。 if(events[i].data.fd == listenfd) { connfd = Accept(listenfd,(SA*)&cliaddr, &clilen); printf("connection from %s, port %d.\n", Inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, buf, sizeof(buf)), ntohs(cliaddr.sin_port)); //設定用於讀操作的檔案描述符和事件 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; if ( sockfd < 0 ) continue; n = read(sockfd, buf, MAXLINE); if ( n < 0) { // Connection Reset:你串連的那一端已經斷開了, //而你卻還試著在對方已斷開的socketfd上讀寫資料! if (errno == ECONNRESET) { Close(sockfd); events[i].data.fd = -1; } else error_quit("read error"); } //如果讀入的資料為空白 else if ( n == 0 ) { Close(sockfd); events[i].data.fd = -1; } else { //設定用於寫操作的檔案描述符和事件 ev.data.fd = sockfd; ev.events = EPOLLOUT|EPOLLET; //註冊事件 Epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } } //如果有資料發送 else if(events[i].events & EPOLLOUT) { sockfd = events[i].data.fd; Writen(sockfd, buf, n); //設定用於讀操作的檔案描述符和事件 ev.data.fd = sockfd; ev.events = EPOLLIN|EPOLLET; //註冊事件 Epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev); } } } return 0; }
與其配套的用戶端在這裡:http://blog.csdn.net/aaa20090987/article/details/8462262