转自:http://name5566.com/4220.html

参考文献列表:
http://www.wangafu.net/~nickm/libevent-book/

此文编写的时候,使用到的 Libevent 为 2.0.21

Libevent 提供了连接监听器 evconnlistener

创建 evconnlistener 实例

  1. // 连接监听器回调函数原型
  2. typedef void (*evconnlistener_cb)(
  3. struct evconnlistener *listener,
  4. // 新的 socket
  5. evutil_socket_t sock,
  6. // 新的 socket 对应的地址
  7. struct sockaddr *addr,
  8. int len,
  9. // 用户自定义数据
  10. void *ptr
  11. );
  12. // 创建一个新的连接监听器
  13. struct evconnlistener *evconnlistener_new(
  14. struct event_base *base,
  15. // 一个新的连接到来时此回调被调用
  16. evconnlistener_cb cb,
  17. // 用户自定义数据,会被传递给 cb 回调函数
  18. void *ptr,
  19. // 连接监听器的选项(下面会详细谈到)
  20. unsigned flags,
  21. // 为标准的 listen 函数的 backlog 参数
  22. // 如果为负数,Libevent 将尝试选择一个合适的值
  23. int backlog,
  24. // socket
  25. // Libevent 假定此 socket 已经绑定
  26. evutil_socket_t fd
  27. );
  28. // 创建一个新的连接监听器
  29. // 大多数参数含义同于 evconnlistener_new
  30. struct evconnlistener *evconnlistener_new_bind(
  31. struct event_base *base,
  32. evconnlistener_cb cb,
  33. void *ptr,
  34. unsigned flags,
  35. int backlog,
  36. // 指定需要绑定的 socket 地址
  37. const struct sockaddr *sa,
  38. int socklen
  39. );

连接监听器的常用选项如下:

  1. LEV_OPT_CLOSE_ON_FREE
    当关闭连接监听器其底层 socket 也被自动释放
  2. LEV_OPT_REUSEABLE
    设置 socket 绑定的地址可以重用
  3. LEV_OPT_THREADSAFE
    设置连接监听器为线程安全的

释放连接监听器

  1. void evconnlistener_free(struct evconnlistener *lev);

错误检测
如果连接监听器出错,我们可以得到通知:

  1. // 连接监听器错误回调函数原型
  2. typedef void (*evconnlistener_errorcb)(struct evconnlistener *lis, void *ptr);
  3. // 为连接监听器设置错误回调函数
  4. void evconnlistener_set_error_cb(struct evconnlistener *lev,
  5. evconnlistener_errorcb errorcb);

一个详细的范例(echo 服务器)

    1. #include <event2/listener.h>
    2. #include <event2/bufferevent.h>
    3. #include <event2/buffer.h>
    4. #include <arpa/inet.h>
    5. #include <string.h>
    6. #include <stdlib.h>
    7. #include <stdio.h>
    8. #include <errno.h>
    9. // 读取回调函数
    10. static void
    11. echo_read_cb(struct bufferevent *bev, void *ctx)
    12. {
    13. struct evbuffer *input = bufferevent_get_input(bev);
    14. struct evbuffer *output = bufferevent_get_output(bev);
    15. // 将输入缓冲区的数据直接拷贝到输出缓冲区
    16. evbuffer_add_buffer(output, input);
    17. }
    18. // 事件回调函数
    19. static void
    20. echo_event_cb(struct bufferevent *bev, short events, void *ctx)
    21. {
    22. if (events & BEV_EVENT_ERROR)
    23. perror("Error from bufferevent");
    24. if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
    25. bufferevent_free(bev);
    26. }
    27. }
    28. // 连接监听器回调函数
    29. static void
    30. accept_conn_cb(struct evconnlistener *listener,
    31. evutil_socket_t fd, struct sockaddr *address, int socklen,
    32. void *ctx)
    33. {
    34. // 为新的连接分配并设置 bufferevent
    35. struct event_base *base = evconnlistener_get_base(listener);
    36. struct bufferevent *bev = bufferevent_socket_new(
    37. base, fd, BEV_OPT_CLOSE_ON_FREE);
    38. bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL);
    39. bufferevent_enable(bev, EV_READ|EV_WRITE);
    40. }
    41. // 连接监听器错误回调函数
    42. static void
    43. accept_error_cb(struct evconnlistener *listener, void *ctx)
    44. {
    45. struct event_base *base = evconnlistener_get_base(listener);
    46. // 获取到错误信息
    47. int err = EVUTIL_SOCKET_ERROR();
    48. fprintf(stderr, "Got an error %d (%s) on the listener. "
    49. "Shutting down.\n", err, evutil_socket_error_to_string(err));
    50. // 退出事件循环
    51. event_base_loopexit(base, NULL);
    52. }
    53. int
    54. main(int argc, char **argv)
    55. {
    56. struct event_base *base;
    57. struct evconnlistener *listener;
    58. struct sockaddr_in sin;
    59. int port = 9876;
    60. if (argc > 1) {
    61. port = atoi(argv[1]);
    62. }
    63. if (port<=0 || port>65535) {
    64. puts("Invalid port");
    65. return 1;
    66. }
    67. base = event_base_new();
    68. if (!base) {
    69. puts("Couldn't open event base");
    70. return 1;
    71. }
    72. memset(&sin, 0, sizeof(sin));
    73. sin.sin_family = AF_INET;
    74. sin.sin_addr.s_addr = htonl(0);
    75. sin.sin_port = htons(port);
    76. listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
    77. LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
    78. (struct sockaddr*) & sin, sizeof(sin));
    79. if (!listener) {
    80. perror("Couldn't create listener");
    81. return 1;
    82. }
    83. evconnlistener_set_error_cb(listener, accept_error_cb);
    84. event_base_dispatch(base);
    85. return 0;
    86. }

