知识点:三个多路并发模型(select 、poll 、epoll)
题目:以epoll模型,编写一个可供多个客户端访问的服务器程序。
实现代码:
#include#include #include #include #include #include #include #include #include #include #include #define SRV_PORT 60000#define BUF_SIZE 1024#define MAX_CONN 10000000 // 最大连接数 #define EVS_LEN 10 // epoll_wait()可保存的“收到数据”的fd的最大数目void startServer(){ int iRet; char szSnd[BUF_SIZE]; char szRcv[BUF_SIZE]; char szBuf[BUF_SIZE]; int fd; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd == -1) { perror("fail socket"); return; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(SRV_PORT); iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if (iRet == -1) { perror("fail bind"); close(fd); return; } listen(fd, MAX_CONN);/*=========================================== epoll =======================================*/ int epfd = epoll_create(MAX_CONN); // function 1 struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = STDIN_FILENO; epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev); // function 2 ev.events = EPOLLIN; ev.data.fd = fd; epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); int clientFd; struct sockaddr_in cliAddr; socklen_t addrLen = sizeof(cliAddr); struct epoll_event evs[EVS_LEN]; // 可容纳10个变化的fd int cnt; while(1) { cnt = epoll_wait(epfd, evs, EVS_LEN, -1); // function 3 int i; for (i = 0; i < cnt; i++) { if (evs[i].data.fd == fd) { /* new connect */ clientFd = accept(fd, (struct sockaddr*)&cliAddr, &addrLen); ev.events = EPOLLIN; ev.data.fd = clientFd; epoll_ctl(epfd, EPOLL_CTL_ADD, clientFd, &ev); printf("New connect from %s:%d\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port)); write(clientFd, "Welcome...", 11); } else if (evs[i].data.fd == STDIN_FILENO) { read(STDIN_FILENO, szSnd, BUF_SIZE); printf("command not found\n"); } else { memset (szRcv, 0, BUF_SIZE); iRet = read(evs[i].data.fd, szRcv, BUF_SIZE); if (iRet == 0) { /* 断开连接 */ printf("Disconnect fd:%d\n", evs[i].data.fd); ev.data.fd = evs[i].data.fd; epoll_ctl(epfd, EPOLL_CTL_DEL, evs[i].data.fd, NULL); close(evs[i].data.fd); } else if (iRet < 0) { perror("fail read"); return; } else { memset(szBuf, 0, BUF_SIZE); printf("Recv[%d]:%s\n", evs[i].data.fd, szRcv); memcpy(szBuf, "I have received!", 17); write(evs[i].data.fd, szBuf, strlen(szBuf)); } } } } close(fd); return;}int main(){ startServer(); return 0;}
题目:以select模型,编写一个可供多个客户端访问的服务器程序。
实现代码:
#include#include #include #include #include #include #include #include #include #define SRV_PORT 60000#define BUF_SIZE 1024#define MAX_CONN 10000 void startServer(){ int iRet; char szSnd[BUF_SIZE]; char szRcv[BUF_SIZE]; int fd; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd == -1) { perror("fail socket"); return; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(SRV_PORT); iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if (iRet == -1) { perror("fail bind"); close(fd); return; } listen(fd, MAX_CONN);/**************************************************** select *********************************************************/ fd_set fdset; int i; int maxfd = fd; int clientFd; int fdCnt = 0; // 当前连接的客户端数 int fdArr[MAX_CONN]; // 存放文件描述符fd的数组 struct sockaddr_in cliAddr; socklen_t addrLen = sizeof(cliAddr); while(1) { /* select模型每次都要将“fd们”重新加入fdset,开销很大 */ FD_ZERO(&fdset); FD_SET(STDIN_FILENO, &fdset); FD_SET(fd, &fdset); for (i = 0; i < fdCnt; i++) { FD_SET(fdArr[i], &fdset); // 将用于和客户端通信的fd都加入fdset } select(maxfd + 1, &fdset, NULL, NULL, NULL); if (FD_ISSET(fd, &fdset)) { clientFd = accept(fd, (struct sockaddr*)&cliAddr, &addrLen); if (fdCnt == MAX_CONN) { printf("Connect over count\n"); write(clientFd, "please wait...", 15); close(clientFd); } else { printf("Connect from %s:%d success...\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port)); write(clientFd, "Welcome...", 11); fdArr[fdCnt++] = clientFd; if (clientFd > maxfd) { maxfd = clientFd; // 更新maxfd } } } if (FD_ISSET(STDIN_FILENO, &fdset)) { memset(szSnd, 0, BUF_SIZE); read(STDIN_FILENO, szSnd, 1024); printf("command not found\n"); } for (i = 0; i < fdCnt; i++) { if (FD_ISSET(fdArr[i], &fdset)) { memset(szRcv, 0, BUF_SIZE); iRet = read(fdArr[i], szRcv, BUF_SIZE); if (iRet > 0) { printf("Recv[%d]:%s\n", fdArr[i], szRcv); write(fdArr[i], "I received!", 12); } else if (iRet == 0) { close(fdArr[i]); printf("fd:%d disconnect...\n", fdArr[i]); int j; for(j = i; j < fdCnt - 1; j++) { fdArr[j] = fdArr[j+1]; } fdCnt--; i--; } else { perror("read fail"); return; } } } } return;}int main(){ startServer(); return 0;}
小结:epoll模型的优点在于:①对于客户端的数量没有限制;②内核主动将“可读”的fd写入到struct epoll_events数组内,所以节省了poll模型和select模型的每次轮询整个fd集合的开销。