内容简介:今天我们继续介绍关于TCP异常情况的内容。本篇文章接着上一篇文章,前面分析了在连接过程中的各种异常,本篇文章重点介绍的是在数据传输过程中的各种异常,以及出现异常后的TCP连接的情况。为了便于大家理解本文,这里我们将上一篇文章的前半部分内容拷贝到这里,这部分内容主要介绍协议的内容。下图是网络通信中常见的架构,也就是CS架构。其中程序包括两部分,分别为客户端(Client)和服务端(Server)。当然,实际的环境还要复杂的多,在客户端和服务端之间可能有多种不同种类和数量的设备,这些设备都会增加网络通信的复杂性
今天我们继续介绍关于TCP异常情况的内容。本篇文章接着上一篇文章,前面分析了在连接过程中的各种异常,本篇文章重点介绍的是在数据传输过程中的各种异常,以及出现异常后的TCP连接的情况。为了便于大家理解本文,这里我们将上一篇文章的前半部分内容拷贝到这里,这部分内容主要介绍协议的内容。
下图是网络通信中常见的架构,也就是CS架构。其中程序包括两部分,分别为客户端(Client)和服务端(Server)。当然,实际的环境还要复杂的多,在客户端和服务端之间可能有多种不同种类和数量的设备,这些设备都会增加网络通信的复杂性。自然,也会增加程序开发容错的复杂性。
图1 基本架构
一、TCP的基本流程
在分析异常情况之前,我们先回忆一下TCP协议的基本逻辑。在客户端和服务端能够收发数据之前首先必需建立连接。连接的建立在协议层面也是通过收发数据包完成,只不过在用户层面就是客户端调用了一个connect函数。连接的过程俗称“三次握手”,具体流程如图2所示。
图2 TCP的三次握手流程
TCP连接的断开也是比较复杂的,需要经过所谓的“四次挥手”的流程。其原因是因为TCP是双工通信,分别需要从客户端和服务端2侧断开连接。
图3 TCP的四次挥手
另外一个比较重要的内容是TCP协议的状态转换,理解了这个内容,我们才能清楚出现各种异常情况下数据包的内容。
图4 TCP状态转换图
本文只是简单回忆一下TCP的基本流程,详细的内容可以参考本号之前的文章《 从TCP到Socket,彻底理解网络编程是怎么回事 》
二、异常情况分析
本文的分析假设连接已经建立,目前正在数据收发过程。这种情况下会出现各种异常,比如服务器宕机、进程crash或者进程被kill等等。下面我们分别介绍上述集中情况在TCP通信中的表现。
1. 服务进程crash
服务进程crash恐怕是我们日常生成环境最长遇到的情况,没有之一吧。那么在这种情况下客户端软件是什么反应?客户端是否可以感知?
我们分别写客户端和服务端的程序,客户端不断的发送数据,服务端接收数据。异常的模拟很简单,我们可以在服务端制造一个指针访问异常。此时服务端的程序就会crash掉。然后我们观察客户端的表现。先上结果,客户端的表现如下图所示。
可以看到客户端被reset掉了。我们在结合通过wireshark抓获的此时的数据报文内容,可以看到是一个RST报文。
回忆一下什么情况下服务端会发送RST报文。这种场景跟我们前文介绍的服务端没有监听的情况是类似的。由于服务端程序crash了,此时在操作系统中的套接字数据结构已经被释放,因此在协议层收到数据包的时候无法找到对应的套接字进行处理,于是发送了一个RST报文。
2. 手动杀死服务端应用
这也是线上比较常见的操作,当一个模块上线时,ops同学总是会先把旧的进程杀死,然后再启动新的进程。那么在这个过程中TCP连接又会发生了什么呢?是否会像上一种情况一样被RST呢?同样,我们先看一下结果,如下是客户端的情况。
从上面错误码来看是管道破裂,其实也就是连接被中断了。我们再看一下通过wireshark的抓包结果可以看出服务端发送了一个FIN报文,这个报文表示服务端发起了关闭的请求。而接下来的一个报文是客户端对该请求的确认。
所以,从上面客户端的错误码和报文情况我们可以知道,在kill进程时TCP协议是能够感知到的,并且发送的FIN报文。
我们再进一步的思考一下,为什么kill进程会有FIN呢?这个与前面crash的差异在哪?其实kill进程是通过 shell 想内核发送了SIGKILL或者SIGTERM,内核接收到该信号之后会进行相应的扫尾工作,因此可以看到服务端发送了FIN报文。
3. Server进程所在的主机关机
主机关机(这里指手动关机)的情况与进程被kill是类似的。这时因为在系统关闭时,init进程会给所有进程发送SIGTERM信号,等待一段时间(5~20秒),然后再给所有仍在运行的进程发送SIGKILL信号。当服务器进程死掉时,会关闭所有文件描述符。带来的影响和上面杀死server相同。
4. Server进程所在的主机宕机
这是我们线上另一种比较常见的状况。即使宕机是一个小概率事件,线上几千台服务器动不动一两台挂掉也是常有的事。这里挂掉其实包括2种情况,一种是内核panic,另外一种情况是出现了掉电。对于内核panic的情况不会像关机那样会预先杀死上面的进程,而是突然性的。那么此时我们的客户端准备给服务器端发送一个请求,它由write写入内核,由TCP作为一个报文发出,但因为主机已经挂掉,因此客户端无法收到ACK。于是客户端TCP持续重传分节,试图从服务器上接收一个ACK,然而服务器始终不能应答,重传数次之后,大约几分钟才停止,之后返回一个ETIMEDOUT错误。在这种情况下,如果我们调用的是同步发送接口,则在发送缓冲区慢的情况下会阻塞在这里,导致程序阻塞。
这个时间真的很长,对于某些应用这种长时间的卡顿是不能接受的。因此,需要一种手段处理这种情况,在套接字接口中可以通过SO_SNDTIMEO标记进行设置。但是有利也有弊,如果设置了该参数,可能会出现这的数据发送超时的情况,进而出现向服务端发送重复数据的情况,此时需要服务端做去重处理。
5. 服务器进程所在的主机宕机后重启
在客户端发出请求前,服务器端主机经历了宕机—重启的过程。当客户端TCP把分节发送到服务器端所在的主机,服务器端所在主机的TCP丢失了崩溃前所有连接信息,即TCP收到了一个根本不存在连接上(也就是我们前文介绍的查找不到socket数据结构)的报文,所以会响应一个RST分节。
至此,关于TCP协议中各种异常情况介绍完了,详细了解这些内容后对后续线上问题的分析和解决会有很大的帮助。当然,也有可能还有其它本文没有介绍到的异常情况,也欢迎大家留言交流。
以上所述就是小编给大家介绍的《从TCP协议到TCP通信的各种异常现象和分析(下)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Learn Python 3 the Hard Way
Zed A. Shaw / Addison / 2017-7-7 / USD 30.74
You Will Learn Python 3! Zed Shaw has perfected the world’s best system for learning Python 3. Follow it and you will succeed—just like the millions of beginners Zed has taught to date! You bring t......一起来看看 《Learn Python 3 the Hard Way》 这本书的介绍吧!