前面我们介绍了网络层的IP协议、ICMP协议、ARP协议,后续给大家介绍下传输层的TCP和UDP协议。今天先讲讲TCP协议。
本篇文章概要:
正文
我们知道TCP是传输层协议,用于为应用层提供服务,通过端口号可以唯一标识一个应用。
1
什么是TCP?
TCP 是面向连接的,提供端到端可靠性服务的传输层协议。
面向连接中通信中,会在在两个端点之间建立了一条可靠的数据通信信道。
电话就是一种面向连接的服务,双方建立连接后才能够通话,可以确保对方听到你说话;而发短信就不是一种面向连接的服务,你随时可以发送短信,但是不能确保对方及时收到。
保证从发送端发送的报文都可以被目的端收到,哪怕被丢弃,也可以让发送端重传;
2
为什么需要TCP,TCP可以解决什么问题?
IP 层是「不可靠」的,它只负责数据包的发送,但它不保证数据包能够被接收、不保证网络包的按序交付、也不保证网络包中的数据的完整性。
如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。
因为 TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。后续会讲TCP协议是如何确保数据包的可靠传输的?
3
TCP报文格式
我们知道待发送的数据是根据TCP/IP四层模型层层封装的,那么TCP协议是如何封装的?下面我们看下TCP的报文格式。
如图所示为TCP报文头格式。
TCP数据段由TCP Header(头部)和TCP Data(数据)组成。TCP最多可以有60个字节的头部,如果没有Options字段,正常的长度是20字节。
1、16位源端口号:源主机的应用程序使用的端口号。
2、16位目的端口号:目的主机的应用程序使用的端口号。每个TCP头部都包含源和目的端的端口号,这两个值加上IP头部中的源IP地址和目的IP地址可以唯一确定一个TCP连接。
TCP允许一个主机同时运行多个应用进程。每台主机可以拥有多个应用端口,每对端口号、源和目标IP地址的组合唯一地标识了一个会话。
端口分为知名端口和动态端口。
有些网络服务会使用固定的端口,这类端口称为知名端口,端口号范围为0-1023。如FTP、HTTP、Telnet、SNMP服务均使用知名端口。
动态端口号范围从1024到65535,这些端口号一般不固定分配给某个服务,也就是说许多服务都可以使用这些端口。只要运行的程序向系统提出访问网络的申请,那么系统就可以从这些端口号中分配一个供该程序使用。
3、32位序列号:用于标识从发送端发出的不同的TCP数据段的序号。可以解决网络包乱序问题。
数据段在网络中传输时,它们的顺序可能会发生变化;接收端依据此序列号,便可按照正确的顺序重组数据。
假定主机A和B进行tcp通信,A传送给B一个tcp报文段中,序号值被系统初始化为某一个随机值ISN,那么在该传输方向上(从A到B),后续的所有tcp报文段中的序号值都会被设定为ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。例如某个TCP报文段传送的数据是字节流中的第1025~2048字节,那么该报文段的序号值就是ISN+1025。
4、32位确认序列号:用于标识接收端确认收到的数据段。确认序列号为成功收到的数据序列号加1。用来解决不丢包的问题。
假定主机A和B进行tcp通信,那么A发出的tcp报文段不但带有自己的序号,也包含了对B发送来的tcp报文段的确认号。反之也一样。若确认号=N,则表明:到序号N-1为止的所有数据都已正确收到。
5、4位头部长度:表示头部占32bit字的数目,它能表达的TCP头部最大长度为60字节。
6、6位标志位:
URG:紧急指针是否有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不要按原来的排队顺序来传送。
例如,已经发送了很长的一个程序在远端的主机上运行。但后来发现了一些问题,需要取消该程序的运行。因此用户从键盘发出中断命令(Control+c)。如果不使用紧急数据,那么这两个字符将存储在接收TCP的缓存末尾。只有在所有的数据被处理完毕后这两个字符才被交付接收方的应用进程。这样做就浪费了许多时间。
当URG置为1时,发送应用进程就告诉发送方的TCP有紧急数据要传送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍时普通数据。这时要与首部中紧急指针字段配合使用。
ACK:表示确认号是否有效,携带ack标志的报文段也称确认报文段,仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置1。
PSH:提示接收端应用程序应该立即从tcp接受缓冲区中读走数据,为后续接收的数据让出空间。
当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送操作。这时,发送方TCP把PSH置1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后向上交付。虽然应用程序可以选择推送操作,但推送还很少使用。
RST:表示要求对方重建连接。带RST标志的tcp报文段也叫复位报文段。
当RST=1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。RST置1还用来拒绝一个非法的报文段或拒绝打开一个连接。
SYN:表示建立一个连接,携带SYN的tcp报文段为同步报文段。在连接建立时用来同步序号。
当SYN=1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在相应的报文段中使用SYN=1和ACK=1。因此,SYN置为1就表示这是一个连接请求。
FIN标志:表示告知对方本端要关闭连接了。用来释放一个连接。
当FIN=1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
7、16位窗口大小:表示接收端期望通过单次确认而收到的数据的大小。由于该字段为16位,所以窗口大小的最大值为65535字节,该机制通常用来进行流量控制。
窗口值是【0,2^16-1]之间的整数。窗口指的是发送本报文段的一方的接收窗口(而不是自己的发送窗口)。
窗口值告诉对方:从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。
总之,窗口值作为接收方让发送方设置其发送窗口的依据。并且窗口值是经常在动态变化着。
8、16位校验和:校验整个TCP报文段,包括TCP头部和TCP数据。该值由发送端计算和记录并由接收端进行验证。
9、16位紧急指针:是一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。因此这个字段是紧急指针相对当前序号的偏移量。发送紧急数据时会用到这个。
紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据)。
因此,紧急指针指出了紧急数据的末尾在报文段中的位置。当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为零时也可发送紧急数据。
10、选项:长度可变,最长可达40字节。当没有使用“选项”时,TCP的首部长度是20字节。
4
TCP协议工作原理
知道了TCP报文的封装格式,那么TCP协议是如何为应用层提供服务的呢?如何建立TCP连接?如何保证数据传输的可靠性?又是如何关闭连接的呢?下面我们一起看下
TCP是面向连接的传输层协议,那么TCP在传输数据之前是如何建立连接的呢?
1、主机A(通常也称为客户端)发送一个标识了SYN=1,ACK=0的数据段,表示期望与服务器A建立连接,TCP规定SYN=1时不能携带数据,但要消耗一个序号,因此声明自己的序号是 seq=a;
2、服务器A回复标识了SYN=1,ACK=1的数据段,此数据段的序列号seq=b,确认序列号为主机A的序列号加1(ack=a+1),以此作为对主机A的SYN报文的确认。
3、主机A发送一个标识了ACK=1的数据段,此数据段的序列号seq=a+1,确认序列号为服务器A的序列号加1(ack=b+1),以此作为对服务器A的SYN报文段的确认。
三次握手的过程其实可以理解成谈恋爱的过程,1、男方问女方可以做自己的女朋友吗?2、女生收到表达回应可以呀。3、男生收到女方的确认后就可以亲女方一下作为回应。宣布正式在一起。
TCP建立连接是为了可靠传输数据,那么TCP协议是如何保证数据的可靠传输的呢?
TCP的可靠传输体现在TCP使用了确认技术来确保目的设备收到了从源设备发来的数据,并且是准确无误的。
确认技术的工作原理:目的设备接收到源设备发送的数据段时,会向源端发送确认报文,表明自己收到了哪些数据,源设备收到确认报文后,继续发送数据段,如此重复。
如图所示,主机A向服务器A发送TCP数据段,为描述方便假定每个数据段的长度都是500个字节。
主机A发送前3个数据段,当服务器A成功收到序列号是M+1499的字节以及之前的所有字节时,会以序列号M+1499+1=M+1500进行确认。
但是在发送第N+3个数据段时传输失败,由于数据段N+3传输失败,所以服务器A未能收到序列号为M+1500的字节,因此服务器A还会再次以序列号M+1500进行确认。这时主机A会使用重传机制,重新传输N+3后的数据段。
前面我们讲了TCP报文头中有一个16位窗口大小字段:用来表示接收端期望通过单次确认而收到的数据的大小。通过该机制用来进行流量控制。
TCP滑动窗口技术通过动态改变窗口大小来实现对端到端设备之间的数据传输进行流量控制。
如图所示,主机A和服务器A之间通过滑动窗口来实现流量控制。为方便理解,此例中只考虑主机A发送数据给服务器A时,接收端服务器A通过滑动窗口进行的流量控制。
主机A向服务器发送4个长度为1024字节的数据段,其中主机A的窗口大小为4096个字节。服务器A收到第3个数据段后,缓存区满,第4个数据段被丢弃。服务器以ACK3073响应,窗口大小调整为3072,表明服务器的缓冲区只能处理3072个字节的数据段。
于是主机A改变其发送速率,发送窗口大小为3072的数据段。这样子,主机A下次发送的数据段的窗口大小都是3072。
TCP协议会在传输数据前建立连接,那么数据传输完成后,为了避免维持连接的开销,TCP会关闭连接。
TCP支持全双工模式传输数据,这意味着同一时刻两个方向都可以进行数据的传输。在传输数据之前,TCP通过三次握手建立的实际上是两个方向的连接,因此在传输完毕后,两个方向的连接必须都关闭。
TCP连接的建立是一个三次握手的过程,而TCP连接的终止则要经过四次握手。
如图所示:
1、主机A想终止连接,于是发送一个标识了FIN=1的数据段,序列号为seq=a。FIN=1的作用就是用来释放连接的,表明主机A数据已经发送我完毕,要求释放连接.
2、服务器A回应一个标识了ACK=1的数据段,序列号为seq=b,确认序号为ack=a+1,作为对主机A的FIN报文的确认。
3、主机A收到服务器A的确认后进入等待状态,等待服务器A请求释放连接,服务器A数据发送完成后就向A请求连接释放。
服务器A想终止连接,于是向主机A发送一个标识了FIN=1,ACK=1的数据段,序列号为seq=b,确认序列号为ack=a+1。表明服务器A数据已经发送我完毕,要求释放连接。
为什么主机A要等待呢?
为了这种情况:服务器A向主机A发送 FIN = 1 的释放连接请求,但这个报文丢失了, 主机A没有接到不会发送确认信息,服务器A 超时会重传,这时主机A在 WAIT_TIME 还能够接收到这个请求,这时再回复一个确认就行了。(主机A收到 FIN = 1 的请求后 WAIT_TIME会重新记时)
另外服务器A存在一个保活状态,即如果主机A突然故障死机了,那服务器A那边的连接资源什么时候能释放呢? 就是保活时间到了后,服务器A会发送探测信息, 以决定是否释放连接。
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
注意:中断连接端可以是Client端,也可以是Server端。
为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
来个通俗版的解释:
假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。
这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。
Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!(是服务端先关闭,后客户端关闭)
客户端从建立连接到关闭连接经历的状态:
服务器从建立连接到关闭连接经历的状态:
总结
今天主要介绍了TCP协议,TCP 是面向连接的,提供端到端可靠性服务的传输层协议。TCP连接的建立是一个三次握手的过程,而TCP连接的终止则要经过四次握手。TCP通过确认机制保证数据的可靠性的传输,通过滑动窗口机制进行流量控制解决拥塞问题。