libevent实现UDP通信

该博客介绍了如何在Windows上利用libevent库实现UDP通信,尽管libevent本身不直接支持UDP和IOCP,但可以通过select模型进行适配。文中提供了一个示例,展示了如何创建并绑定端口,接收消息时触发回调函数。代码示例中,当接收到消息时,会回调udpread_cb函数,同时支持在多个线程中进行操作。
摘要由CSDN通过智能技术生成

因为libevent在底层上是没有直接支持udp通信,并且在Windows上,对于udp的iocp操作,libevent也不支持(实际Windows本身是支持IOCP实现UDP通信的)

不过仍然可以利用libevent实现udp通信,调试源码发现,libevent是通过select模型去实现的。

下面的例子实现了先绑定某个端口,当收到消息时,udpread_cb发生回调。

如果不需要绑定端口,那么就需要先sendto后,才能够收到消息,udpread_cb才会发生回调。

同样的,如果需要在多个线程中进行操作,那么可以创建多个event_base

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>

#ifdef _WIN32
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "event.lib")
#pragma comment(lib, "event_extra.lib")
#pragma comment(lib, "event_core.lib")
#else
#include <netinet/in.h>
#include <pthread.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#include <sys/socket.h>
#define GetCurrentThreadId() pthread_self()
#endif

#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include <event2/thread.h>

static const char MESSAGE[] = "Hello, World!\n";

static const int PORT = 9638;

static struct event_base * createEventBase();
static evutil_socket_t createUdp(unsigned short port);
static void udpread_cb(evutil_socket_t, short, void *);

typedef struct tagUdpCtx {
    evutil_socket_t fd;
    struct event* ev;
}UdpCtx;

int mainUdp(int argc, char **argv)
{
    struct event_base *base;

#ifdef WIN32
    WSADATA wsa_data;
    WSAStartup(0x0202, &wsa_data);
#endif

    base = createEventBase();

    if (!base)
    {
        fprintf(stderr, "Could not initialize libevent!\n");
        return 1;
    }

    int iCnt;
    scanf("%d", &iCnt);
    UdpCtx* pCtx = new UdpCtx[iCnt];
    for (int i = 0; i < iCnt; ++i)
    {
        memset(&(pCtx[i]), 0, sizeof(pCtx[i]));
        pCtx[i].fd = createUdp(64000 + i);
        pCtx[i].ev = event_new(base, pCtx[i].fd, EV_READ | EV_PERSIST, udpread_cb, NULL);
        event_add(pCtx[i].ev, NULL);
    }

    printf("main GetCurrentThreadId()=%d\n", GetCurrentThreadId());
    event_base_dispatch(base); //这里面进入循环了

    for (int i = 0; i < iCnt; ++i)
    {
        evutil_closesocket(pCtx[i].fd);
        if (pCtx[i].ev)event_free(pCtx[i].ev);
    }
    delete[] pCtx;
    event_base_free(base);

#ifdef WIN32
    WSACleanup();
#endif
    printf("done\n");
    return 0;
}

static struct event_base * createEventBase()
{
    struct event_base *base = NULL;

    base = event_base_new();

    return base;
}

evutil_socket_t createUdp(unsigned short port)
{
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    sin.sin_addr.s_addr = INADDR_ANY;
    //evutil_inet_pton(sin.sin_family, "192.168.1.100", (void*)&sin.sin_addr);

    evutil_socket_t fd = socket(AF_INET, SOCK_DGRAM, 0);
    
    if(::bind(fd, (struct sockaddr *) &sin, sizeof(sin))!=0)
    {
        printf("bind error:%s,%d\n", evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()), EVUTIL_SOCKET_ERROR());
        evutil_closesocket(fd);
        return EVUTIL_INVALID_SOCKET;
    }

    return fd;
}

static void udpread_cb(evutil_socket_t fd, short ev, void *)
{
    printf("udpread_cb GetCurrentThreadId()=%d\n", GetCurrentThreadId());
    //evutil_closesocket(fd);
    
    char buff[1024] = "";
    struct sockaddr_in addr;
    int iLen = sizeof(addr);
    memset(&addr, 0, iLen);

    int iRet = recvfrom(fd, buff, 1023, 0, (struct sockaddr*)&addr, &iLen);
    if (iRet >= 0)
    {
        char szIp[100] = "";
        unsigned short iPort = ntohs(addr.sin_port);
        evutil_inet_ntop(addr.sin_family, (void*)&addr.sin_addr, szIp, 100);
        printf("recv(%s:%hu):%s\n",szIp, iPort, buff);

        const char* psz = "hello";
        sendto(fd, psz, strlen(psz), 0, (struct sockaddr*)&addr, iLen);
    }
    else
    {
        printf("udpread_cb error:%s,%d\n", evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()), EVUTIL_SOCKET_ERROR());
        evutil_closesocket(fd);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值