Epoll 服务器的一种通用框架,epoll_data_t 的使用

13 篇文章 0 订阅

一、前言

前面已经介绍过一个原始结构epoll服务器的使用,原文请点击查看:epoll原始结构服务器

现在,我将在原epoll服务器的基础上对epoll的用法进行改进,进一步简化epoll服务器的编码方式,这种框架在大型项目中十分高效,正所谓框架搭的好,往里面填“dog shit”都不会有问题。

这当中,我们需要用到一个简单的消息封装结构,这在通信中十分常见,类似于一种伪协议,只有内部通用。不过这种内部通用的协议在很多场合能大大简化程序的编码,提升编码效率。

首先来看一个消息头的定义:

typedef struct tagMSG_HEAD
{
    UCHAR   msgType;
    UCHAR   ucFlag;
    USHORT  usReserve;/* 字节对其 */
    UINT    uiMsgLen;
    UINT    uiPara1;/* 消息交互对方回复的值,用于多次交互中记录状态的情况 */
    UINT    uiPara2;
}MSG_HEAD_S;

#define MSG_BUFF_LEN_1024 1024
#define MSG_BUFF_LEN_2048 2048

这里我们定义了一个消息头,包含消息类型,消息携带flag,消息长度,交互参数para1,para2,其中usReserve字段是为了字节对齐留的,这在通信结构中十分常见,为使CPU能够对数据进行快速访问,数据的起始地址应具有“对齐”特性。比如4字节数据的起始地址应位于4字节边界上,即起始地址能够被4整除。更多的关于字节对齐的介绍,请自行bing 或百度。

下面我们再来看看epoll_event的结构:

typedef union epoll_data {  
    void *ptr;  
    int fd;  
    uint32_t u32;  
    uint64_t u64;  
} epoll_data_t;

struct epoll_event {  
    uint32_t events;      // Epoll events  
    epoll_data_t data;      // User data variable  
};

请重点关注epoll_data_t这个字段。这个字段是一个联合体,联合体在同一时间只有一个成员变量,联合体占用的内存大小为所有成员中占用字节最大的成员决定,在此处,就是类型为uint_64_t 的u64。

原始epoll结构有一个很大的麻烦,fd字段放在了epoll_data_t结构 ,这就使得在使用的时候,我们不得不在fd与用户自己的数据之间做一个抉择,关键是fd不能少,少了也就失去 epoll的意义。选择保存用户数据到epoll_data_t中,那么我们自定义 的结构中一定要包含fd,但是请注意,用户自定义的数据此时只能保存一个地址,即保存在指针*ptr中。常规的编程中,我们发现,几乎所有的epoll结构都没有自定义用户数据,即使定义了,对epoll的使用也不得不变得麻烦,并且用户不得不为每一个epoll注册申请一个 内存空间(本人目前没有发现更好的办法,如果不申请内存,那么只能用全局或静态变量)。当有大量服务连接的时候,每次触发epoll机制都不得不重新根据ptr来获取自定义结构内存,这样难免会影响epoll服务器的性能,即使内核对epoll已经足够优化,用户自己查找的过程中,还是必不可少的增加了时间,间接或直接地降低了epoll的效率。

那么该怎么优化呢?

我们还是来看epoll_data_t这个结构:之前说过,联合体内存是邓毅字节最大的成员的字节数,此处就是u64。我们可以发现,这是一个8字节的长整型,有64位之多。我们可以在这个数上作作文章。想过将这个8字节的长整型分成两个整型部分,当然不是真的对分开,是通过左移右移实现。想过用4个字节保存函数指针,单指针的长度一般根据系统而定,32位系统占用4字节,64位系统指针占8字节,因此,不太合数。那么我们是否可以用来保存一个我们想保存的数字,比如消息类型,32位整型最大值位4294967295,最大42亿之多。另一部分32位,我们用来存放fd,SOCKET取值范围是0~4294967295,实际上我们常用的也就是0~65535。好了,想清楚方案,就很容易了。

