TCP报文段结构

序列号seq: 占32位,本报文段发送的数据组的第一个字节的序号。一个报文段的序号为300,此报文段数据部分共有100字节,则下一个报文段的序号为400。所以序号确保了TCP传输的有序性。
确认号ack: 占32位,期待收到对方发送下一个报文段的第一个字节的序号,表明该序号之前的所有数据已经正确无误的收到。因此当前报文段最后一个字节的编号+1为确认号。
确认ACK: 占1位,仅当ACK=1时,确认号字段才有效。ACK=0时确认号无效
数据偏移: 占4位,并以4个字节为单位。用来指出TCP报文段的数据载荷部分的起始处距离多远,即指出TCP报文段首部长度。首部固定长度20字节,因此数据偏移字段最小 (0101)2 ——>54=20 ;加上最大40字节扩展首部,因此最大为(1111)2———>154=60
窗口: 占16位,以字节为单位。指出发送该报文段一方的接收窗口(我自己的接收窗口)。发送方通过接收方的接收能力设置合适的接收窗口,以此来控制发送方的发送流量,称为流量控制。(在连接建立时接收方就跟发送方说明自己接收能力)
同步SYN: 连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段,若同意连接,则在响应报文段中SYN=1,AKC=1。因此,SYN=1表示这是一个连接请求。SYN这个标志位只有在TCP建立连接才会置为1,握手完成后SYN标志位被置为0。
终止FIN: 用来释放一个连接。FIN=1表示:此报文段发送方的数据已经发送完毕,并要求释放运输链接。
复位标志位RST:用来复位TCP连接。当RST=1时,表明TCP连接出现了异常,必须释放连接,然后再重新建立连接;还用来拒绝打开TCP连接。
推送标志位PSH:接收方的TCP收到该标志位为1的报文段会尽快上交给应用程序,而不必等待接收缓存都填满后再向上交付。
紧急标志位URG:取值为1时紧急指针字段有效;取值为0则无效。
紧急指针:占16位,以字节为单位,用来指明紧急数据的长度。当发送方有紧急数据时,可将紧急数据插队到发送缓存的最前面,并立刻封装到一个TCP报文段中进行发送。紧急执政会指出本报文段数据载荷部分包含了多长的紧急数据,紧急数据之后是普通数据。
补充:ACK,SYN,FIN这些大写单词表示标志位,其至要么是1,要么是0;ack,seq小写的单词表示序号。
三次握手详细过程
第一次握手:建立连接时,客户端发送SYN包到服务器,并进入SYN_SENT状态,等待服务器确认。
第二次握手:服务器收到SYN包,必须确认第一次握手过来的报文段,同时自己也发送一个SYN包,此时服务器进入SYN_RECV状态。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕后客户端和服务器进取ESTABLISHED状态,完成三次握手。
四次握手详细过程
1.客户端进程发出连接释放报文,并且停止发送数据,释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号+1),此时,客户端进入了FIN-WAIT-1(终止等待1)状态。TCP规定,FIN报文段即使不携带数据,也要消耗一个序列号。
2.服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端没有数据发送了,服务器还发送数据,客户端依然要接受。这个状态还要持续一段事件,也就是整个CLOSE-WAIT状态持续的时间。
3.客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接收服务器发送的最后数据)。
4.服务器将最后的数据发送完毕后,就向客户端发送连接释放报文(还需要再发送确认请求),FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时服务器进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5.客户端收到服务器的连接释放报文后,必须发出确认,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)时间后,才进入CLOSED状态。
6.服务器只要收到了客户端发出的确认,立刻进入CLOSED状态。可以看到,服务器结束TCP连接的时间要比客户端早一些。
一些常见的有关问题
为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
为什么不能用两次握手进行连接?
答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的确认应答分组在传输中被丢失的情况下,C将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
TCP连接异常终止
TCP的异常终止是相对于正常释放TCP连接的过程而言的,我们都知道,TCP连接的建立是通过三次握手完成的,而TCP正常释放连接是通过四次挥手来完成,但是有些情况下,TCP在交互的过程中会出现一些意想不到的情况,导致TCP无法按照正常的四次挥手来释放连接,如果此时不通过其他的方式来释放TCP连接的话,这个TCP连接将会一直存在,占用系统的部分资源。在这种情况下,我们就需要有一种能够释放TCP连接的机制,这种机制就是TCP的reset报文。reset报文是指TCP报头的标志字段中的reset位置一的报文,如下图所示:



TCP流量控制的方式
1.拥塞控制
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这种情况就叫做网络拥塞。在计算机网络中数位链路容量(即带宽)、交换结点中的缓存和处理机等,都是网络的资源。若出现拥塞而不进行控制,整个网络的吞吐量将随输入负荷的增大而下降。当输入的负载到达一定程度 吞吐量不会增加,即一部分网络资源会丢失掉,网络的吞吐量维持在其所能控制的最大值。
TCP的四种拥塞控制算法
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
假定:
1.数据时单方向传送,而另一个方向只传送确认
2.接收方总有足够大的缓存空间,因而发送方发送窗口大小由网络的拥塞程度来决定
3.以TCP报文段的个数为讨论问题的单元
拥塞避免算法:
也就是每个传输轮次,拥塞窗口cwnd只能线性+1,而不像慢开始算法每个传输轮次按指数增长。同理,假设24个报文段在传输过程中丢失4个,接收方只收到20个报文段,给发送方依次回复20个确认报文段,一段时间后,丢失的4个报文段的重传计时器超时了,发送方判断可能出现拥塞,更改cwnd和ssthresh(将ssthresh更新为发生拥塞时的cwnd的一半,将cwnd的值减少为1),并重新开始慢开始算法。(ssthresh初始值默认位16个报文段)

