libevent服务端,单线程应用

2023-11-10

libevent版本:libevent-2.1.12-stable

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>

#ifdef _WIN32
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "event.lib")
#pragma comment(lib, "event_extra.lib")
#pragma comment(lib, "event_core.lib")
#else
#include <netinet/in.h>
#include <pthread.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#include <sys/socket.h>
#define GetCurrentThreadId() pthread_self()
#endif

#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include <event2/thread.h>

static const char MESSAGE[] = "Hello, World!\n";

static const int PORT = 9638;

static void listener_cb(struct evconnlistener *, evutil_socket_t,
    struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_readcb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);
static void connlistener_errorcb(struct evconnlistener *, void *);

int main1(int argc, char **argv)
{
    printf("the main thread id: %d\n", GetCurrentThreadId());
    struct event_base *base;
    struct evconnlistener *listener;
    struct event *signal_event;

#ifdef WIN32
    WSADATA wsa_data;
    WSAStartup(0x0202, &wsa_data);
#endif

    base = event_base_new();

    if (!base)
    {
        fprintf(stderr, "Could not initialize libevent!\n");
        return 1;
    }

    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT);

    //创建服务端的socket,并绑定端口监听,当有客户端连接时,则listener_cb发生回调
    listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
        LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1,
        (struct sockaddr *)&sin, sizeof(sin));

    /*//也可以把ipv6添加到监听中去
    struct sockaddr_in6 sin6;
    memset(&sin6, 0, sizeof(struct sockaddr_in6));
    sin6.sin6_family = AF_INET6;
    sin6.sin6_port = htons(2000);
    evconnlistener *listener6 = evconnlistener_new_bind(base, listener_cb, base,
        LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1,
        (struct sockaddr*)&sin6, sizeof(sin6));
    */
    if (!listener)
    {
        fprintf(stderr, "Could not create a listener!\n");
        goto loop_basefree;
        return 1;
    }
    //设置listen监听错误事件,这个其实没什么用
    evconnlistener_set_error_cb(listener, connlistener_errorcb);

    //创建一个新的事件信号
    signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
    if (!signal_event)
    {
        fprintf(stderr, "Could not create/add a signal event!\n");
        goto loop_listener;
        return 1;
    }
    if (event_add(signal_event, NULL) < 0)
    {
        fprintf(stderr, "Could not create/add a signal event!\n");
        goto loop_signal;
        return 1;
    }

    event_base_dispatch(base); //这里面进入循环了

loop_signal:
    event_free(signal_event);
loop_listener:
    evconnlistener_free(listener);
loop_basefree:
    event_base_free(base);

#ifdef WIN32
    WSACleanup();
#endif
    printf("done\n");
    return 0;
}

/**************************************************************************************************************************
libevent的一些问题记录。
1. 读写时,可能存在最大包字节限制,因为默认为16384字节,下面这2个宏是内部定义的。
#define MAX_SINGLE_READ_DEFAULT 16384
#define MAX_SINGLE_WRITE_DEFAULT 16384
可以通过bufferevent_set_max_single_read(...),bufferevent_set_max_single_write(...)去设置最大值。

evutil_socketpair(...)在Windows上使用可能会被防火墙挡住
在上面的应用例子listener_cb()中,之所以采用定时器的方式去获取,是因为可能Windows的防火墙挡住问题,在linux上可以直接使用pipe或者evutil_socketpair
*/
/**********************************************************************************/
//当一个新的连接到来时,触发该回调被调用
//该回调是在 event_base_dispatch() 的内部被执行的,因此这里执行耗时操作可能会影响事件循环。
static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data)
{
    struct event_base *base = (struct event_base *)user_data;
    struct bufferevent *bev;
    /**将fd,也就是socket注册到事件循环中,以后所有的读写都在同一个线程中。
    如果想要读写和释放在别的线程中也能够安全操作,则需要,添加 BEV_OPT_THREADSAFE,同时需要在最开始的时候调用
    evthread_use_windows_threads()、evthread_use_pthreads()告诉平台启用多线程**/
    bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    if (!bev)
    {
        fprintf(stderr, "Error constructing bufferevent!");
        evutil_closesocket(fd);
        //强制结束main里面的event_base_dispatch,这样整个服务端结束
        event_base_loopbreak(base);
        return;
    }

    printf("the listener_cb thread id: %d, bev=%p\n", GetCurrentThreadId(), bev);

    //客户端连接的ip和端口参数
    char szClientIp[256] = { 0 };
    struct sockaddr_in *addr = (struct sockaddr_in *)sa;
    evutil_inet_ntop(addr->sin_family, &addr->sin_addr, szClientIp, sizeof(szClientIp));
    short iPort = ntohs(addr->sin_port);

    //设置读写和客户端连接状态的回调函数
    bufferevent_setcb(bev, conn_readcb, conn_writecb, conn_eventcb, user_data);

    //设置读写回调是否可用
    bufferevent_enable(bev, EV_WRITE | EV_READ);

    //向客户端写数据
    bufferevent_write(bev, MESSAGE, strlen(MESSAGE));

    printf("a new connect %s:%hu\n", szClientIp, iPort);
}


