引:TCP作为传输层的一大协议,虽然没有UDP简单,但是胜在可靠。
TCP认识
TCP(传输控制协议) 是一种面向连接、可靠的、基于字节流的传输层协议。TCP在传送数据之前会先相互发送一些预备报文段协商一些参数,比如序号等等,TCP将用户数据打包成报文段,发送数据后启动一个定时器,另一端对收到的数据进行确认,对失序的数据重新排序,丢弃重复数据,TCP提供端到端的流量控制,并计算和验证一个强制性的端到端校验和。
TCP特点
- TCP是面向连接的,通信前需要建立连接,通信结束需要释放连接。
- TCP提供可靠交付服务,可靠指的是:TCP发送的数据无重复、无丢失、无错误、与发送端顺序一致。
- TCP是面向字节流的,面向字节流指的是:TCP以字节为单位。虽然传输的过程中数据被划分成一个个数据报,但这只是为了方便传输,接收端最终接受到的数据将与发送端的数据一模一样。
- TCP提供全双工通信,全双工通信指的是:TCP的两端既可以作为发送端,也可以作为接收端。
- 一条TCP连接的两端只能有两个端点,TCP只能提供点到点的通信,而UDP可以任意方式的通信。
TCP首部
- 16位端口号:包含源端口和目的端口,客户端通常使用系统自动选择的临时端口号,而服务器则使用自主定义端口号.
- 32位序号:一次TCP通信过程中一个传输方向上的字节流的每个字节的编号,例如:A发送给B的第一个TCP报文段中,序号值被系统初始化为某个随机值ISN,那么在该传输方向上,后续的TCP报文段中序号值将被系统设置为ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。
- 32位的确认号: 用作对另外一方发送来的TCP报文段的响应,其值是收到的TCP报文段的序号值加1。
- 4位头部长度: 标识TCP头部有多少个32bit(4字节),最大60字节.
- 6位标志位:
- URG:表示紧急指针是否有效.
- ACK:表示确认号是否有效,我们称携带ACK标志的TCP报文段为确认报文段.
- PSH:提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接收数据腾出空间.
- RST:表示要求对方重新建立连接(复位报文段)
- SYN:表示请求建立一个连接(同步报文段);
- FIN:表示通知对方本端要关闭连接了(结束报文段);
- 16位窗口大小:TCP流量的一个控制手段,,它告知是对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度.
- 16位校验和:由发送端填充,接收端对TCP报文段执行CRC算法(循环冗余码检验,与UDP一样),检验TCP头部在传输过程中是否损坏,不仅包括TCP头部,也包括数据部分。
- 16位紧急指针:是一个正的偏移量.它和序号字段的值相加表示最后一个紧急数据的下一个字节的序号.
- 选项字段,上述字段都是每个TCP头部必须要有的,而选项字段是可选的,且长度可变,最长40字节。 最常用的选项字段为MMS:最大报文长度。
TCP连接建立(三次握手)
TCP协议中,主动发起请求的一端称为客户端,被动连接的一端称为服务端。不管是客户端还是服务端,TCP连接建立完后都能发送和接收数据。
- 握手前:服务器和客户端都为CLOSED状态,在通信开始前,双方都得创建各自的传输控制块(TCB)。服务器创建完TCB后遍进入LISTEN状态,此时准备接收客户端发来的连接请求。
- 第一次握手:客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,(表示该报文段为连接请求报文) seq=x(x为本次TCP通信的字节流的初始序号)。请求发送后,客户端便进入SYN-SENT状态。
- 第二次握手 :服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,(表示该报文段为连接同意的应答报文) seq=y,(表示服务端作为发送者时,发送字节流的初始序号) ack=x+1(表示服务端希望下一个数据报发送序号从x+1开始的字节)。
该应答发送完成后便进入SYN-RCVD状态。 - 第三次握手 :当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。
该报文段的头部为:ACK=1,seq=x+1,ack=y+1。
客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!
PS:为什么连接建立需要三次握手,而不是两次握手?
防止失效的连接请求报文段被服务端接收,从而产生错误,失效的连接请求指的是:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是失效的。
若建立连接只需两次握手,服务端在收到连接请求后就进入ESTABLISHED状态。此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,此时,如果那个失效的连接请求抵达了服务端,但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。
TCP连接释放(四次握手)
TCP连接发送数据是双向的,因此在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。
- 第一次握手 :若A认为数据发送完成,则它需要向B发送连接释放请求。该请求只有报文头,头中携带的主要参数为:FIN=1,(表示该报文段是一个连接释放请求) seq=u(u-1是A向B发送的最后一个字节的序号)。此时,A将进入FIN-WAIT-1状态。
- 第二次握手 :B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。此时B进入CLOSE-WAIT状态,并向A发送连接释放的应答,其报文头包含: ACK=1,(表示应答) seq=v (v-1是B向A发送的最后一个字节的序号),ack=u+1(表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节)。A收到该应答,进入FIN-WAIT-2状态,等待B发送连接释放请求。第二次挥手完成后,A到B方向的连接已经释放,B不会再接收数据,A也不会再发送数据。但B到A方向的连接仍然存在,B可以继续向A发送数据。
- 第三次握手 :当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。B便进入LAST-ACK状态。
- 第四次握手 :A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。该状态会持续2MSL(报文最大生存时间)时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。
TCP的可靠传输
TCP采用了校验和、连接管理、序列号、确认应答,重发控制、滑动窗口协议(连续ARQ协议、流控制、拥塞控制)等机制来保证它的可靠性。
校验和
TCP和UDP校验和的方式一致,可以参照另一篇博客——UDP协议
连接管理
其实就是利用建立连接时的三次握手来提高可靠性。
通过序列号与确认应答
在TCP中,当发送端的数据到达接收主机时,接收端主机会返回一个收到消息的通知,这个消息叫做确认应答(ACK),比如客户端发送[数据(1-1000)],那么服务端将会回应[确认应答(下一个数1001)]。在一定时间内没有等到确认应答,发送端会认为数据已经丢失,并进行重发。
滑动窗口协议(连续ARQ协议)
概念
先简单说说ARQ(Automatic Repeat reQuest)协议(自动重传请求协议): 当请求失败时它会自动重传,直到请求被正确接收为止。这种机制保证了每个分组都能被正确接收。
再看连续ARQ协议:在ARQ协议发送者每次只能发送一个分组,在应答到来前必须等待。而连续ARQ协议的发送者拥有一个发送窗口,发送者可以在没有得到应答的情况下连续发送窗口中的分组。
发送窗口
发送窗口的大小由接收窗口的剩余大小决定。接收者会把当前接收窗口的剩余大小写入应答TCP报文段的头部,发送者收到应答后根据该值和当前网络拥塞情况设置发送窗口的大小。发送窗口的大小是不断变化的。
发送窗口(p1-p3)由三个指针构成:
- p1:p1指向发送窗口的后沿,它后面的字节表示已经发送且已收到应答。
- p2:p2指向尚未发送的第一个字节。
p1-p2间的字节表示已经发送,但还没收到确认应答。这部分的字节仍需保留,因为可能还要超时重发。
p2-p3间的字节表示可以发送,但还没有发送的字节。 - p3:p3指向发送窗口的前沿,它前面的字节尚未发送,且不允许发送。
发送者每收到一个应答,后沿就可以向前移动指定的字节。此时若窗口大小仍然没变,前沿也可以向前移动指定字节。
当p2和前沿重合时,发送者必须等待确认应答。
接收窗口
接收者收到的字节会存入接收窗口,接收者会对已经正确接收的有序字节进行累计确认,发送完确认应答后,接收窗口就可以向前移动指定字节。
如果某些字节并未按序收到,接收者只会确认最后一个有序的字节,从而乱序的字节就会被重新发送。
流控制
概念
让发送端根据接收端的实际接收能力控制发送的数据量。
目的
防止接收端在高负荷的情况下接收数据,因为这个时候如果接收端会将本应该接收的数据丢弃,就又会触发重发机制,从而导致网络流量的无端浪费。
具体实现
接收端主动向发送端主机通知自己可以接受数据的大小,于是发送端会发送不超过这个限度的数据。这个大小限度就是接收窗口大小,所以流控制就是利用滑动窗口协议实现的。
死锁问题
当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。但是如果这个窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。为了避免此类问题的发生,发送端主机会时不时发送一个叫做窗口探测的数据段,此数据段仅含一个以获取最新窗口的大小信息。
拥塞控制
拥塞控制和流控制的区别?
- 拥塞控制:拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况
- 流控制:流控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收
拥塞情况
观察拥塞的方式有两种:一是超时,二是连续接收到3个重复ACK。
PS:为什么是3个连续的ACK:由于我们不知道一个重复的ACK是由一个丢失的报文引起的,还是由于仅仅出现了几个报文段的重新排序引起的,因此我们等待少量的重复的ACK到来。因为若只是一些报文段的重新排序引起的,一般在重新排序报文段完成并产生一个新的ACK之前只可能产生1-2个重复的ACK。
拥塞的不同处理方式
对于由于超时重传认为的拥塞,我们一般是重传报文段,然后进入慢启动算法(下方);对于由于接收到3个重复的ACK认为的拥塞,我们一般是立即重传报文段,然后进入拥塞避免算法(下方)。这样处理的原因是因为当因为定时器超时,此时网络中可能已经很拥塞,数据确认的ACK已经无法发送回来,因此我们立即减少注入网络中的数据,使用慢启动拥塞窗口减小为1;而对于收到,3个重复的ACK说明还有其他的报文段到达了目的地(因为接收方只有在收到失序的报文段时才会产生重复的ACK而且还有重复的ACK发送回来),也即收发两端还有数据的流动,因此我们不必使用慢启动突然减少注入网络的数据。
慢启动算法 和 拥塞避免算法
发送方维护一个发送窗口,发送窗口的大小取决于网络的拥塞情况(拥塞窗口),发送窗口是动态变化的。
发送方还维护一个慢启动阈值。
- 发送窗口 < 慢启动阈值:使用慢启动算法(指数级)
- 发送窗口 > 慢启动阈值:使用拥塞避免算法(线性加一)
- 发送窗口 = 慢启动阈值:使用慢开始算法或拥塞避免算法
算法的具体过程:
- 通信开始时,使用慢启动算法,发送方的发送窗口设为1,并发送第一个分组M1;
- 接收方收到M1后,返回确认应答,此时发送方发送窗口扩大两倍,并发送M2、M3;(即,发送方每次收到确认应答后,都将发送窗口设为当前值的两倍)
- 若发送窗口>慢启动阈值,则使用拥塞避免算法,每次收到确认应答后都将发送窗口+1;
- 若发送方出现了超时重传,则表明网络出现拥塞,此时:
- 慢启动阈值设为当前发送窗口的一半;
- 发送窗口设为1;
- 启用拥塞避免算法;
PS:发送超时重传时,发送窗口有可能已经超过了慢开始门限,也有可能还没超过;此时不管何种情况,都一律启用拥塞避免算法,并执行上述三步操作!
慢开始算法的作用:慢开始算法将发送窗口从小扩大,而且按指数级扩大,从而避免一开始就往网络中注入过多的分组从而导致拥塞;它将窗口慢慢扩大的过程其实也在探测网络拥塞情况的过程,当发现出现拥塞时,及时降低发送速度,从而减缓网络拥塞。
拥塞避免算法的作用:拥塞避免算法使发送窗口以线性方式增长,而非指数级增长,从而使网络更加不容易发生拥塞。
快重传和快恢复
当因收到三个及三个以上的重复ACK时,使用如下拥塞控制方法:
- 收到3个重复的ACK,将慢启动阈值设为当前拥塞窗口大小的一半
- 此时立即重传丢失的报文不用等到定时器超时(超时重传),此为快重传。
- 此后开始执行拥塞避免而不是慢启动,此为快恢复。
TCP的应用
当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用。常见的使用UDP协议的应用如下:
- HTTP(超文本传输协议)
- FTP(文件传输协议)
- POP、SMTP(邮件传输协议)
- Telnet、Telnet(远程连接协议)
- QQ文件传输