快速重传:
发送方发送1号数据报文段,接收方收到1号报文段后给发送方发回对1号报文段的确认,在1号报文段到达发送方之前,发送方还可以将发送窗口内的2号数据报文段发送出去。
假设该报文丢失,接收方 便不会发送针对该报文的确认报文给发送方,发送方还可以将发送窗口内的4号数据报文段发送出去,接收方收到后,发现这不是按序到达的报文段,因此给发送方发送针对2号报文段的重复确认,表明我现在希望收到的是3号报文段,但是我没有收到3号报文段,而收到了未按序到达的报文段。
当发送方收到了累计3个连续的针对2号报文段的重复确认,立即重传3号报文段,接收方收到后,给发送方发回针对6号报文的确认,表明,序号到6为至的报文都收到了,这样就不会造成发送方对3号报文的超时重传,而是提早收到了重传。


一旦收到3个重复确认,执行快恢复算法,发送方将ssthresh和cwnd值调整为当前窗口的一半,然后继续执行拥塞避免算法

2.滑动窗口
1.发送方根据接收方缓存区大小,动态设置自己的可发送窗口大小,处于窗口内的数据表示可发送,之外的数据不可发送。
2.当窗口内的数据接收到确认回复时,整个窗口会向前移动,知道发送完成所有数据,并且接收方除了确认回复还会说自己现在的接收窗口是多少。
可以用下面的一个例子来具体说明:设A向B发送数据。在连接建立时,B告诉了A:“我的接收窗口是rwnd = 400 ”(这里的rwnd表示receiver window) 。因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。请注意,TCP的窗口单位是字节,不是报文段。假设每一个报文段为100字节长,而数据报文段序号的初始值设为1。大写ACK表示首部中的确认位ACK,小写ack表示确认字段的值ack。

从图中可以看出,B进行了三次流量控制。第一次把窗口减少到rwnd = 300 ,第二次又减到了rwnd = 100 ,最后减到rwnd = 0 ,即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机B重新发出一个新的窗口值为止。B向A发送的三个报文段都设置了 ACK = 1 ,只有在ACK=1时确认号字段才有意义。
如果B向A发送了0窗口的报文段不久后,B的接收缓存又有了一些存储空间,与时B向A发送了rwnd=400的报文段,然而这个报文段在传送过程丢失了,A一直等待收到B发送的非0窗口的通知,而B也一直等待A发送的数据,如果没有其它措施,这种互相等待的死锁局面将会一直延续下去。所以TCP为每一个连接设有一个持续计时器(persistence timer)。只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口控测报文段(携1字节的数据),而对方就在确认这个探测报文段时+给出了现在的窗口值。如果窗口仍然为0,那么收到这个报文段的一方就重新设置持续计时器。
TCP常见应用
- FTP:文件传输协议
2. SSH:安全登录、文件传送(SCP)和端口重定向
3. Telnet:不安全文本传送
4. SMTP:简单邮件传输协议
5. HTTP:超文本传输协议
TCP和UDP的区别
1.TCP连接时需要三次握手,有时间延迟,但是可靠,而UDP无连接,时间上不存在建立连接需要的延迟,但是不可靠。
2.空间上,TCP需要在端系统(因特网上所有主机)中维护连接状态,需要一定的开销。此连接装入包括接收和发送缓存(滑动窗口),拥塞控制参数(拥塞控制)和序号与确认号的参数(握手)。UDP不维护连接状态,也不跟踪参数,开销小。
举个例子:
UDP常用一次性传输比较少量数据的网络应用,如DNS(域名解析),SNMP(简单网络管理协议)等,因为对于这些应用,若是采用TCP,为连接的创建,维护和拆除带来不小的开销。UDP也常用于多媒体应用(如IP电话,实时视频会议,流媒体等)数据的可靠传输对他们而言并不重要,TCP的拥塞控制会使他们有较大的延迟,也是不可容忍的
HTTP使用TCP而不是UDP,是因为对于基于文本数据的Web网页来说,可靠性很重要。
3.UDP分组首部开销小,TCP首部20字节,UDP首部8字节。
4.UDP没有拥塞控制,应用层能够更好的控制要发送的数据和发送时间,网络中的拥塞控制也不会影响主机的发送速率。某些实时应用要求以稳定的速度发送,能容 忍一些数据的丢失,但是不能允许有较大的时延(比如实时视频,直播等)
5.UDP提供尽最大努力的交付,不保证可靠交付。所有维护传输的可靠性工作需要用户在应用层来完成。没有TCP的确认机制丶重传机制。如果因为网络原因没传送到对端,UDP也不会给应用层返回错误信息。
6.UDP面向报文的(TCP面向字节流的),对应用层交下来的报文,添加首部后直接向下交付给IP层,既不合并也不拆分。对IP层交上来的UDP用户数据报,去除首部后就原封不动地交付给上层应用程序,报文不可分割,于是报文是UDP数据报处理的最小单位。正是因为这样,UDP显得不够灵活,不能控制读写数据的次数和数量。比如我们要发送100个字节的报文,我们调用一次sendto函数就会发送100字节,对端也需要用recvfrom函数一次性接收100字节,不能使用循环每次获取10个字节,获取十次这样的做法。
