TCP特点
- 面向连接
- 字节流
- 可靠传输
关于TCP
- 使用TCP通信的双方必须先建立连接,然后才能开始数据的读写。
- 双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输。
- TCP连接是全双工的,双方的数据读写可以通过一个连接进行。完成数据交换之后,通信双方都必须断开连接以释放系统资源。
- TCP的连接是一对一的,所以基于广播和多播的应用程式不能使用TCP服务。(可以使用UDP)
TCP读写数据
- 当发送端应用程序连续执行多次写操作时,TCP模块先将这些数据放入TCP发送缓冲区中。
- 当TCP模块真正开始发送数据时,发送缓冲区中这些等待发送的数据可能被封装成一个或多个TCP报文段发出。
TCP模块发出的报文段的个数和应用程序执行的写操作次数之间没有固定的数量关系。 - 当接收端收到一个或多个TCP报文段后,TCP模块将他们携带的应用程序数据按照TCP报文段的序号依次放入TCP接收缓冲区中,并通知应用程序读取数据。
- 接收端应用程序可以一次性将TCP接收缓冲区的数据全部读出,也可以分多次读取,这取决于用于指定的应用程序读取缓冲区的大小。
应用程序执行的读操作次数和TCP模块接收到的TCP报文段个数之间也没有固定的数量关系。 - 字节流的概念
- 应用程序对数据的发送和接收是没有边界限制的。
- UDP不然,发送端应用程序每执行一次写操作,UDP模块就将其封装成一个UDP数据报并发送之。而接收端必须及时针对每一个UDP数据报执行读操作(recvfrom),否则就会丢包。
并且,用户没有指定足够的应用程序缓冲区来读取UDP数据,UDP数据将被截断。
TCP可靠性
- TCP采用发送应答机制,即发送端发送的每个TCP报文段都必须得到接收方的应答,才认为这个TCP报文段传输成功。
- TCP协议采用超时重传机制,发送端在发送出一个TCP报文段之后启动定时器,如果在定时时间内未收到应答,它将重发该报文段。
- TCP报文段最终是以IP数据报发送的,而IP数据报到达接收端可能乱序、重复,所以TCP协议还会对接收到的TCP报文段重排、整理,再交付给应用层。
TCP头部
- 16位端口号
- 源端口:告知主机该报文段从哪里来。
- 目的端口:报文段要传给哪个上层协议或应用程序。
- 32位序列号
- 一次TCP通信(从建立连接到断开)过程中某一个传输方向上的字节流的每个字节的编号。
- 32位确认号
- 用作对另一方发送来的TCP报文段的响应。
- 4位头部长度
- 标识该TCP头部有多少个32bit(4字节),因为4位长度最大能表示的是15,所以TCP头部最长是60个字节。
- 6位标志
- URG标志:紧急指针是否有效
- ACK标志:确认号是否有效。携带ACK标志的TCP报文称为确认报文段。
- PSH标志:接收端应用程序应该立即从TCP接收缓冲区读走数据,为接收后续数据腾出空间。
- RST标志:要求对方重新建立连接。携带RST报文段的TCP报文段称为复位报文段。
- SYN标志:请求建立一个连接。携带SYN标志的TCP报文段为同步报文段。
- FIN标志:通知对方本端要关闭连接了。携带FIN标志的TCP报文段称为结束报文段。
- 16位窗口大小
- TCP控制流量的一个手段。这里说的窗口,指的是接收通告窗口(RWND)。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。
- 16位校验和
- 由发送端填充,接收端对TCP报文段执行CRC算法以校验TCP报文段在传输过程中是否损坏。
- 16位紧急指针
- 是一个正的偏移量,它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。
- 可变长头部选项
- 最多包含40个字节,因为头部最长是60个字节。
- kind说明选项的类型。
- length(如果有的话),指定该选项的总长度。
- info(如果有的话),是选项的具体信息。
- 如下图
- 最多包含40个字节,因为头部最长是60个字节。
tcpdump
输出
- Flags[S]:表示报文包含SYN标志,是一个同步报文段。
- seq是序号值。
- win是接收通告窗口的大小。
- options是TCP选项
- mss是发送端通告的最大报文段长度。
- sackOK表示发送端支持并同意使用SACK选项。
- TS val是发送端的时间戳
- ecr是时间戳回显应答
- nop是一个空操作选项
- wscale指出发送端使用的窗口扩大因子为6
TCP的建立和关闭
- 第一个TCP报文包含了SYN标志,是同步报文,并且ISN序号值为535734930。
- 第二个TCP报文段也是同步报文,ISN在上一个报文的基础上加了1,表示确认。
- 第三个报文段是对第二个报文段的确认。
- 第四个报文段包含FIN标志,是一个结束报文段,要求关闭连接。
- 第五个报文段对第四个进行了确认。
- 并且发送了自己的结束报文段,第六个报文段。
- 第七个报文段是对第六个报文段的确认。
半关闭状态
- TCP连接是全双工的,所以它允许两个方向的数据传输被独立关闭。
也就是通信的一段可以发送结束报文段给对方,告诉它本端已经完成了数据的发送,但允许继续接收来自对方的数据,直到对方也发送结束报文段以关闭连接。
而这种状态,称为半关闭状态。
连接超时
- 对于服务器对客户端发送的同步报文段没有应答的这种情况,对TCP而言,它必然先进行重连(可能执行多次),如果重连仍然无效,则通知应用程序连接超时。
TCP状态转移
- TCP连接的任何一端在任何时刻都处于某种状态,当前状态可通过
netstat
命令查看。 - 状态转移图(粗虚线表示服务器连接的状态转移,粗实线表示客户端连接的状态转移,CLOSED是一个假想的起始点),如下:
服务器状态
- 服务器通过listen系统调用进入LISTEN状态,被动等待客户端连接,因此执行的是所谓被动打开。
- 服务器一旦监听到某个连接请求(如同步报文段),就将该连接放入内核等待队列中,并向客户端发送带SYN标志的确认报文段,此时该连接处于SYN_RCVD状态。
- 如果服务端成功接收客户端发送回的确认报文段,则该连接转移到ESTABLISHED状态。这个ESTABLISHED状态是连接双方能够进行双向数据传输的状态。
- 当客户端通过close或者shutdown系统调用向服务器发送结束报文段主动关闭连接时,服务器通过返回确认报文段使连接进入CLOSE_WAIT状态,意思是等待服务器应用程序关闭连接。
- 通常,服务器检测到客户端关闭连接后,也会立即给客户端发送一个结束报文段来关闭连接。这将转移到LAST_ACK状态,以等待客户端对结束报文的最后一次确认。一旦确认完成,连接就彻底关闭了。
客户端状态
- 客户端通过connect系统调用主动与服务器建立连接。connect系统调用首先给服务器发送一个同步报文段,使连接转移到SYN_SENT状态。(connect系统调用可能失败的原因:)
- 连接的目标端口不存在(或未被任何进程监听),或者该端口仍被处于TIME_WAIT状态的连接锁占用,则服务端将给客户端发送一个复位报文段,connect调用失败。
- 目标端口存在,但connect在超时时间内未收到服务器的确认报文段,connect调用失败。
- connect调用失败将使连接返回最初的CLOSED状态。如果客户端成功收到服务器的同步报文段和确认,则connect调用成功,连接状态转移到ESTABLISHED状态。
- 当客户端主动关闭连接时,客户端会向服务器发送一个结束报文段,同时进入FIN_WAIT_1状态。若此时客户端收到服务器用于确认的确认报文段,则连接转移到FIN_WAIT_2状态。
当客户端处于FIN_WAIT_2状态,服务器处于CLOSE_WAIT状态,是可能发送半关闭的状态。 - 处于FIN_WAIT_2状态的客户端,需要等待服务器发送结束报文段,才能转移至TIME_WAIT状态,否则它将一直停留在这个状态。(目的就是为了在半关闭的状态下接收数据,不然长时间停留在这个FIN_WAIT_2状态没有益处)
- 连接停留在FIN_WAIT_2状态的情况可能发生:客户端执行半关闭后,未等服务器关闭连接就强行退出了。此时客户端连接将由内核来接管,可称之孤儿连接。
Linux为了防止孤儿连接长时间停留在内核中,定义了两个内核变量,/proc/sys/net/ipv4/tcp_max_)orphans
和/proc/sys/net/ipv4/tcp_fin_timeout
,前者指定了内核能接管的孤儿连接数目,后者指定了孤儿连接在内核中生存的时间。
TIME_WAIT状态
- 客户端在收到服务器的结束报文段之后,并没有直接进入CLOSED状态,而是转移到TIME_WAIT状态。在这个状态,客户端要等待一段长为2MSL(报文段最大生存时间)的时间,才能完全关闭。
MSL是TCP报文段在网络中的最大生存时间,标准文档RFC1122的建议值是2分钟。 - TIME_WAIT存在的原因:
- 可靠的终止TCP连接。
- 保证迟来的TCP报文段有足够的时间被识别并丢弃。
- 关于原因1:
- 如果用于确认服务器结束报文段6的TCP报文段7丢失了,那么服务器会重发这个结束报文段。因此客户端需要停留在某个状态以处理重复收到的结束报文段。否则,客户端将以复位报文段来回应服务器,服务器则认为这是一个错误,因为它期望的是一个像TCP报文段7那样的确认报文段。
- 在Linux系统上,一个TCP端口不能被同时打开多次(两次或以上)。当一个TCP连接处于TIME_WAIT状态时,我们将无法立即使用该连接占用着的端口来建立一个和刚关闭的连接相似的连接(相似是因为有相同的IP地址、端口号)。
这个新的连接称为原来连接的化身。而新的化身可能接收到属于原来连接的、携带应用程序数据的TCP报文段(比如迟到的报文段),这种情况是不应该发生的。这也是TIME_WAIT存在的第二个原因。
- TCP报文段的最大生存时间是MSL,所以坚持2MSL的时间的TIME_WAIT状态能够确保网络上两个传输方向上尚未被接收到、迟到的TCP报文段都已经消失(被中转路由器丢弃)。
因此,一个连接的新的化身,可以在2MSL时间之后安全的建立,而绝对不会接收到属于原来连接的应用程序数据,这就是TIME_WAIT状态要持续2MSL时间的原因。
复位报文段
- 在某些特殊的情况,TCP连接的一端会向另一端发送携带RST标志的报文段,即复位报文段,以通知对方关闭连接或重新建立连接。
- 收到复位报文段的一端应该关闭连接或重新连接,而不能回应这个复位报文段。
异常终止连接
- TCP提供了异常终止一个连接的方法,即给对方发送一个复位报文段。一旦发送了复位报文段,发送端所有排队等待发送的数据都将被丢弃。
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Shell 语法记述 第一篇09/04
- ♥ Linux 线程概述&&创建03/31
- ♥ Linux_命令大全 压缩备份03/16
- ♥ Linux fork&&守护进程03/30
- ♥ Linux 高性能服务器编程:高性能服务器架构一12/05
- ♥ Vim编辑器的操作03/17