当前位置: 首页 > 编程日记 > 正文

基于libevent和unix domain socket的本地server

https://www.pacificsimplicity.ca/blog/libevent-echo-server-tutorial

根据这一篇写一个最简单的demo。然后开始写client。

client调优

client最初的代码如下:

 1 #include <sys/socket.h>
 2 #include <sys/un.h>
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <unistd.h>
 6 #include <sys/socket.h>
 7 #include <fcntl.h>
 8 #include <errno.h>
 9 
10 int main(int argc, char *argv[]) {
11     struct sockaddr_un addr;
12     int fd,rc;
13 
14     if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
15         perror("socket error");
16         exit(-1);
17     }
18 
19     const char *socket_path = "/tmp/mysocket";
20     memset(&addr, 0, sizeof(addr));
21     addr.sun_family = AF_UNIX;
22     strcpy(addr.sun_path, socket_path);
23 
24     if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
25         perror("connect error");
26         exit(-1);
27     }
28 
29     char sendbuf[8145] = {0};
30     rc = 8145;
31     
32     {
33         if (write(fd, sendbuf, rc) != rc) {
34             if (rc > 0) fprintf(stderr,"partial write");
35             else {
36                 perror("write error");
37                 exit(-1);
38             }
39         }        
40     }
41     
42 
43     char buf[1024] = {0};
44 
45     while ((rc = read(fd, buf, 1024)) > 0) {
46         buf[rc] = '\0';
47                 printf("%s\n", buf);
48     }
49 
50     close(fd);
51 
52     return 0;
53 }            

代码很简单,会发现有个问题,read这里会阻塞住不退出。

因为这是阻塞IO,读不到数据时会阻塞。有没办法可以知道服务端已经写完了呢?如果用非阻塞的是不是有不一样的返回码呢。又试了下非阻塞版。

 1 int val = fcntl(fd, F_GETFL, 0);
 2 fcntl(fd, F_SETFL, val|O_NONBLOCK);// 设置为非阻塞
 3 
 4 //...
 5 
 6 char buf[1024] = {0};
 7 while (true) {
 8         rc = read(fd, buf, 1024);
 9         if (rc > 0) {
10             buf[rc] = '\0';
11             printf("recv:%s\n", buf);
12         } else if (rc == 0) {
13             break;
14         } else if (rc < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)) {
15             //printf("errno %d\n", errno);
16             continue;
17         } else {
18             break;
19         }
20     }

这时就会出现一直跑到第15行这里,errno一直是EWOULDBLOCK/EAGAIN。

非阻塞模式下返回值 <0时并且 (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认为连接是正常的, 继续发送。

https://blog.csdn.net/qq_14821541/article/details/52028924

好吧,问题同样没有解决。实际上网络通信server端可能会出现很多情况,写得慢、网络慢或者server挂了等,为了鲁棒性,一个比较通用的策略就是超时。如果超了时间就直接退出。

1 struct timeval tv;
2 tv.tv_sec = 3;
3 tv.tv_usec = 0;
4 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));

用阻塞+超时,这样就可以正常退出了。不过还是没解决正常情况下的退出。

一个简单的思路就是服务端写完了数据,在数据的最终加上一个mark,标识已经写完了,client读到这个mark,就直接退出。

 1 #include <sys/socket.h>
 2 #include <sys/un.h>
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <unistd.h>
 6 #include <sys/socket.h>
 7 #include <fcntl.h>
 8 #include <errno.h>
 9 
