Error: socket hang up 报错分析与解决办法

nodejs做的韩国演唱会抢票软件项目报错如下:

{ err: { code: ‘ECONNRESET’ } }

一、说明一下nodejs项目的基础环境:

  1. 使用thinkjs开发框架
  2. 项目中使用request模块进行HTTP请求
  3. 项目中需要模拟客户端进行对某网站的连续的不同地址的起求
  4. request请求头中使用Connection: Keep-Alive,持久连接,即长连接

二、TCP抓包分析

因为没有多次大量抓包,所以只发现了以下几咱情况

服务器 – 服务端主机,即目标主机

客户机 – 客户端,即程序所在主机

1、测试一的TCP抓包数据分析

20200326155147

  1. No-834 socket(A)是接收到服务器发来的全部数据,
  2. No-835 socket(A)使用原socket发起新的请求(应用层发来发起新的HTTP请求【假定为URL-1】的命令)
  3. No-836 socket(A)收到服务器发来的[FIN, ACK],即要求断开连接(四次挥手之第一手)
  4. No-837 socket(A)客户端应答[ACK](四次挥手之第二手)
  5. No-838,839,840 socket(A)接835发送新数据请求
  6. No-841 socket(A)客户端向服务器发送[FIN,ACK](四次挥手之第三手)
  7. No-842 socket(B)客户端创建新的socket向服务器发起请求[SYN](应用层继续发来发起新的HTTP请求【URL-1】的命令)(三次握手之第一手)
  8. No-843 socket(B)服务器收到握手请求的应答[SYN, ACK](三次握手之第二手)
  9. No-844 socket(B)客户端对服务器应答的应答[ACK](三次握手之第三手)
  10. No-845 socket(B)客户端发送数据包
  11. 正常的话,No-835应该收到一个服务器发来的[FIN, ACK],即要求断开连接
  12. No-846,No-847 socket(A)服务器向客户端发送重建连接的要求[RST](据说是在紧急情况才会连着发送两个[RST],表示强调的意思,我只是好像在哪里听到过这种说法,所以不确定是不是这个意思,只是有这个一点点的印象)
  13. No-848 socket(B)服务器应答
  14. No-849 socket(B)客户端应答
  15. No-850 socket(B)请求数据完成
  16. No-851 socket(B)服务器返回数据
  17. No-852 socket(B)服务器返回数据
  18. No-864 socket(B)服务器返回数据
  19. No-865 socket(B)服务器发起断开请求[FIN, ACK](四次挥手之第一手)Seq=715 Ack=3059 Win=10240 Len=0
  20. No-866 socket(B)客户端应答[ ACK](四次挥手之第二手)
  21. No-867 socket(B)客户端同意断开的应答[FIN, ACK](四次挥手之第三手)
  22. No-872 socket(B)客户端在RTO时间内没有收到服务器应答,决定重新发送,即超时重传
  23. No-875 socket(B)同上
  24. No-876 socket(B)虚假超时重传[TCP Spurious Retransmission],seq=3058, ack=716 … [Reassembly error, protocol TCP: New fragment overlaps old data (retransmission?)] 提示:重新组装错误
  25. No-879 socket(B)服务器发来[RST],要求重建连接

No-876的发包应该是从3059开始发送,但是组装TCP数据后从3058位置开始发送了,这里就是错误(Error: socket hang up)的元凶,至于说它为什么组装错了,我就查不出根源了,只能分析到这里。

错误的开始,是在No-834,834之后应该收到服务器的一个[FIN, ACK]。据之前的包分析,此服务器接收到我发的模拟请求都是不允许长连接的。也可以说,之前的与服务器的交互都是在服务器回复完一条数据(一个HTTP请求的响应)后,都会紧跟一个断开请求。

但是,因为客户端一直是认为对方支持长连接的(因为请求头中使用了Connection: Keep-Alive,又因为没有收到断开请求),又因为这次客户端是出奇的快,在服务器的断开请求包之前给服务器做了应答(No-835)

