【TCP/IP协议详解】
一、TCP/IP协议简介
TCP/IP
传输协议,即传输控制/网络协议
,也叫作网络通讯协议
。它是在网络的使用中的最基本的通信协议。TCP/IP
传输协议对互联网中各部分进行通信的标准和方法进行了规定。并且,TCP/IP
传输协议是保证网络数据信息及时、完整传输的两个重要的协议。
TCP/IP
协议是Internet最基本的协议,其中应用层
的主要协议有Telnet
、FTP
、SMTP
等,是用来接收来自传输层的数据或者按不同应用要求与方式将数据传输至传输层;传输层
的主要协议有TCP
、UDP
,是使用者使用平台和计算机信息网内部数据结合的通道,可以实现数据传输与数据共享;网络层
的主要协议有ICMP
、IP
、IGMP
,主要负责网络中数据包的传送等;而网络访问层
,也叫网络接口层或数据链路层
,主要协议有ARP
、RARP
,主要功能是提供链路管理错误检测、对不同通信媒介有关信息细节问题进行有效处理等。
二、OSI七层参考模型
OSI
的参考模型共有7层,是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系。由低层至高层分别为:物理层
、数据链路层
、网络层
、传输层
、会话层
、表示层
、应用层
。
2.1 OSI七层参考模型与TCP/IP协议模型各层对应关系
三、TCP报文结构
下图为TCP首部结构定义,定义了TCP协议如何读取和解析数据。
3.1 TCP端口
TCP的连接需要四个要素来确定唯一的连接:源IP + 源端口、目的IP + 目的端口, 因此TCP首部预留了两个16位作为端口号的存储,IP地址由上一层IP协议负责传递。源端口和目的端口各占16位,即两个字节,因此端口号的范围为216=65536(0 ~ 65535)。此外0 ~ 1023端口是系统保留的,1023 ~ 65535是用户使用的端口范围。
3.2 TCP的序号和确认号
① 序号(Sequence number,缩写seq
,32位):用于标识数据包在数据流中的位置。发送数据包时数据比较大时则需要分片,序号则用来区分不同的分片并排序,确保了数据的顺序性。
② 确认号(Acknowledge number,缩写ack
,32位):确认数据是否被收到,一般为收到的序号值 + 1。只有 ACK 标志为 1 时确认序号字段才有效。
3.3 数据偏移
表示TCP报文首部长度, 即TCP报文段的数据起始处距离TCP报文段的起始处的长度。以4字节为单位,占4位,取值范围为0~15。由此可计算出TCP首部的最大字节数为 4 * 15 = 60
。TCP首部长度 - 20(固定首部长度) = 选项长度
。
如果数据偏移值为5,则整个TCP报文头的长度是
4 * 5 = 20
字节,相当于TCP首部不包含选项。
如果数据偏移值为15,则整个TCP报文头的长度是4 * 5 = 60
字节,相当于TCP首部中选项占40字节。
3.4 保留位
占6位,默认为0。
3.5 TCP控制位
URG
:紧急标志位,表示数据包的紧急指针生效,数据具有高优先级,优先传送,而不按照原先的队列顺序传送,督促中间设备尽快处理;
ACK
:确认标志位,对已接收的数据包进行确认;
PSH
:推送标志位,表示该数据包被对方接收后应立即交给上层应用,而不在缓冲区排队;
RST
:重置标志位,用于连接复位、拒绝错误和非法的数据包;
SYN
:同步标志位,用于建立会话连接,同步序列号;
FIN
:完成标志位,表示数据传输已经完成,即将关闭连接;
3.6 窗口
表示该报文的发送方能接受的字节数,即窗口大小,传输过程中双方可以动态调整窗口大小,即用来控制数据传输速率。一个TCP数据包大小,最大则为65535(216 - 1)字节,除去头部20字节,理论上可携带的最大数据量为65515字节。
3.7 检验和
用于验证数据完整性,确保数据未被修改。检验和覆盖了整个 TCP 报文段,包括 TCP 首部和 TCP 数据,发送端根据特定算法对整个报文段计算出一个检验和,接收端会进行计算并验证。
3.8 紧急指针
只在URG
标志位置1
的时候有效。表示紧急数据在TCP数据中的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。
3.9 选项
非必须
,长度可变,最大长度为40字节。用于在TCP头部中传输一些额外的控制信息。
3.10 填充
非必须
,填充字段,用来补位,使整个首部长度是4字节的整数倍。
四、TCP/IP协议的三次握手与四次挥手
4.1 TCP/IP协议的三次握手
名称 | 数据发送方向 | SYN(建立连接) | seq(自己的数据) | ACK(收到的请求) | ack(接收到的对方的数据) |
---|---|---|---|---|---|
第一次握手 | 客户端 --> 服务端 | 1 | x | - | - |
第二次握手 | 服务端 --> 客户端 | 1 | y | 1 | x + 1 |
第三次握手 | 客户端 --> 服务端 | - | x + 1 | 1 | y + 1 |
4.2 TCP/IP协议的四次挥手
名称 | 数据发送方向 | FIN(断开连接) | seq(自己的数据) | ACK(收到的请求) | ack(接收到的对方的数据) ack = seq + 1 |
---|---|---|---|---|---|
第一次挥手 | 客户端 --> 服务端 | 1 | u | - | - |
第二次挥手(期间仍存在数据传输) | 服务端 --> 客户端 | - | v | 1 | u + 1 |
第三次挥手 | 服务端 --> 客户端 | 1 | w | 1 | u + 1 |
第四次挥手 | 客户端 --> 服务端 | - | u + 1 | 1 | w + 1 |
4.3 常见问题
1. 为什么需要三次握手?
① 防止旧连接的混淆。 由于网络延迟等原因,可能会出现旧连接的数据包在新连接中被误认为是有效数据包的情况。
② 建立可靠连接。 三次握手才可以同步双方的初始序列号,序列号能够保证数据包不重复、不丢弃和按序传输。两次握手无法防止历史连接,也无法同步双方序列号。
③ 避免资源浪费。 三次握手可以防止未经授权的恶意连接,例如SYN洪泛攻击等。如果服务器收到的SYN包并没有相应的ACK包,那么服务器就不会认为这是一条有效的连接请求,从而防止恶意连接。
2. 为什么四次挥手的第二次和第三次挥手不能合并?
① 第二次挥手的目的是服务端告诉客户端,我(服务端)已经收到你(客户端)发送的包了。
② 第三次挥手的目的是服务端告诉客户端,我(服务端)的数据也已经发送完毕了,不会再发送数据了。
因此,第二次挥手主要是对第一次挥手的响应,第三次挥手主要是告知客户端,服务端已经不会再发送数据了。在第二次挥手和第三次挥手之间,服务端还是可以向客户端发送数据的, 因此第二次挥手和第三次挥手不能合并。
3. 三次握手和四次挥手的目的是什么?
①三次握手的目的是保证双方都准备好了资源,双方的发送和接收能力都正常。
②四次挥手的目的是保证双方的资源都能正常释放掉。
4. 服务端收到第四次挥手的数据包之前,会释放掉资源么?
不会, 即使服务端调用了
close()
,仍然会保留资源,直到收到客户端的发来的第四次挥手的数据包为止。当然,如果服务端一直没有收到第四次挥手的数据包,服务端会认为是不是第三次挥手的数据包丢失了,不断重试第三次挥手,重试一定次数,仍然没有收到,服务端直接断开连接。
5. 第三次挥手什么时候发?
服务端调用
close()
之后才能发。
6. TCP/IP协议在第四次挥手时为什么要等待2MSL
(最长报文段寿命,Maximum Segment Lifetime
)?
① 保证客户端发送的最后一个ACK报文段能够到达服务端。 因为该ACK有可能丢失,从而导致处在LAST-ACK状态的服务端收不到对FIN-ACK的确认报文。
② 防止"已失效的连接请求报文段"出现在本连接中。 客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现滞留在网络中的报文段。
7. 为什么客户端先调close(),而服务器不能先调?
① 先调用
close()
的一方需要等待2MSL之后才能释放资源,在这段时间期间时不允许用bind
的方式去重新绑定端口的,这时就会出现端口被占用的情况。
② 客户端的端口是由操作系统随机分配的,被占用之后会再重新分配一个,而服务端先调close()
,则需设定套接字选项,配置重复利用端口资源才可以。