根据以上规则,我们队epoll_ctl函数进行简单的封装,并把这个想法加上去:注意data.u64的使用。

static int Epoll_Control(const int sockfd, const int oper, const int events, unsigned int CallbackType)
{
    struct epoll_event ep_event;
    int iret = 0;
 
    memset(&ep_event, 0, sizeof(ep_event));
    ep_event.events = events;
    /* 先存自定义消息类型,并左移(向高位移)32位,不同消息类型对应不同的回调函数,低位32位存储fd */
    ep_event.data.u64 = (uint64_t)(((uint64_t)(CallbackType) << 32) | (uint64_t)(int)sockfd);
 
    iret = epoll_ctl(g_Main_Epoll_Fd, oper, sockfd, &ep_event);
    if (0 > iret)
    {
        perror("epoll_ctl error.");
        printf("epoll_ctl error opt:%d\n", oper);
        return -1;
    }
 
    return 0;
}

注册完成了,等待的就是epoll_wait触发回调,触发epoll_wait时,我们可以这样获取到fd和自定义类型,定义为宏,规则:先用int截断长整型,去除fd,然后右移32位,再以unsigned int 截断获取自定义type,先后顺序可调整,方法不变。

#define GET_EPOLL_CALLBACK_TYPE(ep_event)   (unsigned int)((ep_event).data.u64 >>32)
#define GET_EPOLL_FD(ep_event)  (int)(unsigned int)((ep_event).data.u64)

二、源码

由于时间有限,下面的源代码是在之前的epoll服务器上改写的,主要就是添加了epoll的回调,另外就是回调中含有回调,这在框架中十分常见。代码并没有编译调试,但是基本思路是正确的,一些epoll等资源回收还有缺陷,后面有时间会进行调试修改。

#ifdef __cplusplus             //告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
extern "C"{
#endif

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <poll.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
 
 
//#include "basetype.h"
/*
typedef union epoll_data {  
    void *ptr;  
    int fd;  
    uint32_t u32;  
    uint64_t u64;  
} epoll_data_t;

struct epoll_event {  
    uint32_t events;      // Epoll events  
    epoll_data_t data;      // User data variable  
};
*/

typedef struct tagMSG_HEAD
{
    UCHAR   ucmsgType;
    UCHAR   ucFlag;
    USHORT  usReserve;/* 字节对其 */
    UINT    uiMsgLen;
    UINT    uiPara1;/* 消息交互对方回复的值,用于多次交互中记录状态的情况 */
    UINT    uiPara2;
}MSG_HEAD_S;

#define MSG_BUFF_LEN_1024 1024
#define MSG_BUFF_LEN_2048 2048

 
typedef enum tagEPOLL_MSG_TYPE
{
    EPOLL_PROC_INFD = 0,
    EPOLL_PROC_RECVMSG,
    EPOLL_PROC_TYPE2,
    EPOLL_PROC_MAX
}EPOLL_MSG_TYPE_E;



typedef void (*EPOLL_CALLBACK_PF)(int , unsigned int);

static EPOLL_CALLBACK_PF g_apfEpollCallback[EPOLL_PROC_MAX] = {NULL};

/***************************************************************************
*				|
* Function Name : SORT_RegCallBackFunc
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
void EPOLL_RegCallBackFunc(EPOLL_MSG_TYPE_E type, EPOLL_CALLBACK_PF callBack)
{
    /* we thinks The default type > 0 */
    if (EPOLL_PROC_MAX > type)
    {
        g_apfEpollCallback[type] = callBack;
    }
}