//向客户端写完数据后发生的一个回调,通常是调用bufferevent_write后发生了回调。
//该回调是在 event_base_dispatch() 的内部被执行的,因此这里执行耗时操作可能会影响事件循环。
static void conn_writecb(struct bufferevent *bev, void *user_data)
{
    printf("the conn_writecb thread id: %d, bev=%p\n", GetCurrentThreadId(), bev);
    //user_data值为bufferevent_setcb的最后一个参数
    struct evbuffer *output = bufferevent_get_output(bev);
    if (evbuffer_get_length(output) == 0)
    {
        //printf("flushed answer\n");
    }
}

//当收到客户端发送过来的数据时,该函数发生了回调
//该回调是在 event_base_dispatch() 的内部被执行的,因此这里执行耗时操作可能会影响事件循环。
static void conn_readcb(struct bufferevent *bev, void *user_data)
{
    printf("the conn_readcb thread id: %d, bev=%p\n", GetCurrentThreadId(), bev);
    struct evbuffer *input = bufferevent_get_input(bev);
    size_t len = evbuffer_get_length(input);
    if (!len)
    {
        puts("接收到的数据个数是0");
        return;
    }
    char data[1025] = "";
    size_t size = 0;
    /*从缓冲区中读取接收到的数据,如果不采用这种方式,那么只有等到下次收到用户发送的数据时才会触发。
     所以循环读取所有数据,不过实际这个缓冲区大小是足够的,默认为 16384=16k
     可以通过 bufferevent_set_max_single_read/write来修改
    */
    while (0 != (size = bufferevent_read(bev, data, 1024)))
    {
        printf("data=%s, len=%d\n", data, size);
    }
    const char *wData = "send to client!";
    bufferevent_write(bev, wData, strlen(wData) + 1);

    //主动断开与客户端的连接
    //bufferevent_free(bev);
    printf("a new conn_readcb %p\n", bev);
}

//该回调是在 event_base_dispatch() 的内部被执行的,因此这里执行耗时操作可能会影响事件循环。
static void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
    printf("the conn_eventcb thread id: %d, bev=%p\n", GetCurrentThreadId(), bev);
    if (events & BEV_EVENT_EOF)
    {
        printf("Connection closed.\n");
    }
    else if (events & BEV_EVENT_ERROR)
    {
        printf("Got an error on the connection: %s\n", evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
    }
    // None of the other events can happen here, since we haven't enabled timeouts
    bufferevent_free(bev);
}

static void signal_cb(evutil_socket_t sig, short events, void *user_data)
{
    printf("the signal_cb thread id: %d\n", GetCurrentThreadId());
    struct event_base *base = (struct event_base *)user_data;
    struct timeval delay = { 2, 0 };

    printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");

    event_base_loopexit(base, &delay);
}

