使用epoll实现TCP多路复用
来源:互联网 发布:大连知润科技 编辑:程序博客网 时间:2024/06/11 14:55
epoll极简介绍
关于epoll的详细介绍,已经有较多文章可以参考,例如这篇文章介绍就比较详细:
http://blog.chinaunix.net/uid-24517549-id-4051156.htmlepoll编程的接口:
epoll_create
创建一个epoll内核对象,返回指向该对象的fdepoll_ctl
往epoll中添加、删除、修改需要监控的套接字epoll_wait
等待epoll中的套接字产生可读、可写、异常消息
使用epoll时有如下应该注意的地方:
- 确保被epoll的套接字必须是非阻塞的
- 读取可读的TCP套接字时,需要在循环中读取多次,直到返回值为-1且errno为EAGAIN为止,因为只有这种情况才说明可读的数据已经全部读完了
- 已经出错或断开连接的fd需要及时从epoll中删除掉,然后close fd
如下代码是使用epoll实现的多路复用TCP的简单Server及其测试客户端:
/****************************************************************************** * 文件名称:TestEpoll.cpp * 文件描述:Epoll测试服务器端 * 创建日期:2015-04-09 * 作 者:casheywen ******************************************************************************/#include <iostream>using namespace std;#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/epoll.h>#include <arpa/inet.h>#include <unistd.h>#include <fcntl.h>#define LOG_ERR(fmt, args...) fprintf(stderr, "%d|"fmt"\n", __LINE__, ##args)#define LOG_INFO(fmt, args...) fprintf(stdout, "%d|"fmt"\n", __LINE__, ##args)int CreateListenFd(const char *pszIP, uint16_t usPort){ struct sockaddr_in stAddr; stAddr.sin_family = AF_INET; stAddr.sin_port = htons(usPort); stAddr.sin_addr.s_addr = inet_addr(pszIP); socklen_t nAddrLen = sizeof(struct sockaddr_in); int iFd = socket(AF_INET, SOCK_STREAM, 0); if (iFd < 0) { LOG_ERR("create socket fail: %s", strerror(errno)); return -1; } if (0 > bind(iFd, (struct sockaddr *)&stAddr, nAddrLen)) { LOG_ERR("bind fail: %s", strerror(errno)); return -1; } if (0 > listen(iFd, 64)) { LOG_ERR("listen fail: %s", strerror(errno)); return -1; } LOG_INFO("Listening: %s:%hu, fd=%d", pszIP, usPort, iFd); return iFd;}bool SetSockNonBlock(int iSockfd){ int iRet = fcntl(iSockfd, F_GETFL, 0); if (-1 == iRet) { return false; } if (-1 == fcntl(iSockfd, F_SETFL, iRet | O_NONBLOCK)) { return false; } return true;}int main(){ int iEpollFd = epoll_create(100); // 100为预估需要epoll的fd数量 if (iEpollFd < 0) { LOG_ERR("epoll_create fail: %s"); return 1; } int iListenFd = CreateListenFd("0.0.0.0", 12333); if (iListenFd < 0) { LOG_ERR("CreateListenFd Fail"); return 1; } if (!SetSockNonBlock(iListenFd)) // 确保socket为非阻塞状态 { LOG_ERR("SetSockNonBlock Fail: %s", strerror(errno)); return 1; } struct epoll_event ev, events[20]; ev.events = EPOLLIN; ev.data.fd = iListenFd; // 将监听的socket加入epoll int iRet = epoll_ctl(iEpollFd, EPOLL_CTL_ADD, iListenFd, &ev); if (iRet < 0) { LOG_ERR("epoll_ctl fail: %s", strerror(errno)); return 1; } while (true) { iRet = epoll_wait(iEpollFd, events, 20, -1); // 最后的-1表示超时时间无穷大 if (iRet < 0) { if (errno == EINTR) { LOG_ERR("Interrupted, quit."); } else { LOG_ERR("epoll_wait fail: %s", strerror(errno)); } return 1; } int iEvents = iRet; for (int i = 0; i < iEvents; i++) { // 对于监听状态中的套接字,可读意味着有新的连接 if (events[i].data.fd == iListenFd) { struct sockaddr_in stClientAddr; socklen_t nAddrLen = sizeof(stClientAddr); memset(&stClientAddr, 0, sizeof(stClientAddr)); int iClientFd = accept(iListenFd, (struct sockaddr *)&stClientAddr, &nAddrLen); if (iClientFd < 0) { LOG_ERR("accept fail: %s", strerror(errno)); return 1; } if (!SetSockNonBlock(iClientFd)) // 将新连接的套接字设置为非阻塞 { LOG_ERR("SetSockNonBlock Fail: fd=%d %s", iClientFd, strerror(errno)); return 1; } LOG_INFO("Connected:%s:%hu, fd=%d", inet_ntoa(stClientAddr.sin_addr), htons(stClientAddr.sin_port), iClientFd); ev.events = EPOLLIN; ev.data.fd = iClientFd; // 将连接的fd加入epoll int iRet = epoll_ctl(iEpollFd, EPOLL_CTL_ADD, iClientFd, &ev); if (iRet < 0) { LOG_ERR("epoll_ctl fail: %s", strerror(errno)); return 1; } } else // 对于客户端连接可读的情况 { int iClientFd = events[i].data.fd; static char s_acBuf[10 * 1024] = {0}; int iTotal = 0; do { iRet = recv(iClientFd, &s_acBuf[iTotal], sizeof(s_acBuf) - iTotal, 0); if (iRet > 0) { iTotal += iRet; } else if (iRet < 0 && errno == EAGAIN) { LOG_INFO("Total: %d Bytes, [%s]", iTotal, s_acBuf); break; } else { if (iRet == 0) // 连接已经断开 { LOG_INFO("Disconnected: fd=%d", iClientFd); } else // iRet < 0 出现错误 { LOG_INFO("recv fail: fd=%d %s", iClientFd, strerror(errno)); } // 将出错或断开连接的fd从epoll中去掉 int iRet = epoll_ctl(iEpollFd, EPOLL_CTL_DEL, iClientFd, NULL); if (iRet < 0) { LOG_ERR("epoll_ctl fail: %s", strerror(errno)); return 1; } close(iClientFd); break; } } while (iTotal < sizeof(s_acBuf)); } } } return 0;}
/****************************************************************************** * 文件名称:TcpClient.cpp * 文件描述:Epoll测试客户端 * 创建日期:2015-04-09 * 作 者:casheywen ******************************************************************************/#include <iostream>using namespace std;#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <signal.h>#define LOG_ERR(fmt, args...) fprintf(stderr, "%d|"fmt"\n", __LINE__, ##args)#define LOG_INFO(fmt, args...) fprintf(stdout, "%d|"fmt"\n", __LINE__, ##args)void SigPipeHandler(int iSigno){ LOG_ERR("SigPipe received"); exit(1);}bool ConnectTcpSocket(int iFd, const char *pszIP, uint16_t usPort){ struct sockaddr_in stAddr; memset(&stAddr, 0, sizeof(stAddr)); stAddr.sin_family = AF_INET; inet_aton(pszIP, &stAddr.sin_addr); stAddr.sin_port = htons(usPort); int iRet = connect(iFd, (struct sockaddr *)&stAddr, sizeof(stAddr)); if (iRet < 0) { LOG_ERR("Connect Fail: %s", strerror(errno)); return false; } return true;}int main(){ int iFd = socket(AF_INET, SOCK_STREAM, 0); if (iFd < 0) { LOG_ERR("Create Socket Fail: %s", strerror(errno)); return 1; } if (!ConnectTcpSocket(iFd, "127.0.0.1", 12333)) { LOG_ERR("ConnectUnixSocket Fail"); return 1; } LOG_INFO("Connect Success"); if (SIG_ERR == signal(SIGPIPE, SigPipeHandler)) // 当连接中断时调用write函数会收到SIGPIPE信号 { LOG_ERR("Signal Fail: %s", strerror(errno)); return 1; } char szContent[4096]; ssize_t nWrite = 0; while (cin >> szContent) { nWrite = write(iFd, szContent, strlen(szContent)); if (nWrite < 0) { LOG_ERR("write Fail: %s", strerror(errno)); return 1; } } return 0;}
0 0
- 使用epoll实现TCP多路复用
- epoll实现I/O多路复用
- epoll多路复用
- epoll多路复用
- udp epoll tcp epoll使用
- 多路复用I/O Epoll的简单使用
- 浅析epoll-为何多路复用I/O要使用epoll
- 浅析epoll-为何多路复用I/O要使用epoll
- 浅析epoll-为何多路复用I/O要使用epoll
- epoll实现TCP通信
- 多路复用IO实现方式:select,poll,epoll的区别
- Python异步非阻塞IO多路复用Select/Poll/Epoll使用
- I/O多路复用select、poll、epoll的区别使用
- Python异步非阻塞IO多路复用Select/Poll/Epoll使用
- I/O多路复用select、poll、epoll的区别使用
- 使用poll实现socket多路复用
- linux 使用select实现多路复用
- IO多路复用之epoll
- NOI2005维护数列
- 美素数 HDU 4548 TLE! = =|| 打表是一种态度。。。
- Android Studio安装及首次运行遇到的问题
- strchr函数的实现,顺序在字符串中寻找所需字符
- CentOS通过删除旧内核解决/boot空间不足的问题
- 使用epoll实现TCP多路复用
- Facebook Graph API - 统计comment的数量
- iOS应用性能调优的25个建议和技巧
- 第21题
- 【瞎搞】 Codeforces Round #215 (Div. 1) A Sereja and Algorithm
- 搬家APP-竞品分析报告
- Jsoup解析html页面实现CSDN博客客户端
- 学习设计模式五步走
- 数组实现顺序栈与队列