以下是用人话说的TCP通信:

No-836 【我的手机号A】服务器说:断开吧,

No-837 【我的手机号A】我说:收到,等一下我看看有没有什么事

No-838 【我的手机号A】我说:给你来一个包

No-839 【我的手机号A】我说:再给你来一个包

No-840 【我的手机号A】我说:再再给你一个包,最后一个了

No-841 【我的手机号A】我说:行了哥们,可以断开了

No-842 【我的手机号B】我说:哥们,我换了个号,你在不(握手第一次)

No-843 【我的手机号B】服务器说:我在呀,兄弟你在不?(握手第二次)

No-844 【我的手机号B】我说:收到,你我都在就好(握手第三次)

No-845 【我的手机号B】我说:说正事儿了,给你来个正经的包(带应用层数据的)

No-846 【我的手机号A】服务器说:喂喂,兄弟,我感觉你掉线了呢,换个号吧

No-847 【我的手机号A】服务器说:喂喂,兄弟,我感觉你掉线了,换个手机号

其实我TM早换完了,你还在那跟我喊个六呀!

以下都是使用【我的手机号B】与服务器通信

No-848 服务器说:收到

No-849 我说:哥们,给你个分片的包,第一个

No-850 我说:哥们,分片第二个包,没了

No-851 服务器说:回复849的,收到

No-852 服务器说:回复850的,收到

No-864 服务器说:兄弟,我知道你要什么了,给你(返回HTML信息)

No-865 服务器说:兄弟,不唠了,再见,行不?(挥手一)

No-866 我说:收到了,稍等,我看看还有啥事没。(挥手二)

No-867 我说:哥们,没事了,那就这样吧(挥手三)

…现在的时间,我正等服务器向我发起挥手四

但是超时了,通过计算得出的RTO超时时间

No-872 我说:哥们,收到没,我说没事了。

No-875 我说:哥们,收到没,我说没事了。

No-876 我说:这会我就着急了,嗓子都哑了,说话都变声了(数据计算错了,应该是seq=3059,结果也不知道怎么的,seq=3058了)

No-879 服务器说:你说啥呢,听不懂,你换电话吧。

结果:应用层的socket报错:Error: socket hang up

测试二的TCP抓包数据分析

20200326214510

Time直到166秒时,结束生命。

看这组抓包,换成人话大致的意思就是我一个劲的请求握手,服务器就一个劲的拒绝握手,要求我重说,只要我重说5次,他不给我正经的应答,我就换电话(换端口)

以下30秒都在这种状态下通信,我实在是受不了了,30秒了你都没给我来个个正经的,不跟你(服务器)扯了,应用层就报错了。

从时间上来看,这个测试的流程可能是因为代理使用时长过了。项目中使用代理IP,买得是1分钟的,但是粗略看到,时长大约都在90秒左右,可能还会有更长的。

结果:应用层的socket报错:Error: socket hang up

三、从应用层的角度来说造成这个报错的原因

在使用request模块发起请求时,请求头信息中使用了Connection: keep-alive,持久连接。双方都可以发起持久连接的请求,但是双方也都可以拒绝持久连接。通俗说就是我可以请求你在我闲着的时候保持通话,但是你也可以在我闲着没声儿的时候直接挂我电话的意思,反之亦然。

另一种可能在request参数中增加了forever: true,如下:

在接收方不允许长连接的情况下,也同样是会报错的,Error: socket hang up

四、我的解决办法

  1. 检查request模块headers中是否存在Connection: Keep-Alive,如果存在,则删除此项;
  2. 检查request模块参数项中是否存在forever: true,如果存在,则删除此项;
  3. 如果与我前面说的环境大有不同,而且按照上面1、2两条查找修改后还是不能解决,那就联系我【织梦先生】,一起研究。

 

扩展:

wireshark抓包实用过滤表达式

wireshark抓包异常数据、异常描述信息

TCP常用网络抓包分析工具