网络

网络链接错误分类及分析

2023-02-17  本文已影响0人  程序员札记

链接错误主要分几个阶段

  1. 链接建立期
    这个阶段主要是https 相关的错误,
  1. 链接期
  1. 关闭期

TCP 四次挥手

image.png

第一次挥手:客户端发送一个FIN为1,序列号随机生成的报文给服务器(假设序列号为M),进入FIN_WAIT_1状态;
第二次挥手:服务器收到这个报文之后,发送一个ACK为1,acknowledge number=M+1的应答报文给客户端,进入CLOSE_WAIT状态。此时客户端已经没有要发送的数据了,但仍可以接受服务器发来的数据。
第三次挥手:服务器发送一个FIN为1,序列号随机生成的报文给客户端(假设序列号为N),进入LAST_ACK状态;
第四次挥手:客户端收到服务器的FIN报文后,进入TIME_WAIT状态;接着发送一个ACK为1,acknowledge number=N+1给服务器;服务器收到后,确认acknowledge number是否为N+1,变为CLOSED状态,不再向客户端发送数据。客户端等待2*MSL(报文段最长寿命)时间后,也进入CLOSED状态。完成四次挥手。

tcp 报文

我们在日常工作中,经常要诊断网络连接中断问题, 那么就把这中问题归个类方便大家在处理日常的异常时候能够快速理解网络发生了什么。

