《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