一个线程一个event_base,代码仅做练习使用,没有考虑销毁。
仅支持vs2012及以上。
#pragma once
#include <functional>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
#include "event2/event_struct.h" // for: struct event
struct event_base;
struct bufferevent;
class EventLoopThread
{
public:
typedef std::function<void()> Functor;
EventLoopThread();
~EventLoopThread();
struct event_base *startLoop();
void runInLoop(const Functor &cb);
std::thread::id getThreadID() const { return threadID_; }
private:
void wakeup();
static void WakeupCallback(evutil_socket_t, short, void *arg);
void threadFunc();
struct event_base *base_;
bool exiting_;
std::thread thread_;
std::mutex mutex_;
std::condition_variable cond_;
std::vector<Functor> functors_;
evutil_socket_t wakeFd_[2];
evutil_socket_t connFd_;
struct event ev_;
std::thread::id threadID_;
};
class EventLoopThreadPool
{
public:
EventLoopThreadPool();
~EventLoopThreadPool();
void setThreadNum(int num);
void start();
EventLoopThread *getNextLoop();
std::thread::id currentThreadID() const;
private:
std::vector<EventLoopThread*> loops_;
int num_;
int next_;
};
class Channel
{
public:
typedef std::function<void(struct bufferevent *bev)> ReadCallback;
typedef ReadCallback WriteCallback;
typedef std::function<void(struct bufferevent *bev, short what)> EventCallback;
Channel(struct event_base *base, evutil_socket_t fd);
~Channel();
void start();
private:
Channel(const Channel&);
Channel& operator=(const Channel&);
static void readCallback(struct bufferevent *bev, void *data);
static void writeCallback(struct bufferevent *bev, void *data);
static void eventCallback(struct bufferevent *bev, short what, void *data);
private:
ReadCallback readCallback_;
WriteCallback writeCallback_;
EventCallback eventCallback_;
struct bufferevent *bufferEvent_;
};
class TcpServer
{
public:
TcpServer(unsigned short port);
~TcpServer();
void start();
private:
TcpServer(const TcpServer&);
TcpServer& operator=(const TcpServer&);
static void listenCallback(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg);
private:
unsigned short port_;
struct evconnlistener *listener_;
EventLoopThreadPool eventPool_;
};
#include "stdafx.h"
#include <iostream>
#include "EventLoopThread.h"
#include "event2/event.h"
#include "event2/event_struct.h"
#include "event2/util.h"
#include "event2/buffer.h"
#include "event2/listener.h"
#include "event2/bufferevent.h"
EventLoopThread::EventLoopThread()
: base_(NULL)
, exiting_(false)
, thread_(std::bind(&EventLoopThread::threadFunc, this))
, threadID_(thread_.get_id())
{
}
EventLoopThread::~EventLoopThread()
{
exiting_ = true;
event_base_loopexit(base_, NULL);
thread_.join();
}
struct event_base *EventLoopThread::startLoop()
{
{
std::unique_lock<std::mutex> ul(mutex_);
while (NULL == base_) {
cond_.wait(ul);
}
}
return base_;
}
void EventLoopThread::runInLoop(const Functor &cb)
{
{
std::lock_guard<std::mutex> lock(mutex_);
functors_.push_back(cb);
}
wakeup();
}
void EventLoopThread::wakeup()
{
char c = 'w';
::send(wakeFd_[0], &c, 1, 0);
}
void EventLoopThread::WakeupCallback(evutil_socket_t fd, short, void *arg)
{
EventLoopThread *p = (EventLoopThread*)arg;
if (p) {
char buf[16] = {0};
::recv(p->wakeFd_[1], buf, sizeof(buf), 0);
std::vector<Functor> functors;
{
std::lock_guard<std::mutex> lock(p->mutex_);
functors.swap(p->functors_);
}
for (auto& f : functors) {
f();
}
}
}
void EventLoopThread::threadFunc()
{
struct event_base *base = event_base_new();
int iret = evutil_socketpair(AF_INET, SOCK_STREAM, 0, wakeFd_);
std::cout << "socket pair ret:" << iret << std::endl;
iret = event_assign(&ev_, base, wakeFd_[1], EV_READ | EV_PERSIST, WakeupCallback, this);
if (0 != iret) {
std::cout << "event assign error" << iret << std::endl;
}
iret = event_add(&ev_, NULL);
if (0 != iret) {
std::cout << "event add error" << iret << std::endl;
}
{
std::unique_lock<std::mutex> ul(mutex_);
base_ = base;
cond_.notify_one();
}
event_base_dispatch(base);
printf("event loop exiting ...\n");
}
///
EventLoopThreadPool::EventLoopThreadPool()
: num_(1) , next_(0)
{
}
EventLoopThreadPool::~EventLoopThreadPool()
{
for (auto loop : loops_) {
delete loop;
}
}
void EventLoopThreadPool::setThreadNum(int num)
{
num_ = num;
}
void EventLoopThreadPool::start()
{
for (int i=0; i<num_; i++) {
EventLoopThread *loop = new EventLoopThread();
loops_.push_back(loop);
}
}
EventLoopThread* EventLoopThreadPool::getNextLoop()
{
EventLoopThread *pCurrent = loops_[next_++];
next_ %= num_;
return pCurrent;
}
std::thread::id EventLoopThreadPool::currentThreadID() const
{
if (next_ - 1 >= 0) {
return loops_[next_ - 1]->getThreadID();
} else {
return std::thread::id();
}
}
Channel::Channel(struct event_base *base, evutil_socket_t fd)
: bufferEvent_(bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE))
{
}
Channel::~Channel()
{
// FIXME
}
void Channel::start()
{
bufferevent_setcb(bufferEvent_, readCallback, writeCallback, eventCallback, this);
int iret = bufferevent_enable(bufferEvent_, EV_READ | EV_WRITE);
if (0 != iret) {
std::cout << "bufferevent enable error" << std::endl;
}
}
void Channel::readCallback(struct bufferevent *bev, void *data)
{
fprintf(stdout, "ReadCallback..., threadID:%d\n", ::GetCurrentThreadId());
struct evbuffer *input = bufferevent_get_input(bev);
size_t len = evbuffer_get_length(input);
evbuffer_drain(input, len);
fprintf(stdout, "drain, len:%d\n", len);
//struct evbuffer *output = bufferevent_get_output(bev);
//size_t len = evbuffer_get_length(input);
//evbuffer_remove_buffer(input, output, len);
}
void Channel::writeCallback(struct bufferevent *bev, void *data)
{
fprintf(stdout, "WriteCallback..., threadID:%d\n", ::GetCurrentThreadId());
struct evbuffer *output = bufferevent_get_output(bev);
}
void Channel::eventCallback(struct bufferevent *bev, short what, void *data)
{
if (!data) {
return;
}
Channel *p = (Channel*)data;
fprintf(stdout, "EventCallback...\n");
if (what & BEV_EVENT_READING) {
fprintf(stdout, "BEV_EVENT_READING...\n");
}
if (what & BEV_EVENT_WRITING) {
fprintf(stdout, "BEV_EVENT_WRITING...\n");
}
if (what & BEV_EVENT_EOF) {
fprintf(stdout, "BEV_EVENT_EOF...\n");
}
if (what & BEV_EVENT_ERROR) {
fprintf(stdout, "BEV_EVENT_ERROR...\n");
bufferevent_free(bev);
}
if (what & BEV_EVENT_TIMEOUT) {
fprintf(stdout, "BEV_EVENT_TIMEOUT...\n");
}
if (what & BEV_EVENT_CONNECTED) {
fprintf(stdout, "BEV_EVENT_CONNECTED...\n");
}
}
TcpServer::TcpServer(unsigned short port)
: port_(port)
{
}
TcpServer::~TcpServer()
{
}
void TcpServer::start()
{
eventPool_.setThreadNum(3);
eventPool_.start();
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(0);
sin.sin_port = htons(port_);
struct event_base *base = event_base_new();
evconnlistener_new_bind(base, listenCallback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
-1, (sockaddr*)&sin, sizeof(sin));
event_base_dispatch(base);
}
void TcpServer::listenCallback(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)
{
if (!arg) {
return;
}
fprintf(stdout, "new connection is comming...\n");
TcpServer *p = (TcpServer*)arg;
EventLoopThread *loop = p->eventPool_.getNextLoop();
Channel *channel = new Channel(loop->startLoop(), fd);
loop->runInLoop(std::bind(&Channel::start, channel));
fprintf(stdout, "currentThreadID:%d\n", loop->getThreadID());
}
#include "stdafx.h"
#include <iostream>
#include "EventLoopThread.h"
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
{
TcpServer tcpServer(5100);
tcpServer.start();
}
system("pause");
WSACleanup();
return 0;
}