在《网络编程》中提到TCP的三个特点:面向连接、可靠、流式服务。现在具体看一下其特点。
一、TCP固定头部结构
16位端口号:告知主机该报文段来自哪里(源端口号)要传给那个上层协议或应用程序(目的端口号)。
32位序号:一次TCP通信过程中某一个传输方向上的字节流的每个字节的编号。A发送给B的第一个报文段中,序号值被系统初始化为某个随机值ISN,后续在该方向上的TCP报文段的序号值被设置为ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。
32位确认号:另一方对TCP报文段的响应,其值是收到的TCP报文段的序号值加1。
4位头部长度:标识该TCP头部有多少个32位(即有多少行),因为4位最大能表示15,所以TCP头部最长是60字节。
6位标志:
URG标志:紧急指针是否有效。
ACK标志:确认号是否有效。称携带ACK标志的TCP报文段为确认报文段。
PSH标志:提示接收端应用程序应该立即从TCP接受缓冲区中读走数据,为接受后续数据腾出空间。TCP缓冲区 在网卡上,有大小限制,如果应用程序一直不读取就会一直在缓冲区,影响后续数据。
RST标志:表示要求对方重新建立连接。称携带RST标志的TCP报文段为复位报文段。
SYN标志:请求建立连接。称携带SYN标志的TCP报文段为同步报文段。
FIN标志:通知对方本段要关闭连接。称携带FIN标志的TCP报文段为结束报文段。
16位窗口大小:TCP流量控制的一个手段。这里窗口指接收通告窗口。告诉对方本端的TCP缓冲区还能接收多大字节的数据,避免发过来接收不了,数据丢失。
16位校验和:发送端填充,接收端对TCP报文段执行CRC算法检验TCP报文段在传输过程中是否损坏。不仅检验头部也检验数据部分。
16位紧急指针:它和序号值相加表示最后一位紧急数据的下一字节的序号,这部分为紧急数据先处理。
可以看出其可靠性:
1、32位序号:保证数据有序;
2、32位确认号与超时重传机制:保证报文段不会丢失;
3、6位标志:说明报文段的类型;
4、16位窗口大小:告诉对方本端TCP缓冲区还能容纳多少字节的数据,这样对方可以控制发送数据的速度,保证发送的数据流量不会超过容纳范围,也就保证了不会丢失数据;
5、校验和:保证数据的准确性。
补充一下TCP的超时重传机制。
TCP模块为每个TCP报文段都维护一个重传定时器,该定时器在TCP报文段第一次被发送时启动,如果超过时间没有收到对方确认,就重传并重置定时器。至于超时多长时间重传或重传多少次,就看TCP的重传策略。
每次重传超时时间都增加一倍,因为比如说第一次0.2s没有成功,那再传时0.2s也不会成功,所以增加一倍。当达到最大重传次数,底层的IP和ARP开始接管,直到客户端放弃连接。Linux有两个重要的内核参数与TCP超时重传相关:/proc/sys/net/ipv4/tcp_retries1(底层IP接管之前最少重传的次数,默认值为3)和/proc/sys/net/ipv4/tcp_retries2(放弃连接前TCP最多可以重传的次数默认值为15)。
二、面向连接
三次握手建立连接,四次挥手断开连接。其具体过程如下:
三次握手:客户端先发起连接,发送SYN(连接请求),然后服务器确认收到并同意建立连接,客户端在进行确认。
为什么客户端还要发送一次确认呢?
TCP有超时重传机制,客户端发送一次连接长时间没有收到确认,客户端会再次发送连接请求。如果第一次因为报文段丢失没有收到确认,第二次正常建立连接,数据传输后释放连接;如果第一次是因为报文段在某些网络节点长时间滞留,以致于连接释放以后才到达服务器,本来这是一个已经失效的报文段,但是服务器收到以后,会误认为这是客户端新发送的一次连接请求,服务器端发送确认报文段同意连接,如果没有第三次确认,连接就建立了。但是客户端并没有发送连接请求,也不会发送数据,服务器就会一直等待,浪费资源。也就是说,客户端再发送一次确认主要是为了防止已失效的连接请求报文段突然又传送到服务器端而产生错误。
TCP连接的释放客户端和服务器都可以先发起,这里以客户端发起断开连接请求为例。
客户端先向服务器发送连接释放报文段,并停止发送数据,主动关闭TCP连接。服务器收到连接释放报文段后立即发出确认,但是服务器到客户端方向的连接并未关闭,等服务器处理完所有数据后,给客户端发送连接释放报文段。客户端收到后发送确认信息。
这里有一点要注意,这是TCP连接还没有释放掉,必须经过2MSL(最长报文短寿命)时间后,大约2分钟才能结束。
为什么必须在TIME_WAIT状态等待2MSL的时间呢?
1、可靠地终止TCP连接。要保证最后一个ACK能够到达,如果服务器端没有没有收到最后一个ACK,就会超时重传,但是客户端已经结束了,服务器没有办法正常的终止连接。
2、保证让迟来的TCP报文段(已失效的报文段)有足够的时间被识别并丢弃。在TIME_WAIT状态时,无法使用该链接占用的端口号建立新的连接。如果没有TIME_WAIT状态,迟来的报文段(已经失效的报文段)会出现在下一次的连接中,但实际上是应该被丢弃的。
因为TCP报文段的最大生存时间是MSL,所以坚持2MSL时间可以确保网络上两个传输方向上尚未被收到的、迟到的TCP报文都已经消失(被中转路由丢弃)。因此必须在TIME_WAIT状态等待2MSL的时间。
三、状态转移
服务器
通过listen系统调用进入LISTEN状态,被动等待客户端连接;服务器一但监听到某个连接请求,将其放入listen创建的队列中,并向客户端发送带SYN标志的确认报文段。此时该连接处于SYN_RCVD状态。如果服务器成功收到客户端发回的确认报文段,则转移到ESTABLISHED状态(连接双方能够进行双向数据传输状态)。
当客户端主动请求关闭连接时,服务器通过返回确认报文段进入CLOSE_WAIT状态(服务器应用程序关闭连接),服务器检测到客户端关闭连接后,也会立即给客户端发送报文段关闭连接,进入LAST_ACK状态,等待客户端对结束报文段的最后一次确认。一旦完成连接就彻底关闭。
客户端
通过connect系统调用主动与服务器建立连接。connect首先给服务器发送一个同步报文段,使连接转移到SYN_SENT状态。connect调用失败返回到CLOSED状态。客户端成功收到服务器的同步报文段和确认,进入ESTABLISHED状态。
当客户端主动关闭时,向服务器发送一个结束报文段,同时进入FIN_WAIT1状态,收到服务器的确认报文段进入FIN_WAIT2状态。此时服务器发送结束报文段,客户端进入TIME_WAIT状态。
总的状态转移: