EISCONN的故事

在这春风明媚的日子里,有位T同学很苦恼。忙碌了一整天,有个BUG愣是定位不出来。简单描述呢,现象是这样子的:

第一次处理是正常的,但是后续的处理就是报错。sendto()调用错误码是 EISCONN(已被连接)。

忧伤的问题

当然,代码BUG的范围也很快确定了,就是新加入的statsd-client-cpp工具库里。代码量不到两百行,失败的地方就是在sendto()的执行里(代码看这儿)。一看错误码(EISCONN,比较少见),说“socket已经被连接”——但咱这明明是UDP协议啊,无连接无connect的!

T同学咨询了周围的大师们,翻阅了《Unix网络编程》(没错,这书在上次的故事里也出场了!),里面说:

sendto()函数的执行流大约是这样子的:

  1. 连接套接字;
  2. 输出数据报文;
  3. 断开套接字连接;

无语了。按照圣经里说法,连接都是被断开了的啊!还怎么会报错“已被连接”?!!

失败的补救

T同学比较耿直,对付BUG比较直接粗暴:报啥错误就解决啥错误!思路有俩:

  1. 人肉断开它!
    这个实际上是行不通的(只能整个儿socket关闭销毁掉,不能只断开)

  2. 连接了也继续发!
    虽然一般情况下UDP协议的程序都是不管连接直接发sendto()的,但是先连接后再write()也是可行的。

于是按照方法2搞起!然而,实践证明这是不给力的,UDP对端根本没有收到任何数据!

正确的思路

这种头痛医头、瞎试几次的做法,本质上就不能算正确的方法。我们的思路应该是:

  1. 收集现象
  2. 分析原因
  3. 验证方案
  4. 解决问题

上面折腾了这么久,基本上还是只有一个错误码和代码出错的位置,现象数据太少了。T同学冷静下来后,开始祭出大杀器strace工具程序!
strace程序能够捕抓系统调用(system call),并把这些调用接口的时间、参数、返回值、耗时等等都记录下来;输出信息可读性相当的高(比起gdb爽多了),能反映出系统最底层的运行状况,是后台开发程序员的居家旅行必备的强(zhuang)大(bi)工具。

神奇的零

具体strace的过程就不多说了。调整进程数量、执行strace、查看log、研究执行状况:

strace32 -s 10086 -o /tmp/Strace.log -tt -p $(pidof -s get_api_key)

强大万能的strace

终于发现一个关键现象:每次的连接socket()返回值都是0!
这里稍微解释一下,为什么零值会是很特殊:socket()返回值表示的是文件描述符;在POSIX标准里,有三个特殊的文件描述符值0、1、2,分别是STDIN(标准输入)、STDOUT(标准输出)、STDERR(标准错误)。所以默认情况下,零值都会作为STDIN(标准输入)使用;只有当程序主动关闭了STDIN时,系统才会分配0值给socket()使用。

所以这时候思路有两个了:

  1. 假如系统分配的0值是正确的,那么得寻找0值后来被“弄坏”(已连接)的出现地方;
  2. 假如系统分配的0值是错误的,那么一定是某处BUG“失误”把STDIN释放掉了;

这种“辩证”的思路,让我忽然想起了《撸撸姐的超本格事件簿》里的给出各种伪解答搞笑助手:每次破案分析都至少有正反两个思路,看起来毫无盲点,但总是被撸撸姐指出第三种情况!哈哈哈!!!不过在现实场景里,正反两面都深入思考的做法,一般帮助很大的。

谁弄坏了0

所以0是被谁弄坏的?怎么弄坏的?

为了深入探究这个问题,得先了解一下程序运行的环境。这个工具库之前已经在现网的服务器里跑了很久,也有两个简约的单元测试。都没有发现过问题。这次是在往CGI接口里使用,运行环境是QZHTTP+FastCGI。

所以正常情况下,STDIN应该是CGI与qzhttp收包程序之间的连接,用来传递HTTP请求报文。而分析到的一个现象是,CGI程序的功能完全正常!也就是说程序之间传递数据(STDIN)是正常的……等等,STDIN(0值)不是分配给我们用吗??为毛还不影响功能啊啊啊???

秘诀就是——dup2(a, b)!!这个函数能够把一个文件描述符的信息拷贝到另一个描述符里!所以文章最初出现的“EISCONN”错误的原因是:

原本UDP无连接的socket,被人用dup2()“篡改”了,于是就变成了另外的文件描述符,出现了“已连接”的状态。

写个小程序验证了一下,果然能够随便拷贝到STDIN描述符里!!!而再次strace抓包查看,也发现了明显的dup调用:

dup2调用证据

可以清晰看到对STDIN、STDOUT都做了dup2()操作!!!太邪恶了!!!火速在万能的StackOverflow.com上也找到了一篇关于0值的问答,里面有人提到了如何解决这个问题!那就是把0、1、2预留下来给系统!老子不陪你们玩零了!!(傲娇地离去!)

另一个思路

等等,刚才怎么这么快就进入了“解决问题”的节奏??? T同学的案例虽然因为屏幕不够长被滚动掉了,但是我们要分析所有的现象找到原因啊!

于是进入刚才的第二个思路:是谁把STDIN给关闭了??

STDIN被close()的证据

深入strace的log,发现close(0)首次出现的位置没有太特别,距离后来socket()和sendto()调用很遥远,暂时发现不了什么(后来细想,考虑时序其实是个误导)。

不过,因为是新引入的库导致了这个问题,基本上猜测要么就是库代码有BUG,要么是库和QZHTTP不兼容。重新阅读代码中与close()有关的片段,尝试梳理一下思路.果然发现一个问题:

代码中的close()

d->sock是个关键的值,而且没有初始化!一般来说变量没有初始化,会是一个随机的值;但是我们的场景里,StatsdClient对象是一个单体实例,以static限定符实现的——所以是有初始值0的——刚好触发了BUG。

解决

再次核对代码逻辑和strace的log,脑补了一下执行的流程,基本上就确认BUG的原因就是这里了。

所以啊,在构造函数里增加一个初始化 d->sock = -1; 问题解决了。

写到这里的时候,忽然想起上两周给赵总用这个工具库,他也出现了一些诡异问题。当时他的现象是:随机出现accept错误。现在看来BUG原因都是一个,只不过赵总的使用方式是临时变量,没初始化的值就是随机值,因此偶然触发故障(他当时引起的是accept失败,也是描述符问题)。而这次是必现的问题,定位起来轻松一些。

思考

所以,要保持良好的代码编写习惯。顺手初始化了,就不会有这么纠结的问题了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432786.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言内循环,C语言循环控制语句

C语言循环控制语句是一个基于C语言的编程语句,该语句主要有while循环语句、do-while循环语句和for循环语句来实现循环结构。中文名C语言循环控制语句类 别while循环语句,do-while语句等目 地实现循环结构属 于计算机领域归 类编程语句基 …

同步,异步,多线程,你怎么看?

同步,异步,多线程,你怎么看? 原文:同步,异步,多线程,你怎么看?[原创]讲解同步与异步的帖子多如牛毛,个人的理解,简单地说同步就是串行,异步就是并…

openssl-1.0.0b - libssl 移植到ARM Linux

开发环境: ubuntu 10.04 arm-linux-gcc version 4.4.1 目标环境 友善之臂mini6410 linux-2.6.36 移植步骤 1.至官网下载最新的openssl,解压缩 2.cd进入openssl-1.0.0b目录 3.执行./Confiugre linux-elf-arm&#xff0c…

html仿qq最小化怎么实现,JS仿QQ好友列表展开、收缩功能(第一篇)

