运输层
因为TCP属于运输层的协议,所以我们首先了解一下运输层。 运输层为 应用进程 提供端到端的的逻辑通信。(补充:网络层为 主机之间 提供逻辑通信。) 运输层有两种不同的运输协议,即面向连接的TCP和无连接的UDP。
TCP最主要的特点
1、TCP是面向连接的运输层协议。 也就是说,应用程序在使用TCP协议之前,必需先建立TCP连接。在传送完数据完毕后,必须释放已经建立的TCP连接。也就是说,应用进程之间的通信好像在“打电话”:通话前要先拨号建立连接,通话结束后要挂机释放连接。 2、每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的。 3、TCP 提供可靠交付的服务。 通过TCP连接传送的数据,无差错,不丢失 ,不重复,并且按序到达。 4、TCP提供全双工通信。 TCP允许通信双方的应用进程在任何时候都能发送数据。TCP连接的两端都设置有发送缓存和接收缓存,用来临时存放双向通信的数据。在发送时,应用进程在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收入道的数据放入缓存,上层应用进程在合适的时候读取缓存中的数据。 5、面向字节流。 6、提供拥塞控制。 当网络出现拥塞的时候,TCP能够减小向网络注入数据的速率和数量,缓解拥塞。
TCP报文段的首部格式
了解一些TCP首部中常用的字段: ACK: 确认ACK,仅当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在建立连接后所有传送的报文段都必须把ACK置为1。 SYN: 同步SYN, 在连接建立时用来同步序号。当SYN = 1而ACK=0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在相应的报文段中使SYN=1和ACK = 1。因此,SYN置为1就表示这是一个连接请求或连接接受报文。 FIN: 用来释放一个连接。当FIN = 1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
TCP的可靠传输机制
TCP的可靠传输机制是基于连续ARQ协议和滑动窗口协议的。 TCP协议在发送方维持了一个发送窗口,发送窗口以前的报文段是已经发送并确认了的报文段,发送窗口中包含了已经发送但未确认的报文段 和允许发送但还未发送的报文段 ,发送窗口以后的报文段是缓存中还不允许发送的报文段。 当发送方 向接收方 发送报文时,会依次发送窗口内的所有报文段,并且设置一个定时器,这个定时器可以理解为是最早发送但未收到确认的报文段。如果在定时器的时间内收到某一个报文段的确认回答,则滑动窗口,将窗口的首部向后滑动到确认报文段的后一个位置,此时如果还有已发送但没有确认的报文段,则重新设置定时器,如果没有了则关闭定时器。 如果定时器超时,则重新发送所有已经发送但是未收到确认的报文段,并将超时的间隔设置为以前的两倍。当发送发收到接收方的三个冗余的确认应答后,这是一种指示,说明该报文段以后的报文段很有可能发生丢失了,那么发送方会启用快速重传的机制,就是当前定时器结束前,发送所有已发送的但确认的报文段。 接收方使用的是累计确认的机制,对于所有按序到达的报文段,接收方返回一个报文段的肯定回答。如果收到了一个乱序的报文 段,那么接方会直接丢弃, 并返回一个最近的按序到达的报文段的肯定回答。使用累计确认保证了返回的确认号之前的报文段都 已经按序到达了,所以发送窗口可以移动到已确认报文段的后面。 发送窗口的大小是变化的,它是由接收窗口剩余大小和网络中拥塞程度来决定的,TCP 就是通过控制发送窗口的长度来控制报文 段的发送速率。 但是 TCP 协议并不完全和滑动窗口协议相同,因为许多的 TCP 实现会将失序的报文段给缓存起来,并且发生重传时,只会重 传一个报文段,因此 TCP 协议的可靠传输机制更像是窗口滑动协议和选择重传协议的一个混合体
TCP如何保证数据包传输的有序可靠?
对字节流分段并进行编号然后通过ACK回复和超时重发这两个机制来保证。 (1)为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区。 (2)并为每个已发送的数据包启动一个超时定时器 (3)如在定时器超时之前收到了对方发来的应答信息(可能是对本包的应答,也可以是对本包后续包的应答),则释放该数据包占用的缓冲区; (4)否则,重传该数据包,直到收到应答或重传次数超过规定的最大次数为止。 (5)接收方收到数据包后,先进行CRC校验,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包,表明该数据已收到,如果接收方正好也有数据要发给发送方,应答包也可方在数据包中捎带过去。
TCP三次握手
三次握手其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接受能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。 刚开始客户端处于closed的状态,服务端处于Listen状态,进行三次握手
- 第一次握手: 客户端给服务端发一个SYN报文,这时首部中的同部位SYN = 1, 同时选择一个初始序号seq = x 。TCP规定,SYN报文段(即SYN = 1的报文段)不能携带数据,但要消耗一个序号。这时TCP客户进程进入SYN-SENT(同步发送)状态。
- 第二次握手: 服务器收到客户端的SYN报文之后,会以自己的SYN报文作为应答,服务端在确认报文段中应把SYN位和ACK位都置1,确认号是ack = x + 1,同时也为自己选择一个初始序号 seq = y 。这个报文段也不能携带数据,但同样要消耗掉一个序号。这时TCP服务器进程进入SYN-RCVD(同步收到)状态。
- 第三次握手: TCP客户进程在收到服务端的确认后还要向服务端做出确认。确认报文段的ACK置为1,确认号ack = y + 1,而自己的序号seq = x + 1(初始为seq = x,所以第二个报文段要+1)。ACK报文段可以携带数据,不携带数据则不消耗序号。这时TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态。当服务端收到客户端的确认后,也进入ESTABLISHED状态。
问题思考: 三次握手的作用是什么?
- 第一次握手:客户端发送网络包,服务端收到了。
这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。 - 第二次握手:服务端发包,客户端收到了。
这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接受能力是否正常。 - 第三次握手:客户端发包,服务端收到了。
这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
三次握手过程中可以携带数据吗? 其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据 为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。 也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
TCP四次挥手
建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。 TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或服务器均可主动发起挥手动作。 刚开始双方都处于 ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程如下:
- 第一次挥手: 客户端进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。 客户端 把连接释放的报文段首部的终止控制位FIN置1,其序号seq = u,它等于前面已传送过的数据的最后一个字节的序号加1。这时 客户端 进入FIN-WAIT-1(终止等待1)状态,等待 服务器 的确认。请注意,TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号。
- 第二次挥手: 服务器 收到连接释放报文段后即发出确认,确认号是 ack = u + 1,而这个报文段自己的序号是v,等于 客户端前面已经传送过的数据的最后一个字节的序号加1。然后 服务器就进入CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时应该通知高层应用进程,因而从 客户端 到 服务器这个方向的连接就释放了。这时TCP连接处于半关闭状态,即 客户端已经没有数据要发送了,但服务器若发送数据, 客户端仍要接收。也就是说,从服务器到 客户端这个方向的连接并未关闭,这个状态可能会持续一段时间。
客户端收到来自服务器的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待服务器发出的连接释放报文段。 - 第三次挥手: 若 服务器 已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时 服务器 发出的连接释放报文段必须使 FIN = 1。现假定服务器的序号为w,(在半关闭状态服务器可能有发送了一些数据)。服务器必须重复上次已经发送过的确认号 ack = u + 1。这时 服务器 就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 第四次挥手: 客户端在收到b的连接·释放报文段后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack = w + 1,而自己的序号是seq = u + 1(根据TCP标准,前面发送过的FIN报文段要消耗一个序号)。然后进入到TIME-WAIT(时间等待)状态。请注意,现在TCP连接还没有释放掉,必须经过时间等待计时器(TIME-WAIT timer)设置时间2MSL后,客户端才进入到CLOSED状态。
时间MSL叫做最长报文段寿命。
|