select和poll被鄙视得厉害, 因为有了Linux epoll的存在。 网上讲解select/poll/epoll的例子不胜枚举, 各种比喻, 各种图示。 其实, epoll并没有那么玄乎。 本文中, 我们不进行画图讲解, 也尽量避免过多的文字描述, 只是初步来感受一下epoll.
服务端代码为:
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <errno.h>
- #include <malloc.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <sys/ioctl.h>
- #include <stdarg.h>
- #include <fcntl.h>
- #include <fcntl.h>
- #include <sys/poll.h>
- #include <sys/epoll.h>
- #define BACKLOG 100
- int main()
- {
- int iListenSock = socket(AF_INET, SOCK_STREAM, 0);
- sockaddr_in addr;
- memset(&addr, 0, sizeof(addr));
- inet_aton("0.0.0.0", &addr.sin_addr);
- addr.sin_family = AF_INET;
- addr.sin_port = htons(8888);
- int iOpt = 1;
- setsockopt(iListenSock, SOL_SOCKET, SO_REUSEADDR, &iOpt, sizeof(iOpt)); // 标配
- bind(iListenSock, (sockaddr*)&addr, sizeof(addr));
- listen(iListenSock, BACKLOG);
- epoll_event ev;
- ev.data.fd = iListenSock;
- ev.events = EPOLLIN;
- epoll_event events[BACKLOG + 1];
- int epollFD = epoll_create(BACKLOG + 1); // 告诉内核监测的数目, 返回的epollFD为epoll管理句柄
- epoll_ctl(epollFD, EPOLL_CTL_ADD, iListenSock, &ev); // 将ev和对应的iListenSock添加到epoll句柄,用于被epollFD管理
- while(1)
- {
- int timeoutMS = -1; // 永不超时
- int nfds = epoll_wait(epollFD, events, BACKLOG + 1, timeoutMS); // events和nfds是一对输出值
- printf("nfds is %d\n", nfds);
- for(int i = 0; i < nfds; i++)
- {
- if(events[i].data.fd == iListenSock) // 用于监听客户端连接的socket
- {
- int iConnSock = accept(iListenSock, NULL, NULL);
- if (iConnSock < 0)
- {
- continue;
- }
- ev.data.fd = iConnSock;
- ev.events = EPOLLIN;
- epoll_ctl(epollFD, EPOLL_CTL_ADD, iConnSock, &ev); // 将ev和对应的iListenSock添加到epoll句柄,用于被epollFD管理
- printf("new sock came, fd is %d\n", iConnSock);
- }
- else
- {
- int iConnSock = events[i].data.fd; // 用于通信的socket
- char szBuf[1024] = {0};
- int recvLen = recv(iConnSock, szBuf, sizeof(szBuf) - 1, 0);
- if (recvLen > 0)
- {
- printf("recv data [%s] from fd [%d]\n", szBuf, iConnSock);
- }
- else if(0 == recvLen)
- {
- ev.data.fd = iConnSock;
- epoll_ctl(epollFD, EPOLL_CTL_DEL, iConnSock, &ev);
- close(iConnSock);
- printf("connection closed, local fd is [%d]\n", iConnSock);
- }
- else
- {
- ev.data.fd = iConnSock;
- epoll_ctl(epollFD, EPOLL_CTL_DEL, iConnSock, &ev);
- close(iConnSock);
- printf("recv error, local fd is [%d]\n", iConnSock);
- }
- }
- }
- }
- close(epollFD);
- close(iListenSock);
- return 0;
- }
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <errno.h>
- #include <malloc.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <sys/ioctl.h>
- #include <stdarg.h>
- #include <fcntl.h>
- #include <fcntl.h>
- int main()
- {
- int sockClient = socket(AF_INET, SOCK_STREAM, 0);
- struct sockaddr_in addrSrv;
- addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1");
- addrSrv.sin_family = AF_INET;
- addrSrv.sin_port = htons(8888);
- connect(sockClient, ( const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in));
- char szSendBuf[100] = "this is me";
- while(1)
- {
- send(sockClient, szSendBuf, strlen(szSendBuf) + 1, 0);
- scanf("%s", szSendBuf);
- }
- close(sockClient);
- return 0;
- }
- all: server client
- server: server.o
- g++ -o server server.o
- client: client.o
- g++ -o client client.o
- server.o: server.cpp
- g++ -c server.cpp
- client.o:client.cpp
- g++ -c client.cpp
- clean:
- rm -f server client *.o
- xxxxxx:~/network> ./server
- nfds is 1
- new sock came, fd is 5
- nfds is 1
- recv data [this is me] from fd [5]
- nfds is 1
- new sock came, fd is 6
- nfds is 1
- recv data [this is me] from fd [6]
- nfds is 1
- new sock came, fd is 7
- nfds is 1
- recv data [this is me] from fd [7]
本文只是epoll的一个简单开始, 比较基础, 后面我们还会进行更多的介绍, 便于深入理解其思路。