头部一般20个字节,但是有可变长度字段,因此是不定长的头部;头部包括了源端口号以及目的端口号、序列号字段(Seq)、确认应答字段(ACK)以及控制位、窗口大小、校验和等。
其中控制位主要比如有ACK、RST、FIN、SYN四个;
SYN是在请求建立连接的时候置一
RST在出现异常希望强制断开的时候置一
ACK的值表示收到了这个ACK_NUM-1及以前的报文段
FIN表示希望断开连接,并且之后不再发送应用数据了。
本质来说,TCP建立连接的过程就是去协商信息的过程,具体的信息包括:Socket、序列号、窗口大小;双方信息协商好,可以认为TCP连接就建立了,所以说一个TCP连接,就是为了保证数据在两端之间可靠交付而去维护的一些信息状态的集合,
a. 从连接层面来说,TCP是面向连接的,UDP是无连接的;
b. 从服务对象来说,TCP只能是一对一的,UDP可以一对一、一对多、甚至多对多;
c. 可靠性:TCP能够向上层提供可靠的交付,包括了数据无差错、不丢失、不乱序、不重复等等。但是UDP是不保证可靠交付的,基于UDP又想要可靠只能在应用层去完成。
d. 拥塞控制和流量控制:TCP有,UDP无
e. 头部开销:TCP是变长的头部,一般是20个字节起;UDP是定长头部,都是8个字节;
f. 传输方式:TCP是面向字节流的,发送的消息是没有边界的,但是UDP发送的消息有边界,是一个包一个包的发送的。
g. 分片方式:基于TCP的话,超过了MSS,则会在TCP层进行分段,然后才向下交付给IP层,保证IP层的数据包不会超过MTU(1500Byte),如果丢失了段,那就只重发丢失的段即可;UDP中不会再传输层分片,如果大于了MTU,会在IP层进行分片;
h. 应用场景:HTTP/HTTPS、FTP一般基于TCP;DNS、音频视频一般基于UDP协议。
TCP连接的建立会经历经典的三次握手
a、第一次握手 SYN ,首先由客户端向服务端发送一个控制位置1的SYN报文,请求建立连接,同时初始化一个随机值作为序号字段(client_isn);报文发送后,客户端进入SYN_SENT状态。
b、第二次握手 ACK+SYN,服务端收到了SYN后,服务端也会初始化随机值作为序列号字段(server_isn),控制位中的ACK与SYN均置为一,返回一个ACK+SYN报文,在响应客户端的同时向客户端申请连接(为什么不四次握手的原因也在此,即第二次和第三次握手可合并);报文发送后,服务端进入SYN_RCVD状态。
c、第三次握手 ACK,客户端收到SYN+ACK后,返回一个ACK报文,表示同意与服务端建立连接,这时返回报文的序列号字段为client_isn+1,控制位的ACK置一;报文发送后,客户端进入ESTABLISHED状态,对方服务端收到ACK后也会进入ESTABLISHED状态,至此三次握手完成。
首先回答为什么不两次握手,TCP不采用两次握手
最主要的原因是防止历史冗余连接的初始化,考虑一个场景,如何发送了一个SYN在网络中阻塞了,客户端又发送了一个新的SYN,这时候如果旧的SYN又比新的SYN先到达服务端,那么服务端就会响应一个ACK=旧的Seq_num + 1,但是客户端看到ACK的序号明显不是想要的 = 新的Seq_num + 1,就会向服务端发送一个RST,强制中断这个一个TCP连接,直到新的SYN到了服务端,服务端响应ACK=新的Seq_num + 1,这时候客户端才会与其正常建立连接。如果只有两次握手的话,就没有办法阻止历史连接的初始化,需要多i一次来给客户端判断是否当前的连接是最新的连接。
另外一个原因是TCP建立连接的过程需要客户端和服务端双方同步序列号,按理说应该客户端发送给服务端序列号,服务端响应,之后服务端向客户端发送序列号,客户端响应,这样一来一去才能保证双方的序列号都被可靠的同步了,如果只有两次握手,那么只能保证客户端的序列号被服务端同步了,但是服务端的序列号不一定被客户端同步,这样就只能保证可靠的是:客户端能向服务端发送,服务端只能接收。
另外三次握手还可以避免资源的浪费,比如,如果两次握手,客户端发起SYN的时候因为网络阻塞重发了SYN,因为没有第三次握手,所以在第二次握手的时候服务端就已经建立起了连接,也就是每一个SYN服务端就会建立一个连接,因此会有很多冗余的连接,造成不必要的资源浪费。
不采用四次握手很简单,虽然四次握手可以保证双方的序列号被可靠同步,但是第二次和第三次其实是可以合并为一次的,所以就没有必要多一次通信。
TCP在断开连接的时候要经历四次挥手的过程,注意客户端和服务端都可以主动断开连接,下面以客户端主动断开为例。
谁主动发起的断开,谁才会有TIME-WAIT状态,
建立连接时,四次握手中的第二次和第三次可以简化为一步,从而是三次握手,但是在断开连接时,第二步ACK和第三步FIN不能同时发送,因为在服务端FIN前,需要将其未发送的数据或者未处理的数据处理完以后才能发送FIN,所以ACK和FIN不能合并一步,因此需要四次挥手。
首先需要TIME_WAIT的原因是:
1、防止具有相同四元组的数据包被接收,导致数据错乱:如果不要TIME-WAIT,直接关闭,那么如果刚好相同四元组的TCP连接又建立了,而且新的客户端的ACK刚好是之前旧TCP连接中正在发送的序列号,那么这时候旧网络的TCP报文就可能被新连接中的客户端接收,造成严重的数据错乱,因此添加一个TIME-WAIT来保证这些历史的TCP报文段能在旧连接中正常消亡以后才断开。
2、如果TIME-WAIT没有或者过短,那就可能不能保证被动关闭方是否已经接收到了最后的ACK,这时候被动关闭方如果真的没有接收到ACK,那就会长期处于LAST_ACK的状态,占用内存和端口。
为什么2MSL?
一个MSL是网络段在网络中能存在的最大生命时间,两个也就是往返的最大时间,那么在2*MSL的时间内如果没有重发的FIN,则说明ACK没有丢失,那就可以放心关闭了,如果收到了FIN,就说明ACK丢失了,那就重发一次ACK,并且TIME_WAIT重新计时;总的来说,2MSL就是一个比较充裕的时间来等待看最后一次ACK是否丢失。
TCP内有个保活机制,也就是说在一定时间内如果双方没有通信那就会激活保活机制,
所谓保活机制就是在一定时间内向对方发送多个探测报文,如果对方没有响应则认为这个TCP连接已经死亡。
发送探测报文会有三种情况:
a、对方正常响应,那就说明TCP连接正常;
b、如果对方是崩溃后重启了程序,那么这时候对方会无法识别这个报文,就会返回一个RST,表示强制中断TCP连接;
c、如果对方真的崩溃了,那么多次探测报文都不会有响应,就可以认为TCP连接已经死亡,探测方就可以释放响应的资源了。
参考自:www.xiaolincoding.com
个人精简总结记录