libevent实现UDP通信

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

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

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

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

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

#include 
#include 
#include 
#include 

#ifdef _WIN32
#include 
#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 
#include 
# ifdef _XOPEN_SOURCE_EXTENDED
#  include 
# endif
#include 
#define GetCurrentThreadId() pthread_self()
#endif

#include 
#include 
#include 
#include 
#include 
#include 

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

你可能感兴趣的:(libevent,c++)