/**************************************************************************
*				|
* Function Name : EPOLL_DeRegCallBackFunc
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
void EPOLL_DeRegCallBackFunc()
{
    int i = 0;

    for(i = 0; i < EPOLL_PROC_MAX; i ++)
    {
        g_apfEpollCallback[i] = NULL;
    }
}


typedef enum tagMSG_TYPE
{
    MSG_PROC_TYPE0 = 0,
    MSG_PROC_TYPE1,
    MSG_PROC_TYPE2,
    MSG_PROC_MAX
}MSG_TYPE_E;


typedef void (*MSG_CALLBACK_PF)(int , MSG_HEAD_S *);

static MSG_CALLBACK_PF g_apfMsgCallback[MSG_PROC_MAX] = {NULL};

/**************************************************************************
*				|
* Function Name : MSG_RegCallBackFunc
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
void MSG_RegCallBackFunc(MSG_TYPE_E type, EPOLL_CALLBACK_PF callBack)
{
    /* we thinks The default type > 0 */
    if (MSG_PROC_MAX > type)
    {
        g_apfMsgCallback[type] = callBack;
    }
}

/**************************************************************************
*				|
* Function Name : MSG_DeRegCallBackFunc
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
void MSG_DeRegCallBackFunc()
{
    int i = 0;

    for(i = 0; i < MSG_PROC_MAX; i ++)
    {
        g_apfMsgCallback[i] = NULL;
    }
}




static int g_Main_Epoll_Fd = -1;
#define MAX_EPOLL_EVENT_COUNT 64
#define MAX_LISTEN_NUM  10
#define MAX_BUFF_LEN 512
 
 
#ifndef BIT_TEST
#define BIT_TEST(a, b)          ( 0 != ((a) & (b)))
#endif
 
#ifndef BIT_SET
#define BIT_SET(a, b)           ((a) |= (b))
#endif
 
#ifndef BIT_RESET
#define BIT_RESET(a, b)         ((a) &= ~(b))
#endif
 
#ifndef BIT_MATCH
#define BIT_MATCH(a,b)          (( (a) & (b) ) == (b))
#endif
 
#ifndef BIT_COMPARE
#define BIT_COMPARE(a,b,c)      (( (a) & (b) ) == (c))
#endif


#define GET_EPOLL_CALLBACK_TYPE(ep_event)   (unsigned int)((ep_event).data.u64 >>32)
#define GET_EPOLL_FD(ep_event)  (int)(unsigned int)((ep_event).data.u64)
 
static void  print_socket_info(struct addrinfo *ai)
{
    char ipstr[INET6_ADDRSTRLEN];
    uint16_t port;
    void *addr  = NULL;
    char *ipver = NULL;
    struct sockaddr_in *ipv4 = NULL;
    struct sockaddr_in6 *ipv6 = NULL;
 
    assert(NULL != ai);
    
    if (ai->ai_family == AF_INET) 
    { // IPv4
        ipv4 = (struct sockaddr_in *)ai->ai_addr;
        addr = &(ipv4->sin_addr);
        ipver = "IPv4";
        port =ntohs(((struct sockaddr_in*)ai->ai_addr)->sin_port);
    } 
    else 
    { // IPv6
        ipv6 = (struct sockaddr_in6 *)ai->ai_addr;
        addr = &(ipv6->sin6_addr);
        ipver = "IPv6";
        port =ntohs(((struct sockaddr_in6*)ai->ai_addr)->sin6_port);
    }
 
    // convert the IP to a string and print it:
    (void)inet_ntop(ai->ai_family, addr, ipstr, sizeof ipstr);
    printf(" server initing... AF: %s IP: %s,PORT: %u\n", ipver, ipstr,port);
 
    return ;
}
 
static int SetSocketNoblocking(const int sockfd)
{
    int flags = 0;
    int iret  = 0;
 
    flags = fcntl (sockfd, F_GETFL, 0);
    if (flags == -1)
    {
        perror ("fcntl");
        return -1;
    }
 
    flags |= O_NONBLOCK;
    iret = fcntl (sockfd, F_SETFL, flags);
    if (iret == -1)
    {
        perror ("fcntl");
        return -1;
    }
 
    return 0;
}
 
 
/**************************************************************************
*				|
* Function Name : Epoll_Control
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
static int Epoll_Control(const int sockfd, const int oper, const int events, unsigned int CallbackType)
{
    struct epoll_event ep_event;
    int iret = 0;
 
    memset(&ep_event, 0, sizeof(ep_event));
    ep_event.events = events;
    /* 先存自定义消息类型,并左移(向高位移)32位,不同消息类型对应不同的回调函数,低位32位存储fd */
    ep_event.data.u64 = (uint64_t)(((uint64_t)(CallbackType) << 32) | (uint64_t)(int)sockfd);
 
    iret = epoll_ctl(g_Main_Epoll_Fd, oper, sockfd, &ep_event);
    if (0 > iret)
    {
        perror("epoll_ctl error.");
        printf("epoll_ctl error opt:%d\n", oper);
        return -1;
    }
 
    return 0;
}
 
 
static int CreateServerScoket(const char *host, const char *port)
{
    struct addrinfo hints;
    struct addrinfo*ailist= NULL; 
    struct addrinfo *ai= NULL;
    int iret;
    int sockfd = 0;
    int sockoptval = 1;
     
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC; // AF_INET 或 AF_INET6 
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;     /* All interfaces */
     
    iret = getaddrinfo(host, port, &hints, &ailist);
    if (0 != iret)
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(iret));
        return -1;
    }
     
    for(ai = ailist;ai != NULL; ai = ai->ai_next) 
    {
 
        sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
        if (0 > sockfd)
        {
            continue;
        }
    
        /* set reuse addr */
        iret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockoptval, sizeof(sockoptval));
        if (0 > iret)
        {
            perror("setsockopt error\n");
            freeaddrinfo(ailist);
            return -1;
        }
    
        iret = bind(sockfd, ai->ai_addr, ai->ai_addrlen);/*sizeof(struct sockaddr)*/
        if (0 == iret)
        {
            print_socket_info(ai);
            break;
        }
        else
        {
            close(sockfd);
        }
    }
 
    if (NULL == ai)
    {
        fprintf(stderr, "get server socket error.\n");
        return -1;
    }
    
    freeaddrinfo(ailist); 
 
    /* set no-block mode */
    iret = SetSocketNoblocking(sockfd);
    if (0 != iret)
    {
        return -1;
    }
 
    /* listen socket */
 
    iret = listen(sockfd, MAX_LISTEN_NUM);
    if (0  > iret)
    {
        perror("listen socket error.\n");
        return -1;
    }
 
    printf("server socket init success. listenning...\n");
 
    return sockfd;
}
 
 
/**************************************************************************
*				|
* Function Name : HandleAccept
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
static void HandleAccept(const int listen_scok, int Event)
{
    
    struct sockaddr_in addr;
    socklen_t  len = sizeof(addr);
    int client_sock  = -1;
 
    if (BIT_TEST(EPOLLIN, Event))
    {
        client_sock = accept(listen_scok, (struct sockaddr *)&addr, &len);
        if(client_sock < 0 )
        {
        	perror("accept infd error");
        	return;
        }
 
        (void)Epoll_Control(client_sock, EPOLL_CTL_ADD, EPOLLIN, EPOLL_PROC_RECVMSG);/* 注册服务器回调函数类型 */
    }
 
    return ;
}
 
