项目中的libevent
单线程libevent模式
项目里面是多线程版的,我先理解下单线程的。
//client
.调用NGP::init()
bool NGP::init(NGPcontext context)
{
_context = context;
//_TcpLink = NEWSP(TcpLink);
_TcpLink = NEWSP(TcpLinkEx);
_TcpLink->Init(this);
return true;
} .初始化Libevent
bool LibEvtServer::init(I_NetServerEvent* event, int start, int size)
{
m_ids = new ChannelIDGenerator();
m_ids->init(start, size);
m_allChannels.resize(m_ids->getSize()); m_event = event; //使用windows模式的线程和锁
int hr = evthread_use_windows_threads();//当前是两个线程一个主线程一个派发线程 //一个线程只有一个event_base,对应的是一个struct event_base结构体和相应的事件管理器,这个事件管理器管理跟这个event_base绑定的事件
m_base = event_base_new();//创建event_base
if (!m_base) {
fprintf(stderr, "Could not initialize libevent!\n");
return false;
} return true;
}
.监听,搞不懂客户端为什么要监听
bool LibEvtServer::listen(int* port)
{
struct sockaddr_in sin; memset(&sin, , sizeof(sin));
sin.sin_family = AF_INET;
if(- == *port)
sin.sin_port = htons();
else
sin.sin_port = htons(*port); //如果libevent分配和绑定套接字就调用evconnlistener_new_bind,这个相当于原始的socket()和bind()和listen(),然后有连接会调用listener_cb
//其中调用了evutil_make_socket_nonblocking,将socket设置成无阻塞的,bind绑定,evconnlistener_new创建监听器,然后返回监听器
//连接监听器使用event_base来得知什么时候在监听套接字上有TCP连接,当有连接时会调用回调函数
m_listener = evconnlistener_new_bind(m_base, ::listener_cb, (void*)this,//evconnlistener_new_bind提供了监听和接受TCP的一个方法,这个是要libevent分配和绑定套接字
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -,
(struct sockaddr*)&sin,
sizeof(sin));
if (!m_listener)
{
return false;
} if( - == *port)
*port = ntohs(sin.sin_port); if (!m_listener) {
fprintf(stderr, "Could not create a listener!\n");
return false;
}
m_spThread.reset(new std::thread([this]
{
//SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
//event_base_loop(m_base, EVLOOP_ONCE);
//循环在epoll/kqueue系统调用上,当事件发生时自动调用绑定的事件,但这些事件需要绑定到这个event_base上面
event_base_dispatch(m_base);//启动event_base的循环,event_base_dispatch内部就是一个while循环,项目直接开了一个线程取做这个
if(WSAENOTSOCK == WSAGetLastError())
{
Plug::PlugMessageBox(L"操作无效套接字啊!");
} Plug::PlugMessageBox(L"Libevent派发线程退出!");
}));
return true;
} .客户端连接
bool LibEvtServer::connet_to(int ip, int port)
{
//创建基于socket的bufferevent,并将其托管给event_base
//buffevent从单词可以看出是在event上的buffer,即在read/write两个事件上有input和output缓冲区
//是文件描述符,决定哪个文件被写入写出,可以将socket看成文件描述符,但不允许设置成pipe(2),为安全起见可以设置成-1,然后 set it with bufferevent_setfd or bufferevent_socket_connect().
auto bev = bufferevent_socket_new(m_base, -, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE); //创建一个bufferevent,关联sockfd,托管给event_base
if (!bev){
std::cout << "bufferevent_socket_new error!" << std::endl;
return false;
} struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ntohl(ip);
sin.sin_port = htons(port); //连接
//If the bufferevent does not already have a socket set, we allocate a new socket here and make it nonblocking before we begin.
int hr = bufferevent_socket_connect(bev, (struct sockaddr*)&sin, sizeof(sin));
if( != hr)
return false; std::lock_guard<std::mutex> lock(m_offline_mtx);//跟申请释放channel id互锁
auto c2 = CreateChannel(bev); //设置bufferevent上的回调
bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, c2);//设置读写事件
//启动bufferevent
bufferevent_enable(bev, EV_READ | EV_WRITE);//启动读写事件,将读写事件放入监听队列poll中
return true;
}
//-------------------------------------到此client方面就做好了libevent方面的准备------------------------- //server
/* 成员函数 */
bool LibEvtServer::init(I_NetServerEvent* event, int start, int size)
{
m_ids = new ChannelIDGenerator();
m_ids->init(start, size);
m_allChannels.resize(m_ids->getSize()); m_event = event; //event支持多线程的初始化函数
int hr = evthread_use_windows_threads(); m_base = event_base_new();
if (!m_base) {
fprintf(stderr, "Could not initialize libevent!\n");
return false;
} return true;
} //侦听端口,-1表示向系统申请一个任意可用端口
bool LibEvtServer::listen(int* port)
{
struct sockaddr_in sin; memset(&sin, , sizeof(sin));
sin.sin_family = AF_INET;
if(- == *port)
sin.sin_port = htons();
else
sin.sin_port = htons(*port); m_listener = evconnlistener_new_bind(m_base, ::listener_cb, (void*)this,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -,
(struct sockaddr*)&sin,
sizeof(sin));
if (!m_listener)
{
return false;
} if( - == *port)
*port = ntohs(sin.sin_port); if (!m_listener) {
fprintf(stderr, "Could not create a listener!\n");
return false;
}
m_spThread.reset(new std::thread([this]
{
//SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
//event_base_loop(m_base, EVLOOP_ONCE);
event_base_dispatch(m_base);
if(WSAENOTSOCK == WSAGetLastError())
{
Plug::PlugMessageBox(L"操作无效套接字啊!");
} Plug::PlugMessageBox(L"Libevent派发线程退出!");
}));
return true;
} //然后在这个监听器里面的回调函数里面
/* 新连接事件处理 */
typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);//这个是回调的原型
static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
auto ser = (LibEvtServer*)user_data;
ser->listener_cb(listener, fd, sa, socklen, user_data);
} void LibEvtServer::listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data)
{
//获取listener相关的event_base
auto base = evconnlistener_get_base(listener); //建立socket的bufferevent
auto bev = bufferevent_socket_new(base, fd, BEV_OPT_THREADSAFE);//BEV_OPT_CLOSE_ON_FREE |
if (!bev)
{
fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return ;
} auto c2 = CreateChannel(bev); //设置回调
bufferevent_setcb(bev, conn_readcb, NULL, conn_eventcb, c2);
//启动bufferevent
bufferevent_enable(bev, EV_READ | EV_WRITE );
} //看来服务器过程和客户端差不多,
//libevent过程,基于event_base的socket bufferevent,在event_base_dispath里面不知道用的是select还是iocp,然后发现socket状态发生变化可读,可写,或者
//错误都会调用相应的注册时间,建立的socket都是不阻塞的。还有个这个读写回调到底是什么时候调用的,我还不太明白,和那个读写缓冲区到底什么区别.
项目中的libevent的更多相关文章
- 项目中的Libevent(多线程)
多线程版Libevent //保存线程的结构体 struct LibeventThread { LibEvtServer* that; //用作传参 std::shared_ptr<std::t ...
- 在CLion项目中指定不同版本的链接库
在项目中, 需要使用到libevent-2.1.x, 但是Ubuntu16.04自带的libevent版本为2.0.5, 需要另外编译安装新版的libevent, 安装过程很简单 -stable.ta ...
- VS项目中使用Nuget还原包后编译生产还一直报错?
Nuget官网下载Nuget项目包的命令地址:https://www.nuget.org/packages 今天就遇到一个比较奇葩的问题,折腾了很久终于搞定了: 问题是这样的:我的解决方案原本是好好的 ...
- ABP项目中使用Swagger生成动态WebAPI
本文是根据角落的白板报的<使用ABP实现SwaggerUI,生成动态webapi>一文的学习总结,感谢原文作者角落的白板报. 1 安装Swashbuckle.core 1.1 选择WebA ...
- iOS 之项目中遇到的问题总结
昨天去一家公司面试,面试官问了我在项目开发中遇到过哪些问题,是什么引起的,怎样解决的? 当时由于有点小紧张只说出了一两点,现在就来好好总结一下. 问题: 1.两表联动 所谓的两表联动就是有左右两个表格 ...
- My97DatePicker时间控件在项目中的应用
一.下载My97DatePicker的压缩包My97DatePicker.rar,解压. 注:My97DatePicker最新版本有开发包,项目中使用时删掉,以便节省空间,提高程序的运行效率. 二.在 ...
- 在项目中同时使用Objective-C和Swift
苹果发布的Swift语言可以和之前的Objective-C语言同时存在于一个项目中. 可能有人会认为是同一个类文件中既可以有Objective-C也可以有Swift,这是不对的.同一个类文件或同一个代 ...
- 在数据库访问项目中使用微软企业库Enterprise Library,实现多种数据库的支持
在我们开发很多项目中,数据访问都是必不可少的,有的需要访问Oracle.SQLServer.Mysql这些常规的数据库,也有可能访问SQLite.Access,或者一些我们可能不常用的PostgreS ...
- 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入
在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...
随机推荐
- iOS访问通讯录开发-读取联系人信息
读取通信录中的联系人一般的过程是先查找联系人记录,然后再访问记录的属性,属性又可以分为单值属性和多值属性.通过下面例子介绍联系人的查询,以及单值属性和多值属性的访问,还有读取联系人中的图片数据. 本案 ...
- Spring IoC容器的设计—3—次线
这里涉及的是主要接口关系,而具体的IoC容器都是在这个接口体系下实现的,比如DefaultListableBeanFactory,这个基本IoC容器的实现就是实现了ConfigurableBeanFa ...
- 分享9款极具创意的HTML5/CSS3进度条动画
1.HTML5/CSS3图片加载进度条 可切换多主题 今天要分享的这款HTML5/CSS3进度条模拟了真实的图片加载场景,插件会默认去从服务器下载几张比较大的图片,然后让该进度条展现当前读取图片的进度 ...
- MySQL Limit order by
今天写模糊查询的时候,按照时间排序并进行分页时,在mybatis的映射文件中有这样一条sql语句 SELECT <include refid="Base_Column_List&quo ...
- Fragstats软件使用及其景观生态学意义
[转]Fragstats软件使用及其景观生态学意义 原文地址:http://blog.163.com/shuailai@126/blog/static/13238040820104152513 ...
- 在ArcGIS中WGS84大地坐标和投影平面坐标的转换
以WGS84转换为北京54坐标为例: 首先你要先知道转化的参数,鉴于我国曾使用不同的坐标基准(BJ54.State80.Correct54),各地的重力值又有很大差异,所以很难确定一套适合全国且精度较 ...
- linux网络编程中阻塞和非阻塞socket的区别
读操作 对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返 回.当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数.当 ...
- .Net三维控件
AnyCAD .Net三维建模和可视化控件为.Net 4.0开发者提供简单易用的三维建模.三维可视化和文件交换的API. 30天试用版下载: 1. 三维建模 三维建模有以下功能: 三维基本体,如点. ...
- MySQL用命令行导出数据库
MySQL命令行导出数据库:首先进入cmd然后:1. cd C:\Program Files (x86)\MySQL\MySQL Server 5.1\bin2. mysqldump -uroot - ...
- iOS获取汉字的拼音
在iOS开发中经常涉及到汉字的排序,最常见的就是需要根据首字母的字符顺序排列,比如常见的通讯录等.总结出来,大致可以分为两种方法,其中参考文献[1]中提供的方法十分复杂,利用查表的方法是先,并且代码量 ...