TCP协议
面向字节流的点对点全双工通信方式。TCP传输的数据单元叫报文段,TCP报文段 = TCP首部 + TCP数据部分。一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个是TCP的拆包和粘包。应用程序写入数据的字节大小可能大于套接字发送缓冲区的大小,此时就需要进行MSS大小的TCP分段( MSS=TCP报文段长度-TCP首部长度)。

几个重要字段的含义:
序列号:TCP是面向字节流的,故每一个字节都有一个编号,帧的序号就是本帧第一个字节的编号。
确认号:接收方希望收到下一帧的第一个字节的序号。
首部长度:占四位,以4B为计算单位。
确认位ACK:ACK = 1时,确认号字段才有效,TCP规定建立连接后所有的报文段都必须把ACK置1。
同步位SYN:SYN = 1表示这是一个连接请求或连接接收报文。
终止位FIN:用来释放一个连接,FIN = 1表示此报文段发送方的数据已经发送完了,请求释放传输连接。
窗口字段:允许对方发送的数据量。
校验和:首部和数据两部分的校验,计算时需要加上12B的伪首部。
选项字段:目前只规定了一种选项即MSS(最大报文段长度)。
确认应答机制
每一个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下一次你从哪里开始发。TCP使用累计确认,收到某一个确认序号,即代表前面的帧全部接受对。
重传机制
发送过程中会发生丢包 的现象, 而 丢包可能会丢普通报文, 也可能会丢 ACK ,两种情况下,发送方都不知道数据对方是否收到。TCP具有乱序重组的功能,所以失序时,接收方对失序的报文段也会进行暂存。TCP重传方式:
超时重传机制:在一段时间内 没收到应答 ,发送方就会再发送一次数据。对于超时重传机制,多久没收到应答进行重新发送的时间间隔, 会越来越长. 如 : 第一次发送没收到, 间隔1秒再发送 , 第二次发送又没收到 ,就会间隔 2 秒再发送, 以此类推. 也不会一直重新发送, 尝试几次后, 如果仍然不成功,就会断开连接,重新尝试连接. 重连也连不上,就放弃了。
冗余ACK:再次确认某个报文段的ACK。超时重传的缺点是周期太长,可用冗余ACK进行快速重传。例如:如果发送方发出了1,2,3,4,5份数据,第一份先到送了,于是就ack回2,结果2因为某些原因没收到,3到达了,于是还是ack回2,后面的4和5都到了,但是还是ack回2,因为2还是没有收到,于是发送端收到了三个ack=2的确认,知道2还没有到,于是就马上重转2。然后,接收端收到了2,此时因为3,4,5都收到了,于是ack回6。于是引入SACK选择确认选项,若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据;通过选择确认 ,可以只传送缺少的数据而不重传已经正确到达接受方的数据。
TCP三次握手连接
客户端连接请求报文段:只有头部数据,序号为x,虽不含任何数据,但要消耗掉一个序号:SYN = 1,seq = x。
接收方确认报文段:SYN = 1,ACK = 1,seq = y,ack = x + 1。
发送方确认报文段:ACK = 1,seq = x + 1,ack = y + 1。这里可带数据了。
TCP三路握手过程的状态变迁:
CLOSED:起始点,在超时或者连接关闭时候进入此状态,这并不是一个真正的状态,而是这个状态图的假想起点和终点。
LISTEN:服务器端等待连接的状态。服务器经过 socket,bind,listen 函数之后进入此状态,开始监听客户端发过来的连接请求。此称为应用程序被动打开(等到客户端连接请求)。
SYN_SENT:第一次握手发生阶段,客户端发起连接。客户端调用 connect,发送 SYN 给服务器端,然后进入 SYN_SENT 状态,等待服务器端确认(三次握手中的第二个报文)。如果服务器端不能连接,则直接进入CLOSED状态。
SYN_RCVD:第二次握手发生阶段,跟 3 对应,这里是服务器端接收到了客户端的 SYN,此时服务器由 LISTEN 进入 SYN_RCVD状态,同时服务器端回应一个 ACK,然后再发送一个 SYN 即SYN+ACK 给客户端。
ESTABLISHED:第三次握手发生阶段,客户端接收到服务器端的 ACK 包(ACK,SYN)之后,也会发送一个 ACK 确认包,客户端进入 ESTABLISHED 状态,表明客户端这边已经准备好,但TCP 需要两端都准备好才可以进行数据传输。服务器端收到客户端的 ACK 之后会从 SYN_RCVD 状态转移到 ESTABLISHED 状态,表明服务器端也准备好进行数据传输了。所以 ESTABLISHED 也可以说是一个数据传送状态。
TCP 2次握手行不行?为什么要3次?
为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤,如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。
TCP四次握手释放
客户端连接释放报文段:只有头部数据,但要消耗掉一个序号。FIN = 1,seq = u。
服务端的确认报文:ACK = 1,seq = v,ack = u + 1。
服务端连接释放报文段:ACK = 1,FIN = 1,seq = w,ack = u + 1。
接收端的确认报文段:ACK = 1,seq = u + 1,ack = w + 1
TCP建立连接时,是没有历史包袱的,立即就能完成,而 断开连接时, 客户端发送FIN给服务器时, 服务器中可能还有数据在缓冲区中没读完, 服务器要把数据处理完了再发FIN , 而客户端什么时候发FIN就是代码层次的问题了。如果服务器没有缓冲区数据要处理,服务端在发送连接释放的确认报文段时,也是可以同时发送连接释放报文段的。
TCP四次挥手过程的状态变迁。
FIN_WAIT_1:第一次挥手。主动关闭的一方(执行主动关闭的一方既可以是客户端,也可以是服务器端,这里以客户端执行主动关闭为例),终止连接时,发送 FIN 给对方,然后等待对方返回ACK 。
CLOSE_WAIT:接收到FIN 之后,被动关闭的一方进入此状态。具体动作是接收到 FIN,同时发送ACK。之所以叫 CLOSE_WAIT 可以理解为被动关闭的一方此时正在等待上层应用程序发出关闭连接指令。
FIN_WAIT_2:主动端(这里是客户端)先执行主动关闭发送FIN,然后接收到被动方返回的 ACK后进入此状态。
LAST_ACK:被动方(服务器端)发起关闭请求,由状态2 进入此状态,具体动作是发送 FIN给对方,同时在接收到ACK 时进入CLOSED状态。
CLOSING:两边同时发起关闭请求时(即主动方发送FIN,等待被动方返回ACK,同时被动方也发送了FIN,主动方接收到了FIN之后,发送ACK给被动方),主动方会由FIN_WAIT_1 进入此状态,等待被动方返回ACK。
TIME_WAIT:从状态变迁图会看到,四次挥手操作最后都会经过这样一个状态然后进入CLOSED状态。
滑动窗口机制
在发送数据时 , 一次性发送多个报文,相当于一个窗口,将窗口内的数据一次性发出去, 在发送的第一条的ACK返回后,窗口就往后滑动,继续发送后续数据。因为为了保证可靠性, 降低了数据传输的效率。为了补救传输数据的效率,使用了滑动窗口机制 . 因此滑动窗口是一种效率补救机制。发送数据过程中必然会产生丢包的问题: 在丢包的情况下, 能否保证可靠性呢. 下面分为两种情况分析 :
丢失的是ACK:如果丢失的是其中某条数据的ACK,并不会影响可靠性,因为只要后续数据的ACK收到,就说明前面的的数据都已经发送到接收端了,因为累计的特点,后续的ACK会涵盖前面的ACK。
丢失的是数据包:如果丢失的是其中某个数据包( 如 1001-2000)的数据包丢失),接收方就会一直向发送方返回1001的ACK(冗余ACK),在重复几次后,发送方就明白了 是1001-2000的数据包丢失,启动重发机制,重新发送 1001-2000的数据包。
流量控制机制
流量控制是用来控制滑动窗口的大小的,因为滑动窗口机制提高了数据发送的效率,如果滑动窗口越大,发送速率越大,但是如果发送速率过大, 而接收方的接收速率 ,是有限的, 这样就会导致接收方处理不过来, 导致丢包问题, 这样就要频繁的进行重发 , 效率反而更低。流量控制机制是一种保证可靠性的机制。根据接收方接收速率的大小,确定滑动窗口的大小。那么如何确定接收方的接收速率多大呢?在接收方接收数据时,接收方的操作系统内核中会有一块 " 接收缓冲区",先存放当前接收到的数据,而读取接收缓冲区中的存放的数据的速度,就是接收速率。接受速率的多快是根据我们的所写的代码的实现方式来确定的,代码的实现方式 五花八门,就是导致接收的速率也各不相同,直接表示并不好表示。