/**************************************************************************
*				|
* Function Name : ProcRecvMsg
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
static void ProcRecvMsg(int sockfd, unsigned int Event)
{
    ssize_t recvlen = 0;
    char recvBuf[MSG_BUFF_LEN_1024];
    MSG_HEAD_S *pstMsgHead = NULL;
    unsigned int uiMsgType = MSG_PROC_MAX;
 
    if (BIT_TEST(EPOLLIN, Event))
    {
        //read ev  ready;
        recvlen = recv(sockfd, recvBuf, sizeof(recvBuf), 0);
        if(recvlen < 0)
        {
        	perror("recv error.");
        	return ;
        }
        else if(recvlen == 0)
        {
        	printf("client quit\n");	
            (void)Epoll_Control(sockfd, EPOLL_CTL_DEL, 0,0);
        	close(sockfd);
        }
        else
        {
            /* 可在此处定义回调函数  , 此种情况下,我们只需要在accept出注册关心EPOLLIN事件就可以
            此处的回调函数可以是处理客户端的不同请求,是通用的,定义方式与epoll回调函数注册方式一致。*/
            /* callback_proc[type],msgtype是解析消息传入的消息头携带的
            
            */

            pstMsgHead = (MSG_HEAD_S)(recvBuf);
            /* 这里需要转字节序 */
            uiMsgType = ntohl(pstMsgHead->uiMsgLen);
            /* 这里在内部处理,消息发送,配置等 */
            g_apfMsgCallback[uiMsgType](sockfd, pstMsgHead);  
        }
    }
 
    return ;
}
 
