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