那接收方如何接收缓冲区的大小告诉发送方呢?可以在ACK的报文中带上这个信息,如果缓冲区满了,窗口大小就会变成0,此时发送方就会停止发送数据。
拥塞控制机制
通信的双方A和B要完成通信,不单只有这两个,还有很多的中转站 ( 交换机 \ 路由器) 。此时用接收缓冲区剩余空间的大小来衡量接收方B的接收速率,那中转站的中转能力又要如何进行衡量呢?这就是拥塞控制。
先以最小的滑动窗口来试探。
如果不丢包,说明网络顺畅,逐步增大滑动窗口。
放大到一定程度后,速率已经比较快了,网络出现拥堵,进一步出现丢包的情况,当发现丢包后就要减小滑动窗口。
TCP采用慢启动(Slow Start)。传输轮次:指把发送窗口内可以发送的数据全部发送并接收到最后一个TCP报文的确认报文这样一个来回。cwnd:拥塞窗口。通常在一条TCP连接开始时,cwnd被设置为1个MSS(最大报文段),即cwnd=1,该阶段,每当TCP发送方将发送窗口的数据发送完,并顺利接收到所有的确认后,就会将拥塞窗口大小翻倍,也即慢启动阶段,cwnd以指数形式增长,拥塞窗口会一直增长直到到达慢开始门限ssthresh,开始执行拥塞避免算法。该阶段的拥塞窗口变为线性增长,每次cwnd+1,也即每次增加一个MSS。