/**************************************************************************
*				|
* Function Name : SendMsg2Client
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
static void SendMsg2Client(const int sockfd,  MSG_HEAD_S *pstMsgHead)
{
    ssize_t sendlen = 0;
    char sendBuf[MAX_BUFF_LEN];
    int event = 0;

    pstMsgHead = pstMsgHead;
    
    sprintf(sendBuf, "HTTP/1.0 200 OK\r\n\r\n<html><h1>Hello Epoll! [client_fd:%d]</h1></html>", sockfd);  
    int sendsize = send(sockfd, sendBuf, strlen(sendBuf)+1, 0);  
    if(sendsize <= 0)  
    {  
        perror("send error.");
        (void)Epoll_Control(sockfd, EPOLL_CTL_DEL, 0);
        close(sockfd);
    }
    /*
    else  
    {  
        printf("Server reply msg ok! data: %s\n", sendBuf);  
        event = EPOLLIN | EPOLLERR | EPOLLHUP;
        (void)Epoll_Control(sockfd, EPOLL_CTL_MOD, event);
    }  */
 
    return ;
}

/**************************************************************************
*				|
* Function Name : GloballInit
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
void GloballInit()
{
    EPOLL_RegCallBackFunc(EPOLL_PROC_INFD, HandleAccept);
    EPOLL_RegCallBackFunc(EPOLL_PROC_RECVMSG, ProcRecvMsg);

    /* 注册epoll回调函数内部处理函数,这里不是向epoll注册 */
    MSG_RegCallBackFunc(MSG_PROC_TYPE0, SendMsg2Client);
}