static void connlistener_errorcb(struct evconnlistener *listener, void *user_data)
{
    printf("the connlistener_errorcb thread id: %d\n", GetCurrentThreadId());
    struct event_base *base = (struct event_base *)user_data;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

libevent服务端,单线程应用 的相关文章

  • 如何在MVVM中管理多个窗口

    我知道有几个与此类似的问题 但我还没有找到明确的答案 我正在尝试深入研究 MVVM 并尽可能保持纯粹 但不确定如何在坚持模式的同时启动 关闭窗口 我最初的想法是向 ViewModel 发送数据绑定命令 触发代码来启动一个新视图 然后通过 X
  • 将复选框添加到 UniformGrid

    我正在尝试将复选框动态添加到 wpf 中的统一网格中 但看起来网格没有为它们分配足够的空间 所以它们都有点互相重叠 这就是我将它们添加到后面的代码中的方法 foreach string folder in subfolders PathCh
  • 如何验证文件名称在 Windows 中是否有效?

    是否有一个 Windows API 函数可以将字符串值传递给该函数 该函数将返回一个指示文件名是否有效的值 我需要验证文件名是否有效 并且我正在寻找一种简单的方法来完成此操作 而无需重新发明轮子 我正在直接使用 C 但针对的是 Win32
  • 访问私人成员[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 通过将类的私有成员转换为 void 指针 然后转换为结构来访问类的私有成员是否合适 我认为我无权修改包含我需要访问的数据成员的类 如果不道德 我
  • 将数组向左或向右旋转一定数量的位置,复杂度为 o(n)

    我想编写一个程序 根据用户的输入 正 gt 负 include
  • 如何避免情绪低落?

    我有一个实现状态模式每个状态处理从事件队列获取的事件 根据State因此类有一个纯虚方法void handleEvent const Event 事件继承基础Event类 但每个事件都包含其可以是不同类型的数据 例如 int string
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • 如何在列表框项目之间画一条线

    我希望能够用水平线分隔列表框中的每个项目 这只是我用于绘制项目的一些代码 private void symptomsList DrawItem object sender System Windows Forms DrawItemEvent
  • 使闭包捕获的变量变得易失性

    闭包捕获的变量如何与不同线程交互 在下面的示例代码中 我想将totalEvents 声明为易失性的 但C 不允许这样做 是的 我知道这是错误的代码 这只是一个例子 private void WaitFor10Events volatile
  • Newtonsoft JSON PreserveReferences处理自定义等于用法

    我目前在使用 Newtonsoft Json 时遇到一些问题 我想要的很简单 将要序列化的对象与所有属性和子属性进行比较以确保相等 我现在尝试创建自己的 EqualityComparer 但它仅与父对象的属性进行比较 另外 我尝试编写自己的
  • 当 contains() 工作正常时,xpath 函数ends-with() 工作时出现问题

    我正在尝试获取具有以特定 id 结尾的属性的标签 like span 我想获取 id 以 国家 地区 结尾的跨度我尝试以下xpath span ends with id Country 但我得到以下异常 需要命名空间管理器或 XsltCon
  • 为什么#pragma optimize("", off)

    我正在审查一个 C MFC 项目 在某些文件的开头有这样一行 pragma optimize off 我知道这会关闭所有以下功能的优化 但这样做的动机通常是什么 我专门使用它来在一组特定代码中获得更好的调试信息 并在优化的情况下编译应用程序
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • Json.NET - 反序列化接口属性引发错误“类型是接口或抽象类,无法实例化”

    我有一个类 其属性是接口 public class Foo public int Number get set public ISomething Thing get set 尝试反序列化Foo使用 Json NET 的类给我一条错误消息
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • 从库中捕获主线程 SynchronizationContext 或 Dispatcher

    我有一个 C 库 希望能够将工作发送 发布到 主 ui 线程 如果存在 该库可供以下人员使用 一个winforms应用程序 本机应用程序 带 UI 控制台应用程序 没有 UI 在库中 我想在初始化期间捕获一些东西 Synchronizati
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • C++ 复制初始化和直接初始化,奇怪的情况

    在继续阅读本文之前 请阅读在 C 中 复制初始化和直接初始化之间有区别吗 https stackoverflow com questions 1051379 is there a difference in c between copy i
  • 如何使我的表单标题栏遵循 Windows 深色主题?

    我已经下载了Windows 10更新包括黑暗主题 文件资源管理器等都是深色主题 但是当我创建自己的 C 表单应用程序时 标题栏是亮白色的 如何使我自己的桌面应用程序遵循我在 Windows 中设置的深色主题 你需要调用DwmSetWindo
  • 将文本叠加在图像背景上并转换为 PDF

    使用 NET 我想以编程方式创建一个 PDF 它仅包含一个背景图像 其上有两个具有不同字体和位置的标签 我已阅读过有关现有 PDF 库的信息 但不知道 如果适用 哪一个对于如此简单的任务来说最简单 有人愿意指导我吗 P D 我不想使用生成的

随机推荐

  • micropython-SPI通讯

    micropython SPI通讯 1 什么是SPI 2 SPI通讯原理 3 Micropython中的SPI 4 ZTMR测试SPI 1 ZTMR中SPI引脚 2 ZTMRSPI自测 2 SPI 2板之间通讯测试 1 什么是SPI SPI
  • malloc底层原理实现

    使用过c语言的都知道malloc是一个动态分配内存的函数 还可以通过free释放内存空间 如果我们想分析一下malloc的源码 这其实不是一会就能看懂的 但是我们可以讨论一下malloc的简单实现 在这之前 我们先来看一下虚拟内存空间 虚拟
  • 错误until the update interval of nexus-releases has elapsed or updates are forced

    错误 until the update interval of nexus releases has elapsed or updates are forced 之前是先往nexues本地库里上传了jar文件 从文件系统里传的 而不是从ne
  • 解决Port 8080 is already in use

    前言 在运行项目的时候报错显示端口号已被占用 如下图 解决方法 第一步 win R打开 输入cmd进入命令窗口 第二步 输入netstat ano回车 找到对应的进程PID为14472 第三步 输入命令tasklist回车 找到对应的进程名
  • 面向对象和面向过程思想概述

    面向过程的思想去实现一个功能的步骤 首先要做什么 怎么做 最后我们再代码体现 一步一步去实现 而具体的每一步都需要我 们去实现和操作 这些步骤相互调用和协作 实现我们的功能 每一个步骤我们都是参与者 并且 需要面对具体的每一个步骤和过程 这
  • 猿人学2023比赛题1~7题解之第一题

    前言 我从不回头看之我跟77的雨后小故事 这题有两个解法 分别是全局扣和找加密魔改点套库 方法一 全局扣 1 加密位置 2 确保在浏览器出值跟浏览器一样 1 先把全部代码拉下来 整体运行 发现会缺东西 这是发包的 直接删了就好 2 然后加密
  • mysql联合for update查询_sql的for update

    欢迎大家吐槽 oracle行级共享锁 通常是通过select from for update语句添加的 同时该方法也是我们用来手工锁定某些记录的主要方法 比如 当我们在查询某些记录的过程中 不希望其他用户对查询的记录进行更新操作 则可以发出
  • 【导航】RT-Thread 学习专栏目录 【快速跳转】

    本文是 矜辰所致 的 RT Thread 记录专栏的内容导航 结合自己的学习应用过程的总结记录 有基础理论 有与FreeRTOS的比较 有实际项目 有应用总结 目录 前言 一 环境篇 二 内核篇 三 设备和驱动篇 四 组件软件包篇 五 应用
  • [异步][jms][activeMq]如何做到重试机制不会导致一条消息被多次执行.

    淘宝海量存储之单机事务面临的问题及解决办法 http blog csdn net jiao fuyou article details 15499261 这篇文章讲的比较好 核心关键词 幂等 Message Queue ActiveMQ r
  • 前端HTML鼠标经过链接变换背景颜色

  • Win10关闭自带键盘的三种方法--亲测第三中命令方式有效(需要重启)

    Win10笔记本关闭自带键盘的方法 方法一 1 在Windows10系统桌面 右键点击桌面上的 此电脑 图标 在弹出菜单中选择 属性 菜单项 2 在打开的Windows系统属性窗口中 点击左侧边栏的 设备管理器 菜单项 3 这时会打开设备管
  • 二叉树及其遍历

    二叉树的定义 二叉二叉顾名思义 二叉树是每个节点最多有两个子树的树结构 二叉树的存储 二叉树的存储分为顺序存储和链式存储 顺序存储 深度为k的二叉树需要预留2 k 1 个存储单元 按编号顺序存储 遇空节点留空位 可以看到上面特别多的空节点
  • spring cloud alibaba使用

    文章目录 架构图 环境搭建 Nacos 下载以及配置 测试使用 界面一些功能 可配置项 nacos自带的ribbon负载均衡 OpenFegin 日志配置 设置超时时间 自定义拦截器 Nacos config 根据nacos上的配置文件获取
  • Spring6 框架学习

    Spring6 框架学习 1 Spring介绍 1 1 简介 2002年 Rod Jahnson 首次推出了 Spring 框架雏形interface21 框架 2004年3月24日 Spring 框架以 interface21 框架为基础
  • QObject: Cannot create children for a parent that is in a different thread

    一篇又臭又长的流水账 要看结论可以直接拉到最后 在一个项目中 需要使用串口接收外部的对射管状态 然后调用传感器 由于在之前的项目中 自制了一个带有UI的串口管理类 继承QDialog 最早在主线程中生成这个串口管理类 但是发现程序变得越来越
  • error Command failed with exit code 1.

    从传统JQuery转变到Node js最烦的就是一接手新项目就得npm install 浪费大量时间不说还总会报莫明其妙的错误 这次又遇到了一个error Command failed with exit code 1 因为它的相关处报的问
  • 100+国产大模型排行榜!部分超越ChatGPT-4

    国产大模型的发展速度惊人 至少说明在国内的显卡数量是足够多的 如果能集中资源 或许能快速跟进ChatGPT 不过 其中不少厂家号称已经超越ChatGPT 4 让人感到欣慰 觉得哪个好 评论区见
  • 【金融】新成立基金建仓时点、行业分布与市场行情关系探究

    需要进一步交流 获取数据和代码的同学欢迎私信奥 基于新成立基金建仓带入市场的巨量资金会推动市场行情这一逻辑 开展了一系列研究 首先提出了通过基金净值识别建仓行为 累计绝对值涨跌幅法 和通过基金 值识别建仓行为 法 的两种方法 在通过回顾历史
  • 一键修改分辨率bat_设置分辨率的批处理

    if computername name1 SETRES h800 v600 b32 f85 if computername name2 SETRES h1024 v768 b32 f85 if computername name3 SET
  • libevent服务端,单线程应用

    libevent版本 libevent 2 1 12 stable include