#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 void conn_writecb(struct bufferevent *, void *);
static void conn_readcb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void timer_callback(evutil_socket_t, short, void *);
static void connect_server(struct event_base* base);
int mainC(int argc, char **argv)
{
struct event_base *base;
struct event* timer = NULL;
#ifdef WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif
base = createEventBase();
if (!base)
{
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}
connect_server(base);
//构建并添加一个定时器,它的另一个作用就是让这个iocp模型一直保持着运行直到调用了event_base_loopexit
//通过调试发现,当客户端因为某种原因断开了,那么这个iocp客户端模型会自动结束的,因为base中并没有事件,所以会退出。
//为了让这个iocp一直正常的运行下去,我们可以添加一个永久定时器。
timer = event_new(base, -1, EV_TIMEOUT | EV_PERSIST, timer_callback, NULL);
{
struct timeval timeout = {1,0};
event_add(timer, &timeout);
//event_add(timer, NULL);
}
event_base_dispatch(base); //这里面进入循环了
event_free(timer);
event_base_free(base);
#ifdef WIN32
WSACleanup();
#endif
printf("done\n");
return 0;
}
/**************************************************************************************************************************
libevent的一些问题记录。
1. 读写时,可能存在最大包字节限制,因为默认为16384字节,下面这2个宏是内部定义的。
#define MAX_SINGLE_READ_DEFAULT 16384
#define MAX_SINGLE_WRITE_DEFAULT 16384
可以通过bufferevent_set_max_single_read(...),bufferevent_set_max_single_write(...)去设置最大值。
*/
static struct event_base * createEventBase()
{
struct event_base *base = NULL;
#if 1
base = event_base_new();
#else
struct event_config *cfg = event_config_new();
#ifdef WIN32
//告诉libEvent使用Windows自己的线程和同步锁,并启用多线程安全
evthread_use_windows_threads();
//Windows启用IOCP模式
event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
//根据CPU实际数量配置libEvent的CPU数
SYSTEM_INFO si;
GetSystemInfo(&si);
event_config_set_num_cpus_hint(cfg, 2);//si.dwNumberOfProcessors
#else
//告诉libEvent使用平台自己的线程和同步锁,并启用多线程安全
evthread_use_pthreads();
#endif
base = event_base_new_with_config(cfg);
event_config_free(cfg);
cfg = NULL;
#endif
return base;
}
static void timer_callback(evutil_socket_t fd, short event, void *user_data)
{
struct timeval tv;
evutil_gettimeofday(&tv, nullptr);
printf("it's timeout....%d,%d\n", tv.tv_sec, tv.tv_usec);
}
/*
如果要多个客户端,只需要多次调用这个,也可以创建多个event_base
*/
static void connect_server(struct event_base* base)
{
//struct sockaddr_in6 sin6; ipv6
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
evutil_inet_pton(sin.sin_family, "127.0.0.1", (void *)&sin.sin_addr);
/*如果想要读写和释放在别的线程中也能够安全操作,则需要,添加 BEV_OPT_THREADSAFE,同时需要在最开始的时候调用
evthread_use_windows_threads()、evthread_use_pthreads()告诉平台启用多线程*/
struct bufferevent *bev;
#if 1
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
#else
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE);
#endif
if (!bev)
{
return;
}
//设置读写和客户端连接状态的回调函数
bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, 0);
//设置读写回调是否可用
bufferevent_enable(bev, EV_WRITE | EV_READ);
if (0 != bufferevent_socket_connect(bev, (struct sockaddr *)&sin, sizeof(sin)))
{
fprintf(stderr, "Could not bufferevent_socket_connect!\n");
}
}
//向客户端写完数据后发生的一个回调,通常是调用bufferevent_write后发生了回调。
static void conn_writecb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *output = bufferevent_get_output(bev);
if (evbuffer_get_length(output) == 0)
{
printf("flushed answer\n");
}
}
//当收到客户端发送过来的数据时,该函数发生了回调
static void conn_readcb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *input = bufferevent_get_input(bev);
size_t len = evbuffer_get_length(input);
if (!len)
{
puts("接收到的数据个数是0");
return;
}
char data[1025] = "";
size_t size = 0;
//从缓冲区中读取接收到的数据
while (0 != (size = bufferevent_read(bev, data, 1024)))
{
printf("data=%s, len=%d\n", data, size);
}
const char *wData = "send to client!";
bufferevent_write(bev, wData, strlen(wData) + 1);
//主动断开与客户端的连接
//bufferevent_free(bev);
printf("a new conn_readcb %p\n", bev);
}
static void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
printf("tcb-bev=%p\n", bev);
if (events & BEV_EVENT_EOF)
{
printf("Connection closed.\n");
}
else if (events & BEV_EVENT_ERROR)
{
printf("Got an error on the connection: %s\n", evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
}
else if (events & BEV_EVENT_TIMEOUT)
{
printf("timeout\n");
}
else if (events & BEV_EVENT_CONNECTED)
{
//connected success
printf("connected socket=%d\n", bufferevent_getfd(bev));
return;
}
// None of the other events can happen here, since we haven't enabled timeouts
bufferevent_free(bev);
}
libevent实现客户端
最新推荐文章于 2024-05-20 14:50:38 发布