10 int main(int argc, char *argv[]) {
11     struct sockaddr_un addr;
12     int fd,rc;
13 
14     if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
15         perror("socket error");
16         exit(-1);
17     }
18 
19     struct timeval tv;
20     tv.tv_sec = 3;
21     tv.tv_usec = 0;
22     setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
23 
24     const char *socket_path = "/tmp/mysocket";
25     memset(&addr, 0, sizeof(addr));
26     addr.sun_family = AF_UNIX;
27     strcpy(addr.sun_path, socket_path);
28 
29     if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
30         perror("connect error");
31         exit(-1);
32     }
33 
34     const char *end_mark = "$@%^&~";
35     int end_mark_len = strlen(end_mark);
36 
37     char sendbuf[8145] = {0};
38     rc = 8145;
39     memcpy(sendbuf + rc - end_mark_len, end_mark, end_mark_len);
40     
41     printf("%s %d\n", sendbuf, rc);
42 
43     {
44         if (write(fd, sendbuf, rc) != rc) {
45             if (rc > 0) fprintf(stderr,"partial write");
46             else {
47                 perror("write error");
48                 exit(-1);
49             }
50         }    
51     }
52     
53 
54     char buf[1024] = {0};    
55     while ((rc = read(fd, buf, 1024)) > 0) {
56         buf[rc] = '\0';
57         if (rc < end_mark_len) break;
58         if (strncmp(buf + rc - end_mark_len, end_mark, end_mark_len) == 0) {
59             printf("%s\n", buf);
60             break;
61         }
62     }
63 
64     close(fd);
65 
66     return 0;
67 }

server调优

https://www.pacificsimplicity.ca/blog/libevent-echo-server-tutorial

前面我们用bufferevent_setcb来设置回调函数,libevent的回调触发时机是这样的:

  1. 当输入缓冲区的数据大于或等于输入低水位时,读取回调就会被调用。默认情况下,输入低水位的值是 0,也就是说,只要 socket 变得可读,就会调用读取回调。
  2. 当输出缓冲区的数据小于或等于输出低水位时,写入回调就会被调用。默认情况下,输出低水位的值是 0,也就是说,只有当输出缓冲区的数据都发送完了,才会调用写入回调。因此,默认情况下的写入回调也可以理解成为 write complete callback。
  3. 当连接建立、连接关闭、连接超时或者连接发生错误时,则会调用事件回调。

参考:http://senlinzhan.github.io/2017/08/20/libevent-buffer/

http://www.wangafu.net/~nickm/libevent-book/Ref6_bufferevent.html

这里提到的demo,会有几个问题:

  1. client提前退出时,server端继续写数据会收到sigpipe信号,然后直接退出。
  2. echo_read_cb读回调,如果读的数据比较大,可能会触发多次,然而我们需要在数据结束时再同时处理,这里同样需要判断一下数据是否已经读取结束;
  3. 如果client提前退出,即使忽略了sigpipe信号 ,但是链接依旧不会关闭;

第一个问题,是因为连接建立,若某一端关闭连接,而另一端仍然向它写数据,第一次写数据后会收到RST响应,此后再写数据,内核将向进程发出SIGPIPE信号,通知进程此连接已经断开。而SIGPIPE信号的默认处理是终止程序。解决方案就是直接忽略SIGPIPE信号。

1 signal(SIGPIPE, SIG_IGN);

第二个问题,同样用一个mark来标记读取结束。这里用到evbuffer_peek来获取整个buffer内存而不是copy出来再查。

http://www.wangafu.net/~nickm/libevent-book/Ref7_evbuffer.html

 1 bool CheckReadFinished(struct evbuffer *input) {
 2     const int limit_vec = 10;
 3 
 4     struct evbuffer_iovec v[limit_vec];
 5     int n = evbuffer_peek(input, -1, NULL, v, limit_vec);
 6     if (n <= 0) {
 7         return false;
 8     }
 9 
10     int end_mark_len = strlen(end_mark);
11     for (unsigned i = n - 1; i >= 0; --i) {
12         size_t len = v[i].iov_len;
13         if (len >= end_mark_len) {
14             return strncmp((char*)(v[i].iov_base) + (len - end_mark_len), end_mark, end_mark_len) == 0;
15         } else {
16             if (strncmp((char*)(v[i].iov_base), end_mark + (end_mark_len - len), len) != 0) {
17                 return false;
18             }
19             end_mark_len -= len;
20         }
21     }
22     return false;
23 }

这里直接用了limit_vec来限制大小,如果超出buff大小就认为是错误的。

 1 static void echo_read_cb(struct bufferevent *bev, void *ctx) {
 2     struct evbuffer *input = bufferevent_get_input(bev);
 3     struct evbuffer *output = bufferevent_get_output(bev);
 4     
 5     if (CheckReadFinished(input)) {
 6         size_t len = evbuffer_get_length(input);
 7         printf("we got some data: %d\n", len);
 8         evbuffer_add_printf(output, end_mark);
 9     }
10 }