Connection Reset

  1. Connection reset
    两个连接端, 一端退出,但退出时并未关闭该连接,另一端如果在从连接中读数据则抛出该异常。
    (Connection reset)
  2. connection reset by peer
    如果一端的Socket被关闭(或主动关闭,或因为异常退出而引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。

在TCP首部中有6个标志位,其中一个标志位为RST,用于“复位”的。无论何时一个报文 段发往基准的连接( referenced connection)出现错误,TCP都会发出一个复位报文段。如果双方需要继续建立连接,那么需要重新进行三次握手建立连接

出现RST的原因

RST攻击、干扰

上面简单介绍了RST标志位的作用,很容易想到这样一个场景,如果中间网络节点想要破坏一个tcp连接,那么它只要伪造成其中一方发送RST报文到另一方,即可让双方连接失效。

image

上图中,三次握手建立了tcp连接,由于是https连接,需要进行加密操作。这时,对方发送RST,双方并没有进行“四次挥手”,而是连接直接失效。这时客户端发起重拾,重新建立连接,但是很快又被“对方”重置了。和客户端连接tcp连接以及发送RST报文的,都不一定是真正的服务端,而可能是中间节点。我们使用HTTPS可以避免受到中间人攻击。对方无法使用中间人攻击,但是想阻止我们访问,所以可以通过RST来重置连接。

请求一个不存在的端口

当客户端访问服务端一个没有监听的端口时,服务端会发送RST报文。

image

如上图所示,客户端(192.168.2.192)访问了服务端(192.168.2.1)一个未监听的端口(9090),服务端发送了RST报文。

2.3 异常终止一个连接

终止一个连接的正常方式是一方发送 FIN。有时这也称为有序释放(orderly release),因为在所有排队数据都已发送之后才发送 FIN,正常情况下没有任何数据丢失。但也有可能发送一个复位报文段而不是 FIN来中途释放一个连接。有时称这为异常释放 (abortive release)。

image

上图,当客户端连接redis后,我们手动关闭redis服务,redis服务端会发送RST来强制终止一个连接。客户端通常收到这样的错误:Connection reset by peer,或者:远程主机强迫关闭了一个现有的连接。

半打开连接

如果一方已经关闭或异常终止连接而另一方却还不知道,我们将这样的TCP连接称为半打开(Half-Open)的。只要不打算在半打开 连接上传输数据,仍处于连接状态的一方就不会检测另一方已经出现异常。

排查思路

实际开发过程中,前面三个问题比较容易识别和解决。最困难的是最后一种半打开连接,原因往往很难发现。因为网络正常的情况下,都会通过正常关闭或者2.3的方式来关闭连接。现在客户端和服务端的网络非常复杂,有各种nat,代理,防火墙等设备,这些中间设备可能配置了一些安全策略,导致断开连接而不通知。

通过查询客户端日志,定位出现异常的时间。查询服务端日志,查询有无连接断开日志;查询连接是否存在;如果不存在,查询断开的时间。通过这些可以判断是不是由于半打开连接导致的问题。半打开连接一般是由于中间设备或者网络问题断开连接,而客户端不知道。解决方案就是找到对应的设备,配置连接,避免长连接断开。

具体的错误例子大家可以参照这篇博文 https://hongbshi.github.io/2020/12/13/nginx-connection-reset/

socket hang up

socket hang up 和connection reset 不一样,不是连接关闭。而是server 端主动连接拒绝。

  1. header 不符合规范。 比如发送headers头的时候,设置的Content-length不等于post过去的数据。
  2. 在使用request模块发起请求时,请求头信息中使用了Connection: keep-alive,持久连接。服务端不支持持久链接
  3. 服务端程序处理时间长,超出了默认请求超时时间,导致socket断开。 比如在springboot 里可以通过设置请求超时时间降低出现的概率spring.mvc.async.request-timeout=180000。 node.js提供的httpserver默认会设置超时时间为2分钟。请求超时就会被socket关闭掉。

具体问题可以看这个例子
http://mrdede.com/?p=3536

Cannot assign requested address. Socket TIME_WAIT

在2MSL 的时间内,client 端发出的请求超出了系统的端口数量。这样情况通常

  1. client 端没有主动关闭连接,尤其是在用Jersey client 时候没有主动调用response.getEntity方法
  2. client端就是在2msl 单位时间内发出了超过端口数量的请求。

通常这类问题都可以调整系统的sysctl的参数可以 net.ipv4.ip_local_port_range, 特别强调一下在k8s中,这个参数是安全参数。完全可以在spec 里修改。
https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/

connection ConnectTimeout & ReadTimeout

ConnectTimeout

指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间。
在java中,网络状况正常的情况下,例如使用HttpClient或者HttpURLConnetion连接时设置参数connectTimeout=5000即5秒,如果连接用时超过5秒就是抛出java.net.SocketException: connetct time out的异常。

ReadTimeout

指的是建立连接后从服务器读取到可用资源所用的时间。
在这里我们可以这样理解ReadTimeout:正常情况下,当我们发出请求时可以收到请求的结果,也就是页面上展示的内容,但是当网络状况很差的时候,就会出现页面上无法展示出内容的情况。另外当我们使用爬虫或者其他全自动的程序时,无法判断当前的网络状况是否良好,此时就有了ReadTimeout的用武之地了,通过设置ReadTimeout参数,例:ReadTimeout=5000,超过5秒没有读取到内容时,就认为此次读取不到内容并抛出Java.net.SocketException: read time out的异常。
根据上面关于ConnectTimeout和ReadTimeout的描述,在我们使用需要设置这两项参数的服务或程序时,应该对两项参数一起设置。

URLConnection connection = url.openConnection();
connection.setConnectTimeout(5000); // 5秒 连接主机的超时时间(单位:毫秒)
connection.setReadTimeout(5000); // 5秒 从主机读取数据的超时时间(单位:毫秒)

No more data to read from socket error

这个错误是DB 链接的错误。该异常通常是因为使用了连接池,当从连接池取得的connection失效或者超时的时候,使用这个连接来进行数据库操作就会抛出以上异常。

  1. 数据库意外重启后,原先的数据库连接池能自动废弃老的无用的链接,建立新的数据库链接
  2. 网络异常中断后,原先的建立的 tcp 链接,应该能进行自动切换。比如网站演习中的交换机重启会导致网络瞬断
  3. 分布式数据库中间件,比如 cobar 会定时的将空闲链接异常关闭,客户端会出现半开的空闲链接。

我们最近碰到的一个情况就是在istio 的模型中app 使用isito-proxy作为代理,进行数据库链接。app 的连接池有些链接一小时以内没有被使用过,结果被istio-proxy作为idle 的链接关闭了。当需要用到关闭的链接时候,就会出现No more data to read from socket error的错误。

java.io.IOException : Server returns HTTP response code

505

HTTP Version Not Supported 服务器不支持请求中所指明的HTTP版本。

  1. 通常是http1.0 去访问http1.1的服务器。
  2. URL 不规范。
    我的代码中有基于 HTML 的查询,并且在收到来自服务器的 505 响应时,一种特定类型的查询似乎会引发 IOExceptions。我和其他似乎有类似问题的人一起查看了 505 响应。显然 505 代表 HTTP 版本不匹配,但是当我将相同的查询 URL 复制到任何浏览器(尝试过 firefox、seamonkey 和 Opera)时,似乎没有问题。当同一个查询通过我的 Java 代码时,为什么我会遇到问题?
     private InputStream openURL(String urlName) throws IOException{    
    URL url = new URL(urlName);
    URLConnection urlConnection = url.openConnection();
    return urlConnection.getInputStream();

示例链接:http://www.uniprot.org/uniprot/?query=mnemonic%3aNUGM_HUMAN&format=tab&columns=id,entry%20name,reviewed,organism,length

Others

https://blog.csdn.net/wo240/article/details/80702935

javax.net.ssl.SSLHandshakeException

  1. 不同tls 版本
  2. 不支持cipher suite 导致

调试ssl握手 - Java程序添加vm参数:-Djavax.net.debug=all

查看报文

main, WRITE: TLSv1 Handshake, length = 163
main, READ: TLSv1.2 Alert, length = 2
main, RECV TLSv1 ALERT:  fatal, handshake_failure

发现ssl不一致ssl不一致,则更换jdk版本,或者修改服务器ssl加密版本


image.png

若结果为

RMI TCP Connection(3)-127.0.0.1, WRITE: TLSv1.2 Handshake, length = 143
RMI TCP Connection(3)-127.0.0.1, READ: TLSv1.2 Alert, length = 2
RMI TCP Connection(3)-127.0.0.1, RECV TLSv1.2 ALERT:  fatal, handshake_failure
RMI TCP Connection(3)-127.0.0.1, called closeSocket()
RMI TCP Connection(3)-127.0.0.1, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
RMI TCP Connection(3)-127.0.0.1, called close()
RMI TCP Connection(3)-127.0.0.1, called closeInternal(true)

此时ssl版本一致任然握手失败,则为加密套件不可用 此时加密密钥长度>128,jdk1.8默认支持证书密钥长度小于128


image.png

jdk1.8版本只要修改Java\jre\lib\security\java.security文件,启用crypto.policy。

crypto.policy=unlimited

javax.net.ssl.SSLException

缺少访问指定网址的证书

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provid

证书不被信任

总结

在service集群进行istio 迁移中,我们发现链接问题由于有个istio-proxy ,由以前A ->C 模型变成了 A->B-> C 的模型,同样的traffic 如果出现网络链接问题,可能和之前并不完全相同。 比如socket hung up ,以前不会出现,而是以connection reset 。但是由于B-> C的中断, 可能会体现为socket hung up 。

上一篇 下一篇

猜你喜欢

热点阅读