JS仿QQ好友列表展开、收缩功能(第一篇)发布时间:2020-10-17 14:20:03来源:脚本之家阅读:96作者:erdouzhang效果图如下所示:html:我的好友张三李四...企业好友小明小红...黑名单哈哈...css:ul,h3 {padding: …

Visual Studio 选择相同变量高亮

前段时间一直在使用matlab,今天需要使用vs2008,而用惯了matlab,习惯了其中一项选中变量高亮的设置,突然回来使用VS,感到各种不适应,顿时想到了一个词:矫情 呵呵,于是在网上找各种插件…

html是前段还是后端,javascript属于前端还是后端?

JavaScript是一种属于网络的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的。前端开发和后端开发的区别在于&#xff1a…

Linux查看设置系统时区

关于时区的概念,其实初中地理课已经涉及,很多人都多少了解一些,可能只是细节搞不太清楚。为什么会将地球分为不同时区呢?因为地球总是自西向东自转,东边总比西边先看到太阳,东边的时间也总比西边的早。东边…

SQL基础问题整理

在程序中,数据库操作是必不可少的部分,所以我们要备足数据库相关知识才能去应付程序中出现的种种问题。基于此,我特地在国外网站、博客上整理了一些问题,并附带了答案和解释、参考。为了保证“原汁原味”,我就保留了英…

腾讯或联姻优酷,微信嫁女模式引发互联网通婚潮流

据消息称:腾讯在前段时间联姻京东后有可能继续做甩手掌柜,这回要甩的是腾讯视频。 前几年,最火爆的电商业务除了电商外,再者一个就是视频业务了。 不知道大家还记得优酷当时的崛起之初的情景么?我来罗列一下: 1、 大…

[翻译] 学习iOS开发的建议:如何从菜鸟到专家

[文章原地址] http://mobile.tutsplus.com/tutorials/iphone/ios-quick-tip-from-novice-to-expert/ 翻译有误之处请勿见笑,本人将在文章的部分地方添加注释,并根据需求增减文章内容,在此对原作者辛勤劳作表示感谢 iOS Quick Tip: From Novi…

[nodejs]国内npm安装nodejs modules失败的几个解决方案

使用npm安装node模块时经常有卡住安装失败的情况,如图所示。原因在于npm服务器在美国,还有就是某强大的防火墙作用。这样的问题导致很多新手放弃使用node,几乎每天都有新手再问这个问题。现在分享一下解决这个问题的解决方案 1.可以通过一劳永…

java 合并单元格 把数据合并没了_合并单元格

合并单元格同样也是在表格中进行,关于合并单元格我们需要了解的两个概念:colspan 合并列,rowspan 合并行。colspan(跨列)合并列:colspan属性常用在 td 中,用来指定单元格横向跨越的列数。比如:将下面表格的…

一步步学习微软InfoPath2010和SP2010--第九章节--使用SharePoint用户配置文件Web service(2)--在事件注册表单上创建表单加载规则...

下面练习中,你将添加表单加载规则,将四个文本框域和图片控件与用户配置文件web service连接。当使用用户配置文件web service时,你需要将控件和来自web service合适的域绑定。这个过程需要用户配置文件架构的导航和筛选,来抽取合适…

光耦驱动单向可控硅_华越国际一文带路:可控硅触发设计技巧

序可控硅(Silicon Controlled Rectifier,简称SCR),是可控硅整流元件的简称,是一种具有三个PN结的四层结构的大功率半导体器件,亦称为晶闸管。具有体积小、结构相对简单、功能强等特点,是比较常用的半导体器件之一。家用电器中的调…

Servlet JSP系列文章总结

前言 谢谢大家的捧场,真心感谢我的阅读者。 all 下一期,重点在 数据结构和算法 ,希望给大家带来开心。已经出了几篇,大家爱读就是我的开心。 Servlet & JSP系列总结 博客,呵呵!很开心,认识…

一般通话记录能保存多少条_鸡蛋放冰箱,能保存多少天?正确保存方法是什么?...

鸡蛋是我们经常吃的食物,很多家庭都会经常的买鸡蛋吃。相信大家买回来鸡蛋之后,普遍都是把鸡蛋放入冰箱里,什么时候想吃什么时候拿一个。但是大家可能不知道的是,鸡蛋就算是放在冰箱里保存,也是有保质期的,…

基本矩阵运算的Java实现

基本矩阵运算的Java实现 分类: 图像处理2012-09-18 10:36 2537人阅读 评论(3) 收藏 举报javamatrixparametersstringclassnull一: 矩阵的加法与减法 规则:矩阵的加法与减法要求两个矩阵的行列完全相等,方可以完成两个矩阵的之间的…

json返回页面读取data里的值都是object_【一】尤大神都说Vite香,让我来手把手分析Vite原理...

戳蓝字"前端优选"关注我们哦!一.什么是Vite?法语Vite(轻量,轻快)vite 是一个基于 Vue3单文件组件的非打包开发服务器,它做到了本地快速开发启动、实现按需编译、不再等待整个应用编译完成的功能作用。对于Vite的描述&am…

e记法 python 底数_备战python二级

明天考试去,滚吧提醒与分值:1*40(选择)5*3(填空)101520比如今年的一个题目是要求随机抽一个手机品牌,这道题目的关键点在于你要使用seed()函数覆盖原来的给定的种子seed(1),因为要求…

V210 UART TX 流程

1. 虽然V210的uart驱动是平台总线设备驱动模型,但实际上他还是以字符设备驱动存在,那么分析他的发送流程, 首先找到他的file_operations的write函数 drivers/char/tty_io.c tty_write(struct file *file, const char __user *buf, size_t cou…