第三个问题,client异常退出是避免不了的,所以要有容错机制,同样是采用超时来容错。

 1 static void
 2 accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *address, int socklen, void *ctx) {
 3     evutil_make_socket_nonblocking(fd);
 4 
 5     struct event_base *base = evconnlistener_get_base(listener);
 6     struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
 7     bufferevent_setcb(bev, echo_read_cb, echo_write_cb, echo_event_cb, NULL);
 8 
 9     // 设置超时,然后断开链接
10     struct timeval read_tv = {2, 0}, write_tv = {3, 0};
11     bufferevent_set_timeouts(bev, &read_tv, &write_tv);
12 
13     bufferevent_enable(bev, EV_READ | EV_WRITE);
14 }

然后在BEV_EVENT_TIMEOUT事件触发时free掉evbuff。因为我们指定了BEV_OPT_CLOSE_ON_FREE,所以这时候就会断掉连接。

1 static void echo_event_cb(struct bufferevent *bev, short events, void *ctx) {
2     if (events & BEV_EVENT_ERROR)
3         perror("Error from bufferevent");
4     if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
5         printf("free event\n");
6         bufferevent_free(bev);
7     }
8 }

这里我们也可以看到,正常情况下,当client读取结束之后会close(fd),这时就会触发BEV_EVENT_EOF事件,同样是会关掉服务端的连接。

转载于:https://www.cnblogs.com/linyx/p/9966237.html

相关文章:

软件体系架构模式之一什么是软件架构模式

什么是软件架构模式 计划启动未开发的软件项目&#xff1f;然后选择正确的架构模式将对项目的结果起关键作用。选择市场上最流行或最新的技术并不总是意味着会带来最好的结果。但是&#xff0c;选择最合适的解决方案将为行之有效的问题和反复出现的问题提供可靠的解决方案。 …

HP 服务器 iLO 远程控制软件 介绍

iLO了解&#xff1a;iLO 是一组芯片&#xff0c;内部是vxworks的嵌入操作系统,在服务器的背后有一个标准RJ45口对外连接生产用交换机或者带外管理的交换机。iLO 全名是 Integrated Lights-out&#xff0c;它是惠普某些型号的服务器上集成的远程管理端口&#xff0c;它能够允许用…

【MATLAB】数据分析之数据插值

插值&#xff1a;求过已知有限个数据点的近似函数。 区别于拟合&#xff1a; 拟合&#xff1a;已知有限个数据点求近似函数&#xff0c;不要求过已知数据点&#xff0c;只要求在某种意义下它在这些点上的总偏差最小。 基本常用的插值方法&#xff1a;拉格朗日多项式插值&…

迈斯!啊呸~数学

1.数论 快速幂 int po(int x,int y) {int ans1;while(y){if(y%21)ans1ll*ans*x%p;x1ll*x*x%p;y/2;}return ans; } 乘法逆元&#xff08;保证模域p与求逆元的数互质&#xff09; po(a,p-2);//a为需要求逆元的数 扩展欧几里得&#xff08;exgcd&#xff09; #include<cstdio&g…

软件体系架构模式之二分层体系结构

分层体系结构模式是n层模式&#xff0c;其中组件被组织在水平层中。这是设计大多数软件的传统方法&#xff0c;并且具有独立性。这意味着所有组件都是互连的&#xff0c;但彼此之间不依赖。 图1&#xff1a;分层架构 在此体系结构中有四层&#xff0c;其中每一层在模块和其中的…

linux下mysql的root密码忘记解决方法

1&#xff0e;首先确认服务器出于安全的状态&#xff0c;最安全的状态是到服务器的Console上面操作&#xff0c;并且拔掉网线&#xff0c;或者可以使用--skip-networking限制只能从本地连接2&#xff0e;修改MySQL的登录设置&#xff1a; # vim /etc/my.cnf在[mysqld]的段中加上…

【Python】turtle库的小应用

心血来潮&#xff0c;哈哈哈&#xff0c;画的不好&#xff0c;请多见谅 大家如果想要尝试turtle库&#xff0c;可以借鉴&#xff1a; https://www.cnblogs.com/nowgood/p/turtle.html 导入库&#xff0c;我的pycharm里可以直接使用&#xff0c;哈哈哈&#xff0c;不行就pip…