/**************************************************************************
*				|
* Function Name : GloballFini
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
void GloballFini()
{
    EPOLL_DeRegCallBackFunc();
    EPOLL_DeRegCallBackFunc();

    MSG_DeRegCallBackFunc();
}

 
/**************************************************************************
*				|
* Function Name : main
* Input			: 
* Author		: lily
* Date			: 2018-8-19
* Description	: 
* Return		: 
*				|
**************************************************************************/
int main(int argc, char *argv[])
{
    int listenfd =0;
    int epfd = 0;
    int iret = 0;
    int event = 0;
    struct epoll_event ep_events[MAX_EPOLL_EVENT_COUNT];
    int ev_num = 0;
    int in_sockfd = 0;
    int loop = 0;
    unsigned int CallBackType = EPOLL_PROC_MAX;
    
    if (3 > argc)
    {
        printf("Usage: %s [ip_addr] [port]\n",argv[0]);
        return -1;
    }

    GloballInit();
    
    listenfd = CreateServerScoket(argv[1], argv[2]);
    if (0 > listenfd)
    {
        return -1;
    }
 
    
    epfd = epoll_create(1);
    if (-1 == epfd)
    {
        perror("create epoll error.\n");
        return -1;
    }
    else
    {
        g_Main_Epoll_Fd = epfd;
    }
 
    event = EPOLLIN;
    iret = Epoll_Control(listenfd, EPOLL_CTL_ADD, event, EPOLL_PROC_INFD);
    if (0 != iret)
    {
        return -1;
    }
 
    /* proc epoll event regined */
    printf("server init success, epoll wait now.\n");
    
    for(;;)
    {
        memset(ep_events, 0, sizeof(ep_events));
        ev_num = epoll_wait(g_Main_Epoll_Fd, ep_events, MAX_EPOLL_EVENT_COUNT, -1);
 
        switch (ev_num)
        {
            case 0:
            {
                printf("epoll_wait timeout.\n");
                break;
            }
            case -1:
            {
                perror("epoll_wait error.\n");
                break;
            }
 
            default:
            {
                for (loop = 0; loop < ev_num; loop++)
                {
                    in_sockfd    = GET_EPOLL_FD(ep_events[loop]);
                    CallBackType = GET_EPOLL_CALLBACK_TYPE(ep_events[loop]);

                    /* 这里回调函数仅仅是epoll的回调,至于其他的回调,可以在回调函数内部再回调  */
                    (EPOLL_CALLBACK_PF)g_apfEpollCallback[CallBackType](in_sockfd, CallBackType);
                }
                
                break;
            }
        }
        
    }

    GloballFini();
    return 0;
}



#ifdef __cplusplus
}
#endif

 

以下是使用 epoll 实现的服务器压力测试程序的代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/epoll.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define MAX_EVENTS 1024 #define BUFFER_SIZE 1024 void error_exit(const char *msg) { perror(msg); exit(EXIT_FAILURE); } int create_socket(const char *ip, int port) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { error_exit("Error creating socket"); } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = inet_addr(ip); if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { error_exit("Error connecting to server"); } return sockfd; } int main(int argc, char *argv[]) { if (argc < 4) { printf("Usage: %s <ip> <port> <concurrency>\n", argv[0]); return 1; } const char *ip = argv[1]; const int port = atoi(argv[2]); const int concurrency = atoi(argv[3]); int epoll_fd = epoll_create1(0); if (epoll_fd < 0) { error_exit("Error creating epoll file descriptor"); } struct epoll_event ev, events[MAX_EVENTS]; char buffer[BUFFER_SIZE]; int sockets[concurrency]; int i, n, nfds; for (i = 0; i < concurrency; i++) { int sockfd = create_socket(ip, port); sockets[i] = sockfd; ev.events = EPOLLIN | EPOLLET; // Set edge-triggered mode ev.data.fd = sockfd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev) < 0) { error_exit("Error adding socket to epoll"); } } while (1) { nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds < 0) { error_exit("Error waiting for events"); } for (n = 0; n < nfds; n++) { int sockfd = events[n].data.fd; int len = read(sockfd, buffer, BUFFER_SIZE); if (len < 0) { error_exit("Error reading from socket"); } if (len == 0) { close(sockfd); ev.data.fd = sockfd; if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sockfd, &ev) < 0) { error_exit("Error removing socket from epoll"); } int new_sockfd = create_socket(ip, port); sockets[n] = new_sockfd; ev.events = EPOLLIN | EPOLLET; // Set edge-triggered mode ev.data.fd = new_sockfd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_sockfd, &ev) < 0) { error_exit("Error adding socket to epoll"); } } } } for (i = 0; i < concurrency; i++) { close(sockets[i]); } close(epoll_fd); return 0; } ``` 该程序创建了一个 epoll 实例并向其中添加了一些套接字。它使用边缘触发模式,这意味着当套接字上有数据可读时,程序会立即收到通知。在每个套接字上读取数据时,如果读取失败或读取到的字节数为0,则关闭该套接字并创建一个新套接字,然后将其添加到 epoll 实例中。这样,程序就可以不断地创建和关闭套接字以模拟大量的并发连接。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值