自己写的一个Epoll实例

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

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;
}



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值