evconnlistener tcp高并发服务

libevent本身已经很好的实现了tcp服务,但是libevent在windows下默认的是select模型。select相对IOCP模型而言并发量和性能都要差很多。所以我们需要使用IOCP模型。

1.启用IOCP模型的tcp服务

1.1 启用windows线程安全模型
evthread_use_windows_threads
1.2启用IOCP模型
event_config *cfg = event_config_new();
event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);

//设置cpu数量,cpu内核数*2+2
event_config_set_num_cpus_hint(cfg,8)
1.3 启用tcp服务
evconnlistener_new_bind
1.4 监听已经连接上的套接字数据
m_pbuf_eve = bufferevent_socket_new(base, fd, options);

//启用读写事件通知,默认只有写
if (m_pbuf_eve)
{
	bufferevent_setcb(m_pbuf_eve, sock_read_data_cb, sock_write_data_cb, sock_event_cb, nullptr);
	bufferevent_enable(m_pbuf_eve, EV_READ | EV_WRITE);
}

完整源码:

//接收连接的sock回调
void accept_cb(struct evconnlistener * listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)
{
	SockMgr::instance().new_sockitem(evconnlistener_get_base(listener), fd, addr, socklen);
}

//错误处理回调
void accept_errorcb(struct evconnlistener *listener, void *arg)
{
	cout << "accept error" << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	CSocketStartup start_up;
	evthread_use_windows_threads();

	//默认使用的是Select模型,启用IOCP
	event_config *cfg = event_config_new();
	event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
	event_base *pbase = event_base_new_with_config(cfg);
	if (pbase == NULL)
	{
		cout << "init error,event base is null" << endl;
		return -1;
	}

	sockaddr_in addr = { 0 };
	addr.sin_family = AF_INET;
	addr.sin_port = htons(SERVER_PORT);
	inet_pton(AF_INET, SERVER_IP, &addr.sin_addr);
	evconnlistener *listener = evconnlistener_new_bind(pbase, accept_cb, NULL, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_THREADSAFE, -1, (struct sockaddr*)&addr, sizeof(addr));
	if (listener == nullptr)
	{
		cout << "listener error ,listener is null" << endl;
		return -1;
	}
	evconnlistener_set_error_cb(listener, accept_errorcb);
	cout << "server listen:" << SERVER_IP << ":" << SERVER_PORT << endl;
	
	event_base_dispatch(pbase);
	return 0;
}

2.管理所有TCP连接

  1. 方便处理所有的链接,以及通知消息
  2. 一个链接一个对象,管理对象更方便
  3. 对于接收到的数据异步处理,处理完成之后再发送数据,不要在接收数据的地方做任何耗时操作
sock_mgr.h
#ifndef __CLIENT_SOCK_H__
#define __CLIENT_SOCK_H__
#include "stdafx.h"
#include 
#include 

class CCritical
{
public:
	CCritical(CRITICAL_SECTION &cs) :m_cs(cs)
	{
		::EnterCriticalSection(&m_cs);
	}
	~CCritical()
	{
		::LeaveCriticalSection(&m_cs);
	}
private:
	CRITICAL_SECTION &m_cs;
};
class SockItem
{
public:
	SockItem(struct event_base *base, evutil_socket_t fd, int options = BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE, struct sockaddr *addr = NULL, int socklen = 0);
	~SockItem();

	operator bufferevent *(){return m_pbuf_eve;}
	std::string ip(){ return m_strip; }
	unsigned short port(){ return m_port; }

	int send(const char *pdata, const int nlen);
private:
	bufferevent *m_pbuf_eve;
	std::string m_strip;
	unsigned short m_port;
};

class SockMgr
{
public:
	static SockMgr& instance();
	std::shared_ptr new_sockitem(event_base *pbase, evutil_socket_t fd, struct sockaddr *addr, int socklen);
	void free_sockitem(bufferevent *m_pbuf_eve);
	void broadcast(const char *pdata, const int nlen);
private:
	SockMgr();
private:
	std::map > m_map_sockitem;//标记每个链接
	CRITICAL_SECTION m_cs;
	static SockMgr* s_pthis;
};


#endif


sock_mgr.cpp
#include "stdafx.h"
#include "event2/buffer.h"
#include "sock_mgr.h"
#include 

static void sock_read_data_cb(struct bufferevent *bev, void *ctx)
{
	int nread_size = 0;
	char szData[4096] = "";
	std::string strData;
	do
	{
		nread_size = bufferevent_read(bev, szData, 4096);
		strData = szData;
	} while (nread_size == 4096);

	cout << "sock:" << bufferevent_getfd(bev) << ",read data:" << strData< 0)
	{
		struct sockaddr_in *pin = (struct sockaddr_in *)addr;
		m_strip = inet_ntoa(pin->sin_addr);
		m_port = pin->sin_port;
	}
}

SockItem::~SockItem()
{
	if (m_pbuf_eve)
		bufferevent_free(m_pbuf_eve);
	m_pbuf_eve = nullptr;
}

int SockItem::send(const char *pdata, const int nlen)
{
	if (m_pbuf_eve)
		return bufferevent_write(m_pbuf_eve,pdata,nlen);
	return -1;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SockMgr * SockMgr::s_pthis = NULL;

SockMgr::SockMgr()
{
	::InitializeCriticalSection(&m_cs);
}

SockMgr& SockMgr::instance()
{
	if (s_pthis == NULL)
		s_pthis = new SockMgr();
	return *s_pthis;
}

std::shared_ptr SockMgr::new_sockitem(event_base *pbase, evutil_socket_t fd, struct sockaddr *addr, int socklen)
{
	std::shared_ptr pItem(new SockItem(pbase, fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE, addr, socklen));
	bufferevent *pbuf_eve = *pItem;
	if (pbuf_eve == NULL)
	{
		cout << "new connection bufferevent failed" << endl;
		return NULL;
	}

	cout << "sock:" << bufferevent_getfd(*pItem) << ",ip:" << pItem->ip() << ":" << pItem->port() << " connected" << endl;

	{
		CCritical lk(m_cs);
		m_map_sockitem[pbuf_eve] = pItem;
	}
	
	return pItem;
}

void SockMgr::free_sockitem(bufferevent *pbuf_eve)
{
	std::shared_ptr pitem = nullptr;
	
	{
		CCritical lk(m_cs);
		if (m_map_sockitem.count(pbuf_eve))
			pitem = m_map_sockitem[pbuf_eve];
		m_map_sockitem.erase(pbuf_eve);
	}

	cout << "sock:" << bufferevent_getfd(*pitem) << ",ip:" << pitem->ip() << ":" << pitem->port() << " disconnected" << endl;
}

void SockMgr::broadcast(const char *pdata, const int nlen)
{
	std::map> temp_map_sockitem;
	{
		CCritical lk(m_cs);
		temp_map_sockitem = m_map_sockitem;
	}

	for (std::map>::iterator it = temp_map_sockitem.begin();
		it != temp_map_sockitem.end();++it)
	{
		if (it->second.get())
		{
			it->second->send(pdata, nlen);
		}
	}
}
后面只需要进行数据包处理(注意粘包),异步处理完成之后通过mgr将应答结果发送出去就OK。

你可能感兴趣的:(libevent)