(转)Libevent(5)— 连接监听器的更多相关文章

  1. libevent系列文章

    Libevent 2 提供了 bufferevent 接口,简化了编程的难度,bufferevent 实际上是对底层事件核心的封装,因此学习 bufferevent 的实现是研究 Libevent 底 ...

  2. 项目中的libevent

    单线程libevent模式 项目里面是多线程版的,我先理解下单线程的. //client .调用NGP::init() bool NGP::init(NGPcontext context) { _co ...

  3. libevent入门

    Libevent API =============================== evtimer_new evtimer_new(base, callback, NULL) 用来做定时器,即当 ...

  4. Libevent API

    evtimer_new evtimer_new(base, callback, NULL) 用来做定时器,即当达到一定时间后调用回调函数callback.用evtimer_add激活定时器.比如: m ...

  5. Libevent官方代码样例学习(二)

    连接监听器: 接收TCP连接请求 evconnlistener机制用于监听并接受TCP连接请求. 这些方法在event2/listener.h中声明, 在Libevent 2.0.2-alpha之后的 ...

  6. libevent编程疑难解答

    http://blog.csdn.net/luotuo44/article/details/39547391 转载请注明出处:http://blog.csdn.net/luotuo44/article ...

  7. Oracle 监听器

    Oracle监听器listener是一个重要的数据库服务器组件,在整个Oracle体系结构中,扮演着重要的作用. 监听器Lisener功能 从当前的Oracle版本看,Listener主要负责下面的几 ...

  8. Android基于XMPP Smack Openfire下学习开发IM(五)连接断开重连

    学习过程中大家都碰到过连接被断开的问题给困扰吧,下面教大家如何做到连接断开后,重新连接 首先要创建连接监听器,用来监听连接状态,这里我写了一个类 继承了ConnectionListener,重写了里面 ...

  9. Openfire分析之三:ConnectionManager 连接管理(1)

    Openfire是怎么实现连接请求的? XMPPServer.start()方法,完成Openfire的启动.但是,XMPPServer.start()方法中,并没有提及如何监听端口,那么Openfi ...

随机推荐

  1. CF_402D Upgrading Array 因式分解

    题目链接:http://codeforces.com/problemset/problem/402/D /**算法分析: */ #include<bits/stdc++.h> #defin ...

  2. 两个栈实现一个队列,C语言实现,队列可伸缩,容纳任意数目的元素。

    一.思路:1.创建两个空栈A和B:2.A栈作为队列的入口,B栈作为队列的出口:3.入队列操作:即是入栈A:4.出队列操作:若栈B为空,则将A栈内容出栈并压人B栈,再出 B栈:不为空就直接出栈: 二.代 ...

  3. jQuery进行图片预先加载

    /** **将图片加载到内存中,在页面任何位置引用的时候,直接从缓存中读取,提升用户的体验,提高网站的流畅度.** **/ <!DOCTYPE html PUBLIC "-//W3C/ ...

  4. Kali无法定位软件包的解决方案

    异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 以前遇到过,后来好久没碰Linux了,忘记了.之后上网搜了一下....呃,发现解 ...

  5. 库不存在的排查方法:ImportError: No module named selenium2Library

    解决办法: 把selenium2Library改成Selenium2Library   安装下面四个: python-2.7.13.amd64.msi robotframework-ride-1.5. ...

  6. 利用http协议实现图片窃取

    在http协议里有一个referer,用来标示站点来源,大家都遇到这样的情况.转载了一篇博客,图片显示不正常,就和头信息里这个有关 原理:在webserver里面.依据http协议里面refered头 ...

  7. Caused by: java.lang.NoClassDefFoundError: freemarker/cache/TemplateLoader

    1.错误描写叙述 usage: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -he ...

  8. Android Studio 完美修改应用包名

    我们平时新建项目有些朋友可能当时就是随意写的一个包名,然后在项目过程中, 又感觉这个包名不太好,所以就要对包名进行修改,根据我们正常的修改方式,是这样的. 在种情况是只能修改最外层的那个名称, 如果我 ...

  9. Ubuntu远程桌面xrdp方法

    xrdp方式,该方法支持多用户登录并远程桌面. 首先在Ubuntu上安装GNOME界面,在终端输入命令: sudo apt-get install gnome-panel 安装完成后注销用户. 点击自 ...

  10. 解锁Dagger2使用姿势(一)

    毫无疑问,Dagger2的 上手是有门槛的,有门槛是因为它里边的概念多,用起来复杂,可是一旦你学会了Dagger2的使用,你一定会爱不释手的.与ButterKnife和AndroidAnnotatio ...