libevent VS下的编译
参考:https://blog.csdn.net/swartz_lubel/article/details/55809970
直接进入代码目录,输入nmake /f Makefile.nmake
报如下错误:
需要在 #include <stdint.h> 然后编译成功,生成libevent_core.lib libevent_extras.lib libevent.lib
导入如下库
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "libevent.lib")
#pragma comment(lib, "libevent_core.lib")
#pragma comment(lib, "libevent_extras.lib")
如果不导入Iphlpapi.lib,会报 error LNK2019: 无法解析的外部符号 _if_nametoindex@4,该符号在函数 _evutil_inet_pton_scope 中被引用
#include <iostream>
#include <string.h>
#include "event2/event.h"
#include "event2/bufferevent.h"
#include "event2/listener.h"
#ifdef _WIN32
#include <windows.h>
#include <time.h>
#include <ws2tcpip.h>
#else
#include <unistd.h>
#include <arpa/inet.h>
#endif
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wsock32.lib")
#pragma comment(lib, "libevent.lib")
#pragma comment(lib, "libevent_core.lib")
#pragma comment(lib, "libevent_extras.lib")
using namespace std;
void read_cb(struct bufferevent *bev, void *arg)
{
char buf[1024] = { 0 };
char* ip = (char*)arg;
bufferevent_read(bev, buf, sizeof(buf));
cout << "client " << ip << " say:" << buf << endl;
//写数据给客户端
const char *p = "Hello";
bufferevent_write(bev, p, strlen(p) + 1);
}
void write_cb(struct bufferevent *bev, void *arg)
{
cout << "I'm 服务器,成功写数据给客户端,写缓冲回调函数被调用..." << endl;
}
void event_cb(struct bufferevent *bev, short events, void *arg)
{
char* ip = (char*)arg;
if (events & BEV_EVENT_EOF)
{
cout << "connection closed:" << ip << endl;
}
else if (events & BEV_EVENT_ERROR)
{
cout << "some other error !" << endl;
}
bufferevent_free(bev);
cout << "bufferevent 资源已经被释放..." << endl;
}
void cb_listener(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int len, void *ptr)
{
struct sockaddr_in* client = (sockaddr_in*)addr;
cout << "connect new client: " << inet_ntoa(client->sin_addr) << "::" << ntohs(client->sin_port) << endl;
struct event_base *base = (struct event_base*)ptr;
//添加新事件
struct bufferevent *bev;
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
//给bufferevent缓冲区设置回调
bufferevent_setcb(bev, read_cb, write_cb, event_cb, inet_ntoa(client->sin_addr));
//启动 bufferevent的 读缓冲区。默认是disable 的
bufferevent_enable(bev, EV_READ);
}
int main()
{
#ifdef WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
(void)WSAStartup(wVersionRequested, &wsaData);
#endif
//init server
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
serv.sin_addr.s_addr = htonl(INADDR_ANY);
//创建 event_base
struct event_base * base;
base = event_base_new();
//创建套接字
//绑定
//接收连接请求
struct evconnlistener* listener;
listener = evconnlistener_new_bind(base, cb_listener, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 36, (struct sockaddr*)&serv, sizeof(serv));
//启动循环监听
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
多线程的使用:
参考:https://www.cnblogs.com/zzyoucan/p/3970578.html
#ifndef LIBEVENT_SERVER_H
#define LIBEVENT_SERVER_H
#include "event2/event.h"
#include "event2/bufferevent.h"
#include "event2/listener.h"
#include <iostream>
#include <string.h>
#include <memory>
#include <vector>
#include "event.h"
#include <thread>
#include <mutex>
#include <queue>
#define THREAD_NUM 10
struct conn_queue_item
{
int fd;
};
class LibEvtServer;
//保存线程的结构体
struct LibeventThread
{
LibEvtServer* that; //用作传参
std::shared_ptr<std::thread> spThread; // 线程
struct event_base * thread_base; // 事件根基
struct event notify_event;
evutil_socket_t notfiy_recv_fd; // socketpair 接收端fd(工作线程接收通知)
evutil_socket_t notfiy_send_fd; // socketpair 发送端fd(监听线程发送通知)
std::mutex conn_mtx; //维护连接队列的锁
std::queue<conn_queue_item> conn_queue; //conn_queue 是一个管理conn_queue_item的队列
};
class LibEvtServer
{
public:
LibEvtServer();
virtual ~LibEvtServer();
void Init();
void Listen();
public:
event_base * m_base;
int m_last_thread;
std::vector<LibeventThread*> m_libevent_threads;
evconnlistener * m_listener;
/*main分发线程*/
std::shared_ptr<std::thread> m_spThread;
};
#endif
CPP文件
#include "LibEvtServer.h"
#include "event2/thread.h"
#include <windows.h>
#include <time.h>
#include <ws2tcpip.h>
#include <queue>
#include <mutex>
#include <thread>
LibEvtServer::LibEvtServer() :m_last_thread(-1)
{
}
LibEvtServer::~LibEvtServer()
{
evconnlistener_free(m_listener);
event_base_free(m_base);
}
void read_cb(struct bufferevent *bev, void *arg)
{
char buf[1024] = { 0 };
LibeventThread* plt = (LibeventThread*)arg;
bufferevent_read(bev, buf, sizeof(buf));
//写数据给客户端
const char *p = "i am server, i received your msg!";
bufferevent_write(bev, p, strlen(p) + 1);
}
void write_cb(struct bufferevent *bev, void *arg)
{
LibeventThread* plt = (LibeventThread*)arg;
std::cout << "I'm 服务器,成功写数据给客户端,写缓冲回调函数被调用...";
}
void event_cb(struct bufferevent *bev, short events, void *arg)
{
LibeventThread* plt = (LibeventThread*)arg;
if (events & BEV_EVENT_EOF)
{
std::cout << "connection closed:";
}
else if (events & BEV_EVENT_ERROR)
{
std::cout << "some other error !";
}
bufferevent_free(bev);
std::cout << "bufferevent 资源已经被释放...";
}
void notify_cb2(intptr_t fd, short which, void *pLibeventThread)
{
//首先将socketpair的1个字节通知信号读出(这是必须的,在水平触发模式下如果不处理该事件,则会循环通知,直到事件被处理)
char buf[1];
recv(fd, buf, 1, 0);//从sockpair的另一端读数据
LibeventThread* plt = (LibeventThread*)pLibeventThread;
conn_queue_item item;
//从自己的连接队列中取出连接数
{
//取出队列中的第一个元素
std::lock_guard<std::mutex> lck(plt->conn_mtx);
item = plt->conn_queue.front();
}
//创建每个socket的bufferevent
auto bev = bufferevent_socket_new(plt->thread_base, item.fd, BEV_OPT_THREADSAFE);
//设置接收、状态改变 回调函数
//给bufferevent缓冲区设置回调
bufferevent_setcb(bev, read_cb, NULL, event_cb, plt);
bufferevent_enable(bev, EV_READ | EV_WRITE);
}
void listener_cb2(evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
LibEvtServer *pLibThis = (LibEvtServer*)user_data;
if (!pLibThis)
{
return;
}
int cur_thread = (pLibThis->m_last_thread + 1) % THREAD_NUM; // 轮循选择工作线程
pLibThis->m_last_thread = cur_thread;
conn_queue_item item;
item.fd = fd;
//item.ch2 = NULL;
auto plt = pLibThis->m_libevent_threads[cur_thread];
{
//向线程的队列中放入一个item,每个线程有个队列,保存连接的socketfd
std::lock_guard<std::mutex> lock(plt->conn_mtx);
plt->conn_queue.push(item);
}
//激活读线程的读事件
send(plt->notfiy_send_fd, "c", 1, 0);
}
void LibEvtServer::Listen()
{
#ifdef WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
(void)WSAStartup(wVersionRequested, &wsaData);
#endif
//init server
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
serv.sin_addr.s_addr = htonl(INADDR_ANY);
m_listener = evconnlistener_new_bind(m_base, listener_cb2, (void*)this,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&serv,
sizeof(serv));
if (!m_listener)
{
return;
}
m_spThread.reset(new std::thread([]
(void* arg)
{
//启动循环监听
event_base *base = (event_base*)arg;
event_base_dispatch(base);
}, m_base));
}
void LibEvtServer::Init()
{
//event支持windows下线程的函数
int hr = evthread_use_windows_threads();
//创建 event_base
m_base = event_base_new();
if (!m_base) {
std::cout<<"Could not initialize libevent!\n";
return;
}
for (int i = 0; i < THREAD_NUM; i++)
{
LibeventThread *ptLib = new LibeventThread;
//创建一个socketpair即可与互相通信的两个socket,保存在fds里面
evutil_socket_t fds[2];
if (evutil_socketpair(AF_INET, SOCK_STREAM, 0, fds) < 0)
{
std::cout << "创建socketpair失败\n";
return;
}
//设置成无阻赛的socket
evutil_make_socket_nonblocking(fds[0]);
evutil_make_socket_nonblocking(fds[1]);
ptLib->notfiy_recv_fd = fds[0];
ptLib->notfiy_send_fd = fds[1];
ptLib->thread_base = event_base_new();
//给每个libevent线程设置连接通知回调函数。
ptLib->that = this;
//设置线程事件notify_event
event_set(&ptLib->notify_event, ptLib->notfiy_recv_fd,//EV_READ表示只要这个socket可读就调用notify_cb函数
EV_READ | EV_PERSIST, notify_cb2, ptLib);
//设置事件和event_base的关系
event_base_set(ptLib->thread_base, &ptLib->notify_event); // 设置事件的从属关系(相当于指明事件属于哪个event_base)
//添加事件
event_add(&ptLib->notify_event, 0); // 正式添加事件
m_libevent_threads.push_back(ptLib);
}
//开始创建并启动线程
for (int i = 0; i < THREAD_NUM; ++i)
{
m_libevent_threads[i]->spThread.reset(new std::thread([]
(void* arg)
{
LibeventThread* me = (LibeventThread*)arg;
event_base_loop(me->thread_base, 0);
}, m_libevent_threads[i]));
}
}
调用如下:
#include <iostream>
#include "LibEvtServer.h"
int main()
{
LibEvtServer server;
server.Init();
server.Listen();
return 0;
}