帮助文档
专业提供香港服务器、香港云服务器、香港高防服务器租用、香港云主机、台湾服务器、美国服务器、美国云服务器vps租用、韩国高防服务器租用、新加坡服务器、日本服务器租用 一站式全球网络解决方案提供商!专业运营维护IDC数据中心,提供高质量的服务器托管,服务器机房租用,服务器机柜租用,IDC机房机柜租用等服务,稳定、安全、高性能的云端计算服务,实时满足您的多样性业务需求。 香港大带宽稳定可靠,高级工程师提供基于服务器硬件、操作系统、网络、应用环境、安全的免费技术支持。
服务器资讯 / 香港服务器租用 / 香港VPS租用 / 香港云服务器 / 美国服务器租用 / 台湾服务器租用 / 日本服务器租用 / 官方公告 / 帮助文档
TCP服务器的演变过程:C++使用libevent库开发服务器程序
发布时间:2024-03-07 00:48:10   分类:帮助文档
TCP服务器的演变过程:C++使用libevent库开发服务器程序 C++使用libevent库开发服务器程序 一、引言二、libevent简介三、Libevent库的封装层级3.1、reactor对象封装struct event_base3.2、事件对象struct event3.3、struct bufferevent对象3.4、evconnlistener对象3.5、事件循环3.6、事件处理 四、完整示例代码小结 一、引言 手把手教你从0开始编写TCP服务器程序,体验开局一块砖,大厦全靠垒。 为了避免篇幅过长使读者感到乏味,对【TCP服务器的开发】进行分阶段实现,一步步进行优化升级。 在上一章节介绍了如何使用epoll构建reactor网络模型开发高效的服务器,有了上一节的基础,本节将介绍使用开源库libevent进行开发服务器程序。 #mermaid-svg-VdeR5xDmD29WRglt {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-VdeR5xDmD29WRglt .error-icon{fill:#552222;}#mermaid-svg-VdeR5xDmD29WRglt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VdeR5xDmD29WRglt .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-VdeR5xDmD29WRglt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VdeR5xDmD29WRglt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VdeR5xDmD29WRglt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VdeR5xDmD29WRglt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VdeR5xDmD29WRglt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VdeR5xDmD29WRglt .marker.cross{stroke:#333333;}#mermaid-svg-VdeR5xDmD29WRglt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VdeR5xDmD29WRglt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VdeR5xDmD29WRglt .cluster-label text{fill:#333;}#mermaid-svg-VdeR5xDmD29WRglt .cluster-label span{color:#333;}#mermaid-svg-VdeR5xDmD29WRglt .label text,#mermaid-svg-VdeR5xDmD29WRglt span{fill:#333;color:#333;}#mermaid-svg-VdeR5xDmD29WRglt .node rect,#mermaid-svg-VdeR5xDmD29WRglt .node circle,#mermaid-svg-VdeR5xDmD29WRglt .node ellipse,#mermaid-svg-VdeR5xDmD29WRglt .node polygon,#mermaid-svg-VdeR5xDmD29WRglt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VdeR5xDmD29WRglt .node .label{text-align:center;}#mermaid-svg-VdeR5xDmD29WRglt .node.clickable{cursor:pointer;}#mermaid-svg-VdeR5xDmD29WRglt .arrowheadPath{fill:#333333;}#mermaid-svg-VdeR5xDmD29WRglt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VdeR5xDmD29WRglt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VdeR5xDmD29WRglt .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-VdeR5xDmD29WRglt .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-VdeR5xDmD29WRglt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VdeR5xDmD29WRglt .cluster text{fill:#333;}#mermaid-svg-VdeR5xDmD29WRglt .cluster span{color:#333;}#mermaid-svg-VdeR5xDmD29WRglt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VdeR5xDmD29WRglt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} event_base_new() evconnlistener_new_bind() 设置回调函数 evconnlistener_enable() event_base_dispatch() evconnlistener_accept() bufferevent_socket_new() 设置读回调函数 bufferevent_read() 数据处理 bufferevent_write() 关闭连接 创建事件驱动 初始化事件库 创建监听器 设置回调函数 启用监听器 事件循环 接受客户端连接 创建读写缓冲事件 设置读回调函数 读取数据 数据处理 发送数据 关闭连接 二、libevent简介 libevent是一个事件通知库,封装了reactor。 libevent API 提供了一种机制,用于在文件描述符上发生特定事件或达到超时后执行回调函数。此外,libevent还支持由于信号或常规超时而导致的回调。 libevent 旨在替换在事件驱动的网络服务器中找到的事件循环。应用程序只需要调用event_dispatch(),然后动态添加或删除事件,而无需更改事件循环。 目前,该控件支持/dev/poll, kqueue(), event ports, POSIX select(), Windows select(), poll(), and epoll()。内部事件机制完全独立于公开的事件 API,并且 libevent 的简单更新可以提供新功能,而无需重新设计应用程序。因此,Libevent 允许可移植应用程序开发,并提供操作系统上可用的最具可扩展性的事件通知机制。libevent 还可用于多线程应用程序,方法是隔离每个event_base,以便只有单个线程访问它,或者锁定对单个共享event_base的访问。自由的在 Linux、*BSD、Mac OS X、Solaris、Windows 等设备上编译。 libevent 还为缓冲网络 IO 提供了一个复杂的框架,支持套接字、筛选器、速率限制、SSL、零副本文件传输和 IOCP。自由度包括对几种有用协议的支持,包括 DNS、HTTP 和最小的 RPC 框架。 libevent编译安装: 官网下载安装包并解压。进入解压目录执行: wget https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz tar -zxvf libevent-2.1.12-stable.tar.gz cd libevent-2.1.12-stable ./configure make sudo make install 三、Libevent库的封装层级 3.1、reactor对象封装struct event_base reactor对象封装为struct event_base;通过: (1)event_base_new()构造对象。 (2)event_base_free()销毁对象。 3.2、事件对象struct event 事件对象通过struct event的结构体封装使用。 struct event { struct event_callback ev_evcallback; /* for managing timeouts */ union { TAILQ_ENTRY(event) ev_next_with_common_timeout; int min_heap_idx; } ev_timeout_pos; evutil_socket_t ev_fd; struct event_base *ev_base; union { /* used for io events */ struct { LIST_ENTRY (event) ev_io_next; struct timeval ev_timeout; } ev_io; /* used by signal events */ struct { LIST_ENTRY (event) ev_signal_next; short ev_ncalls; /* Allows deletes in callback */ short *ev_pncalls; } ev_signal; } ev_; short ev_events; short ev_res; /* result passed to event callback */ struct timeval ev_timeout; }; 变量含义ev_evcallback回调函数。事件是异步处理的,需要回调函数。min_heap_idx时间事件的最小堆的索引。ev_fd定时事件的fd。ev_base事件对象所属的reactor的对象。ev_io网络事件关注的事情。ev_signal信号事件关注的事情。ev_timeout超时。ev_timeout_pos和ev_fd定时任务处理的事情。ev_events具体注册的事件。ev_具体的信号。 通常,event对象可以自己处理IO。 (1)event_new():构建事件对象、绑定、事件回调。 (2)event_free():销毁事件对象。 bufferevent和evconnlistener对象只需要关注业务逻辑的处理,由libevent内部处理IO操作。 bufferevent是在event对象上面封装的缓冲区。 3.3、struct bufferevent对象 struct bufferevent中的重要成员变量: 变量含义ev_base事件对象所属的reactor的对象。be_opsbufferevent的具体操作。控制某个事件的打开、关闭、移除等,其中input是用户态读缓冲区,output是用户态写缓冲区readcb读事件的回调函数writecb注意不是写事件回调,而是低水平触发的回调函数。这是涉及到写失败时的处理,内部会处理写事件发送出去。通常不需要设置写回调函数。errorcb所有错误事件的回调函数。被动关闭连接或其他异常的回调函数。wm_read读水平线,里面分有高水平和低水平。低水平是指buffer中有多少数据就要触发回调,默认为0,即每次读事件都会触发回调;高水平是指缓冲区中达到多大的数据就要关闭读事件,即buffer数据比较多的时候不再处理读事件。wm_write写水平线,写只有低水平没有高水平。低水平默认值是0,即用户态缓冲区为空时回调写回调函数。 struct bufferevent_ops中的重要成员变量: 变量含义input用户态读缓冲区。output用户态写缓冲区。 (1)bufferevent_socket_new():构建bufferevent对象。 (2)bufferevent_free():销毁bufferevent对象。 3.4、evconnlistener对象 evconnlistener是专门处理listenfd的对象,使我们不需要关注bind、listen、accept的具体操作。 struct evconnlistener_ops { int (*enable)(struct evconnlistener *); int (*disable)(struct evconnlistener *); void (*destroy)(struct evconnlistener *); void (*shutdown)(struct evconnlistener *); evutil_socket_t (*getfd)(struct evconnlistener *); struct event_base *(*getbase)(struct evconnlistener *); }; struct evconnlistener { const struct evconnlistener_ops *ops; void *lock; evconnlistener_cb cb; evconnlistener_errorcb errorcb; void *user_data; unsigned flags; short refcnt; int accept4_flags; unsigned enabled : 1; }; (1)evconnlistener_new():构建evconnlistener对象、绑定、事件回调。 (2)evconnlistener_free():销毁evconnlistener对象。 (3)evconnlistener_bind_new():创建listenfd、bind、listen、注册读事件。 3.5、事件循环 (1)事件循环:event_base_dispatch(),event_base_loop()。 (2)事件循环退出:event_base_loopexit(),event_base_break()。 3.6、事件处理 设置事件相对应的回调。 (1)如果是使用event对象,在event_new()会设置相对应的回调。 (2)如果IO由libevent处理,那么使用bufferevent_setcb()来设置回调。 void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg) { BEV_LOCK(bufev); bufev->readcb = readcb; bufev->writecb = writecb; bufev->errorcb = eventcb; bufev->cbarg = cbarg; BEV_UNLOCK(bufev); } 四、完整示例代码 #include #include #include #include #include #include #include #define SOCKET_LISTEN_PORT 9703 #define SOCKET_BACKLOG_NUM 128 class asyn_event { private: /* data */ struct event_base *base; struct evconnlistener *listener; public: asyn_event(/* args */); ~asyn_event(); static void accept_cb(struct evconnlistener *listen,evutil_socket_t fd,struct sockaddr *sock,int socklen,void *arg); static void socket_event_callback(struct bufferevent *bev, short events, void *arg); static void socket_read_callback(struct bufferevent *bev, void *arg); void loop_run(); }; asyn_event::asyn_event(/* args */) { base=event_base_new(); struct sockaddr_in server={0}; server.sin_family=AF_INET; server.sin_addr.s_addr=htonl(INADDR_ANY); server.sin_port=htons(SOCKET_LISTEN_PORT); listener=evconnlistener_new_bind( base, &asyn_event::accept_cb, base, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, SOCKET_BACKLOG_NUM, (struct sockaddr*)&server, sizeof(server) ); } asyn_event::~asyn_event() { // 销毁evconnlistener对象 evconnlistener_free(listener); // 销毁事件对象 event_base_free(base); } void asyn_event::accept_cb(struct evconnlistener *listen,evutil_socket_t fd,struct sockaddr *sock,int socklen,void *arg) { struct event_base *base = (struct event_base *)arg; // 连接的建立---接收连接 char ip[32] = { 0 }; evutil_inet_ntop(AF_INET, sock, ip, sizeof(ip) - 1); printf("accept a client fd:%d, ip:%s\n", fd, ip); struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); // 注册读事件 bufferevent_setcb(bev, socket_read_callback, NULL, socket_event_callback, NULL);//写事件回调一般为NULL bufferevent_enable(bev, EV_READ | EV_PERSIST); } void asyn_event::loop_run() { // 事件循环 event_base_dispatch(base); } // 处理连接断开 void asyn_event::socket_event_callback(struct bufferevent *bev, short events, void *arg) { if (events &BEV_EVENT_EOF)//read=0 { printf("connection closed\n"); } else if (events & BEV_EVENT_ERROR)//strerro(errno) { printf("some other error\n"); } else if (events &BEV_EVENT_TIMEOUT) printf("time out\n"); bufferevent_free(bev);// close(fd) } // 读回调 void asyn_event::socket_read_callback(struct bufferevent *bev, void *arg) { struct evbuffer *input = bufferevent_get_input(bev); struct evbuffer *output = bufferevent_get_output(bev); // 从输入缓冲区读取数据 char buf[1024]; size_t len; while ((len = evbuffer_remove(input, buf, sizeof(buf))) > 0) { //printf("Received: %.*s", (int)len, buf); evbuffer_add(output, buf, len); } //bufferevent_write(bev, reply, strlen(reply)); } int main() { asyn_event ev; ev.loop_run(); return 0; } 编译时要指定事件库,添加 -levent 参数。 gcc -o ev ev.c -levent 运行时出现libevent-2.1.so.7。 error while loading shared libraries: libevent-2.1.so.7: cannot open shared object file: No such file or directory 产生原因:libevent动态库在默认安装时,存放的路径在/usr/local/lib下,不在系统的默认查找路径内。 解决办法有两个: (1)将该路径放在系统查找路径内。这种方法仅永久有效。 sudo echo "/usr/local/lib" >> /etc/ld.so.conf sudo ldconfig (2)添加环境变量的方法,添加 export LD_LIBRARY_PATH=XXX。这种方法仅当前有效。 export LD_LIBRARY_PATH=/usr/local/lib/ 小结 (1)有了libevent可以不使用IO函数。因为如果使用IO函数,既需要知道这些IO函数里面的系统调用返回值的含义。 (2)有了libevent就可以不清楚数据拷贝原理。 (3)有了libevent就可以不清楚网络原理以及网络编程流程。 (4)有了libevent只需要知道事件处理,IO操作完全交由libevent处理。 至此,我们实现了使用libevent库开发高并发的服务器程序,但是,这个服务器程序有些局限性,我们还要继续改善、优化。在改进之前,需要开发一个后台日志模块,这是服务器程序必须的,所有,下一个章节将开发一个高效的后台日志模块。
香港云服务器租用推荐
服务器租用资讯
·广东云服务有限公司怎么样
·广东云服务器怎么样
·广东锐讯网络有限公司怎么样
·广东佛山的蜗牛怎么那么大
·广东单位电话主机号怎么填写
·管家婆 花生壳怎么用
·官网域名过期要怎么办
·官网邮箱一般怎么命名
·官网网站被篡改怎么办
服务器租用推荐
·美国服务器租用
·台湾服务器租用
·香港云服务器租用
·香港裸金属服务器
·香港高防服务器租用
·香港服务器租用特价