Epoll的机制就不说了,网上一大堆,只简单列一下Epoll相关的数据结构吧:
structepoll_event {
__uint32_t events; // Epoll events
epoll_data_t data; // User datavariable
};
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
可以看到,epoll_data是个union类型,一般都会存一个fd,但也可以存一个指针。
这里我用它存了一个指针,指向自己定义的一个结构体:
//这个是fd的回调函数,第一个参数传events,第二个参数传EPOLL_DATA数据的指针。
typedef int (*EPOLL_CB)(unsigned int, void*);
//结构体中存了fd和回调函数,会添加到一个链表中保存起来。
typedef struct epoll_data
{
DTQ_NODE_S stEpollDataNode;
int iFd;
EPOLL_CB pfCallback;
}EPOLL_DATA;
//另外定义一个结构,用来保存epoll的fd和epoll_data的链表头:
typedef struct epoll_info
{
int iEpollfd;
DTQ_HEAD_S stEpollData;
}EPOLL_INFO;
核心的思路是,epoll_add事件时,把fd和回调函数存在EPOLLDATA上,然后加到epoll_data上,并把EPOLLDATA数据存在链表上保存起来,当事件到来时,直接调用epoll_data下存的回调函数。
当epoll_del时,删除注册的事件,并把数据从链表上摘除。
这样,可以轻松处理各种IO事件,定时器事件和一些其他事件,并且不会影响epoll机制原有的效率,实现简单,使用方便。
实现了一个简单的服务器程序,编译验证通过,一些细节没有考虑到,以后会添加进去。
下面贴出源码:
<<<main.c>>>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include "list.h"
#define SUCCESS 0
#define FAILED -1
#define LISTEN_MAX 10
#define EPOLLEVENTS_MAX 10
#define BUFSIZE_MAX 1024
#define SERVERADDR INADDR_ANY
#define SERVERPORT 8888
/* Epoll Event */
typedef int (*EPOLL_CALLBACK)(unsigned int, void*);
typedef struct epollinfo
{
int iEpollFd;
DTQ_HEAD stEpollData;
}EPOLL_INFO;
typedef struct epolldata
{
DTQ_NODE stEpollNode;
int iFd;
EPOLL_CALLBACK pfCallback;
}EPOLL_DATA;
int EpollInit(EPOLL_INFO *pstEpollInfo)
{
int iEpollFd;
iEpollFd = epoll_create(1);
if (iEpollFd < 0)
{
return FAILED;
}
pstEpollInfo->iEpollFd = iEpollFd;
DTQ_Init(&pstEpollInfo->stEpollData);
return SUCCESS;
}
int EpollAdd(EPOLL_INFO *pstEpollInfo, int iFd, unsigned int uiEvent, EPOLL_CALLBACK pfCallback)
{
int iRet;
EPOLL_DATA *pstEpollData = NULL;
struct epoll_event stEvent = {0};
/* 创建一个链表节点 */
pstEpollData = (EPOLL_DATA*)malloc(sizeof(EPOLL_DATA));
if (NULL == pstEpollData)
{
return FAILED;
}
/* 初始化该节点,并添加到链表头上 */
DTQ_NodeInit(&pstEpollData->stEpollNode);
DTQ_AddHead(&pstEpollInfo->stEpollData, &pstEpollData->stEpollNode);
pstEpollData->iFd = iFd;
pstEpollData->pfCallback = pfCallback;
stEvent.events = uiEvent;
stEvent.data.ptr = (void*)pstEpollData;
iRet = epoll_ctl(pstEpollInfo->iEpollFd, EPOLL_CTL_ADD, iFd, &stEvent);
if (0 != iRet)
{
return FAILED;
}
return SUCCESS;
}
void EpollDel(int iEpollFd, EPOLL_DATA *pstEpollData)
{
int iRet = 0;
DTQ_NODE *pstNode;
struct epoll_event stEvent = {0};
stEvent.events = 0;
stEvent.data.ptr = NULL;
iRet = epoll_ctl(iEpollFd, EPOLL_CTL_DEL, pstEpollData->iFd, &stEvent);
if (0 != iRet)
{
return;
}
/* 删除epoll事件时,将epoll_data下记录的链表节点摘除 */
DTQ_Del(&pstEpollData->stEpollNode);
return;
}
int ServerInit()
{
int iRet;
int iListenFd;
int iReusePort = 1;
struct sockaddr_in stServerAddr = {0};
iListenFd = socket(AF_INET, SOCK_STREAM, 0);
if (iListenFd < 0)
{
perror("listen socket");
return FAILED;
}
/* 设置IP地址重用,避免fd关闭后再开bind失败 */
(void)setsockopt(iListenFd, SOL_SOCKET, SO_REUSEADDR, &iReusePort, (unsigned int)sizeof(int));
memset(&stServerAddr, 0, sizeof(stServerAddr));
stServerAddr.sin_family = AF_INET;
stServerAddr.sin_addr.s_addr = htonl(SERVERADDR);
stServerAddr.sin_port = htons(SERVERPORT);
iRet = bind(iListenFd, (struct sockaddr *)&stServerAddr, sizeof(stServerAddr));
if (SUCCESS != iRet)
{
perror("bind");
return FAILED;
}
iRet = listen(iListenFd, LISTEN_MAX);
if (SUCCESS != iRet)
{
perror("listen");
return FAILED;
}
return iListenFd;
}
/* 连接过来的FD处理IO事件函数 */
int ProcMsg(unsigned int uiEvent, void *pData)
{
int iRet;
char szBuf[BUFSIZE_MAX];
int iRecvLen, iClientAddrLen;
int iConnectFd;
EPOLL_DATA *pstEpollData;
struct sockaddr_in stClientAddr = {0};
pstEpollData = (EPOLL_DATA*)pData;
iConnectFd = pstEpollData->iFd;
if (uiEvent & EPOLLIN)
{
iRecvLen = recv(iConnectFd, szBuf, BUFSIZE_MAX, MSG_DONTWAIT);
if (iRecvLen > 0)
{
szBuf[iRecvLen - 1] = '\0';
printf(">%s\n", szBuf);
(void) send(iConnectFd, szBuf, BUFSIZE_MAX, 0);
}
else if (0 == iRecvLen)
{
iClientAddrLen = sizeof(stClientAddr);
(void) getpeername(iConnectFd, (struct sockaddr *) &stClientAddr, &iClientAddrLen);
printf("connect %s %d is closed\n", inet_ntoa(stClientAddr.sin_addr), ntohs(stClientAddr.sin_port));
EpollDel(g_stMainEpollData.iEpollFd, pstEpollData);
close(iConnectFd);
}
else
{
perror("recv");
EpollDel(g_stMainEpollData.iEpollFd, pstEpollData);
close(iConnectFd);
}
}
return iRet;
}
/* listen FD处理新连接的回调函数 */
int ServerAccept(unsigned int uiEvent, void *pData)
{
int iRet;
int iListenFd, iConnectFd;
EPOLL_DATA *pstEpollData;
struct sockaddr_in stClientAddr = {0};
int iClientAddrLen = sizeof(stClientAddr);
pstEpollData = (EPOLL_DATA*)pData;
iListenFd = pstEpollData->iFd;
iConnectFd = accept(iListenFd, (struct sockaddr *)&stClientAddr, &iClientAddrLen);
if (iConnectFd < 0)
{
perror("accept");
return FAILED;
}
printf("new connect from %s %d\n", inet_ntoa(stClientAddr.sin_addr), ntohs(stClientAddr.sin_port));
/* 将新连接的FD加入epoll */
iRet = EpollAdd(&g_stMainEpollData, iConnectFd, EPOLLIN, ProcMsg);
if (iRet < 0)
{
perror("epoll_ctl add: client");
return FAILED;
}
return iRet;
}
EPOLL_INFO g_stMainEpollData;
int main(int argc, char *argv[])
{
int iRet;
int iReadyFds, i;
int iListenFd, iConnectFd;
int iClientAddrLen;
int iRecvLen;
struct sockaddr_in stServerAddr, stClientAddr;
struct epoll_event stEvent, astEvents[EPOLLEVENTS_MAX];
EPOLL_DATA *pstEpollData;
EPOLL_CALLBACK pfEpollCallback;
/* 初始化主线程的epoll */
iRet = EpollInit(&g_stMainEpollData);
if (FAILED == iRet)
{
perror("epoll socket");
return FAILED;
}
/* 初始化服务端Listen FD */
iListenFd = ServerInit();
if (iListenFd < 0)
{
perror("ServerInit");
return FAILED;
}
/* 将Listen FD加到epoll中 */
iRet = EpollAdd(&g_stMainEpollData, iListenFd, EPOLLIN, ServerAccept);
if (iRet < 0)
{
perror("epoll_ctl add: listen");
return FAILED;
}
/* 主线程 Epoll 主循环 */
while(1)
{
iReadyFds = epoll_wait(g_stMainEpollData.iEpollFd, astEvents, EPOLLEVENTS_MAX, -1);
for (i = 0; i < iReadyFds; i++)
{
pstEpollData = (EPOLL_DATA*)(astEvents[i].data.ptr);
pfEpollCallback = pstEpollData->pfCallback;
if (NULL != pfEpollCallback)
{
(void)pfEpollCallback(astEvents[i].events, astEvents[i].data.ptr);
}
}
}
return SUCCESS;
}
<<<list.h>>>
typedef struct dtq_node
{
struct dtq_node* pstPrev;
struct dtq_node* pstNext;
}DTQ_NODE;
typedef struct dtq_head
{
DTQ_NODE stHead;
}DTQ_HEAD;
inline void DTQ_Init(DTQ_HEAD* pstList);
inline void DTQ_NodeInit(DTQ_NODE* pstNode);
inline void DTQ_AddAfter(DTQ_NODE* pstPrev, DTQ_NODE* pstInst);
inline void DTQ_AddHead(DTQ_HEAD* pstList, DTQ_NODE* pstNode);
inline void DTQ_Del(const DTQ_NODE* pstNode);
inline void DTQ_Init(DTQ_HEAD* pstList)
{
pstList->stHead.pstNext = &pstList->stHead;
pstList->stHead.pstPrev = &pstList->stHead;
return;
}
inline void DTQ_NodeInit(DTQ_NODE* pstNode)
{
pstNode->pstNext = (DTQ_NODE*)NULL;
pstNode->pstPrev = (DTQ_NODE*)NULL;
return;
}
inline void DTQ_AddAfter(DTQ_NODE* pstPrev, DTQ_NODE* pstInst)
{
pstInst->pstPrev = pstPrev;
pstInst->pstNext = pstPrev->pstNext;
pstPrev->pstNext = pstInst;
pstInst->pstNext->pstPrev = pstInst;
return;
}
inline void DTQ_AddHead(DTQ_HEAD* pstList, DTQ_NODE* pstNode)
{
DTQ_AddAfter (&pstList->stHead, pstNode);
return;
}
inline void DTQ_Del(const DTQ_NODE* pstNode)
{
pstNode->pstPrev->pstNext = pstNode->pstNext;
pstNode->pstNext->pstPrev = pstNode->pstPrev;
return;
}
<<<client.c>>>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SUCCESS 0
#define FAILED -1
#define LISTENNUM_MAX 10
#define BUFSIZE_MAX 1024
int main(int argc, char *argv[])
{
int iRet;
int iLen;
int iTCP_ClientFd;
char szSendBuf[BUFSIZE_MAX] = "\0";
struct sockaddr_in stTCP_ServerAddr;
iTCP_ClientFd = socket(AF_INET, SOCK_STREAM, 0);
if (iTCP_ClientFd < 0)
{
perror("socket");
return FAILED;
}
memset(&stTCP_ServerAddr, 0, sizeof(stTCP_ServerAddr));
stTCP_ServerAddr.sin_family = AF_INET;
stTCP_ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
stTCP_ServerAddr.sin_port = htons(atoi("8888"));
iRet = connect(iTCP_ClientFd, (struct sockaddr *) &stTCP_ServerAddr, sizeof(stTCP_ServerAddr));
if (SUCCESS != iRet)
{
perror("connect");
return FAILED;
}
printf(">");
while (NULL != fgets(szSendBuf, BUFSIZE_MAX, stdin))
{
iLen = strlen(szSendBuf);
szSendBuf[iLen - 1] = '\0';
(void) send(iTCP_ClientFd, szSendBuf, BUFSIZE_MAX, MSG_DONTWAIT);
printf(">");
}
close(iTCP_ClientFd);
return SUCCESS;
}

本文介绍了一种使用Epoll机制实现的高效服务器程序,包括数据结构设计、回调函数应用以及事件处理流程。详细解释了如何利用Epoll管理IO事件、定时器事件和其他事件,同时保持了原有Epoll机制的高效率。并通过一个简单的服务器实例,展示了实现过程和关键代码,旨在简化事件处理并提高服务器性能。
1687

被折叠的 条评论
为什么被折叠?