[转]MySQL修改时区的方法小结

本文转自&#xff1a;https://www.cnblogs.com/mracale/p/6064447.html 这篇文章主要介绍了MySQL修改时区的方法,总结分析了三种常见的MySQL时区修改技巧,包括命令行模式、配置文件方式及代码方式,需要的朋友可以参考下 方法一&#xff1a;通过mysql命令行模式下动态修改 1.1 查…

swift轮播图代码

import UIKit private let CycleCellID "CycleCellID" class BannerCycleView: UIView { var layout : UICollectionViewFlowLayout! var collectionView :UICollectionView! var pageContol : UIPageControl? var cycleTimer : Timer? let timeInterval : TimeI…

软件体系架构模式之三微内核体系架构

当您的软件系统或产品包含许多与外部实体的集成点时&#xff0c;微内核体系结构模式将非常有用。最重要的是&#xff0c;您将无法准确判断出将来将哪些实体集成到您的系统中。可以将微内核架构模式识别为基于插件的模式。。也称为插件架构模式&#xff0c;它由两个主要组件组成…

【MATLAB】交互式绘图(ginput,gtext,zoom)

1、ginput指令&#xff1a; [x,y]ginput(n) %&#xff08;其功能是用鼠标从二维图形中获取n个点的数据坐标&#xff09; x-8*pi:pi/100:8*pi; y1sin(x); y24*sin(x/4); plot(x,y1,r-,x,y2,k-); legend(sin(x),4sin(x/4)); grid on; [x,y]ginput(8); %在下方命令窗…

【斗医】【18】Web应用开发20天

原创作品&#xff0c;允许转载&#xff0c;转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://qingkechina.blog.51cto.com/5552198/1544928 本文通过获取首页内容来讲解Web页面拖拽获取当前页数据&#xff0c;功能类似在google查看图…

数据库和缓存一致性的问题

经常看到有人问怎么解决数据库和缓存一致性的问题&#xff0c;这个问题我觉得是不要去解决。 如果你不信你先看我列的几种情况 假设 数据库一开始和缓存都是1元。 用户更新数据库的同时双写缓存。 1.双写不删 写库充值10元>>>返回成功 ----- 时间间隔 <<<…

【MATLAB】数据分析之求函数的极限(limit)

在MATLAB中采用limit函数求某个具体函数的极限&#xff0c;其调用格式如下&#xff1a; limit(expr,x,a)&#xff1a;当xa时&#xff0c;对函数expr求极限&#xff0c;返回值为函数极限。limit(expr)&#xff1a;默认当x0时&#xff0c;对函数expr求极限&#xff0c;返回值为函…

Spring Boot集成Swagger导入YApi@无界编程

接口APi开发现状 现在开发接口都要在类似YApi上写文档&#xff0c;这样方便不同的团队之间协作&#xff0c;同步更新接口&#xff0c;提高效率。 但是如果接口很多&#xff0c;你一个个手工在YApi去录入无疑效率很低。 如果是使用Spring Boot集成Swagger可以直接导入YApi非常…

【MATLAB】数据分析之函数数值积分

一、数值积分问题的数学表达 称为积分节点&#xff0c;称为求积系数。右端公式称为左端定积分的某个数值积分。MATLAB支持三重以下的积分运算。分别为&#xff1a; 在计算积分时&#xff0c;要求积分区间是确定的。 二、一元函数的数值积分 1、quad函数&#xff1a;采用遍历的…

c++对象切片

什么事对象切片&#xff1a;c在将一个派生类转换为基类的过程中&#xff0c;派生类的一部分将被基类接收不到&#xff0c;只能留下基类大小的对象。传值调用的切片&#xff1a;#include<iostream> using namespace std; class Base { public: virtual void func1() { cou…

javascript数据结构与算法-队列

定义 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;和栈一样&#xff0c;队列是一种操作受限制的线性表。进行插入操作的端称为…

Oracle学习笔记十三 触发器

2019独角兽企业重金招聘Python工程师标准>>> 简介 触发器是当特定事件出现时自动执行的存储过程&#xff0c;特定事件可以是执行更新的DML语句和DDL语句&#xff0c;触发器不能被显式调用。 触发器的功能&#xff1a; 1.自动生成数据 2.自定义复杂的安全权限 3.提供…

使用Notepad++比较文件的差异

有时候需要比较两个文件的差异部分&#xff0c;如果不在git里可以使用Notepad的插件。 在Notepad中安装Compare插件 打开NotePad&#xff0c;点击工具栏上的插件--Plugin Manager--Show Plugin Manager&#xff0c;选中Compare 然后安装。 比如下看两个线程堆栈的差异&#xf…

【MATLAB】将向量表示的多项式用字符串输出的通用函数示例

%创建一个名为pprintf的M文件 function s pprintf(p) %UNTITLED7 此处显示有关此函数的摘要 % 此处显示详细说明 if nargin>1error(Too much input arguments); end while(p(1)0)p1[]; end llength(p); if l0s0; elseif l1snum2str(p(1)); elseif l2s strcat(num2str(p(…

30秒或更短的时间内弄懂的有用CSS代码片段

今天无意间看到这个&#xff0c;真的很牛逼&#xff0c;记录下中文网&#xff1a; caibaojian.com/30-seconds-…转载于:https://juejin.im/post/5bf278a85188255e9b61a226

【MATLAB】数据分析之多项式及其函数

1、多项式的表达式和创建 MATLAB中使用一维向量来表示多项式。将多项式按照降幂次序存放在向量中。 多项式就可以用向量 [2 3 5 0 1]来表示。 2、多项式求根 >> p[1 2 1]p 1 2 1>> r roots(p)r -1-13、由根创建多项式 >> r [2;3]r 23>>…

SpringBoot自定义异常源码分析

SpringBoot自定义异常源码分析 在类上加ControllerAdvice注解&#xff0c;在方法上加ExceptionHandler注解&#xff0c;就可以在方法里处理相应的异常。 1.自定义异常处理类AdditionalExceptionHandler 挂RestControllerAdvice注解&#xff1a; ------------------ 2.自定义…

Android 应用性能优化-StrictMode(严格模式)

为什么80%的码农都做不了架构师&#xff1f;>>> UI线程如果被阻塞5秒的话&#xff0c;那么应用程序此时就会弹出ANR的对话框&#xff0c;ANR对应用程序来说是一个很严重的问题。 如何防止应用程序出现ANR&#xff0c;怎么分析查看导致ANR问题的原因&#xff1f; 我…

跨进程通信,到底用长连接还是短连接

一个完整的软件系统大多数情况下是由多个进程共同协作进行的&#xff0c;哪怕它们在同一台服务器上。所以&#xff0c;进程之间如何进行高效的通信至关重要。 单个应用程序单个数据库这套基础开发套餐我相信每个人都经历过&#xff0c;甚至在初期它们还有可能部署在同一台服务器…

Java的List和Json转换以及StringRedisTemplate往redis存泛型对象

List转Json List<User> user new ArrayList(); String str JSON.toJSONString(user); Json 转List方法一 List<User> user JSON.parseArray(json,User.class); 如果是泛型方法需要使用TypeReference Json 转List 方法二 String json "[{}]"; Li…

【MATLAB】符号数学计算(一):符号对象的创建

一、符号对象的创建命令 1、函数命令sym( ) variablesym(A,flag)Ssym(A,flag) 如果A是(不带单引号)是一个数字、数值矩阵或者数值表达式&#xff0c;则输出结果是将数值对象转换成的符号对象。 如果A(带单引号)是一个字符串&#xff0c;输出结果则是将字符串转换成的符号对象…

分布式session一致性问题

传统的网站结构&#xff08;并发量不大&#xff0c;没有session的不一致的问题。传统的网站结构图&#xff1a; **结论&#xff1a;**从图中可以看出在传统的网站结构中&#xff0c;所有的客户端都连接一个服务器&#xff0c;每个客户端发送过来的请求都被该服务器处理&#…

切换阿里云maven源解决maven中央仓库下载太慢卡顿的问题

maven默认官方的中央仓库有时候很慢下载jar甚至会卡住&#xff0c;那么你可以切换到阿里云的maven源 在本地的maven文件夹新建settings.xml <?xml version"1.0" encoding"UTF-8"?> <settings xmlns"http://maven.apache.org/SETTINGS/1.…