君埋泉下泥销骨
我寄人间雪满头
又要捡起计算机网络了,其实原来我在github上就有个仓库,整理了这些基础知识。但是最近可能会有面试,准备象征性地复习一下。不知道为什么会有点开心,可能是修福报修久了,偶尔学习一下就会很开心。
进入正题!
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的通信协议,数据在传输前要建立连接,传输完毕后还要断开连接。
客户端在收发数据前要使用 connect() 函数和服务器建立连接。建立连接的目的是保证IP地址、端口、物理链路等正确无误,为数据的传输开辟通道。
TCP建立连接时要传输三个数据包,俗称三次握手(Three-way Handshaking)。可以形象的比喻为下面的对话:
- 套接字A:“你好,套接字B,我这里有数据要传送给你,建立连接吧。”
- 套接字B:“好的,我这边已准备就绪。”
- 套接字A:“谢谢你受理我的请求。”
TCP数据报结构
我们先来看一下TCP数据报的结构:
重点讲一下下面几个关键词:
- 数据序号:Seq序号占32位,用来标识从主机A到主机B的数据包的序号,发送数据时要进行标记
- 确认序号:Ack确认号,也是32位,客户端和服务端都可以发送,一般性地,Ack = Seq + 1
- 标志位:每个标志位占用1Bit,6个标志位分别为 URG、ACK、PSH、RST、SYN、FIN,具体含义如下:
- URG:紧急指针(urgent pointer)有效;
- ACK:确认序号有效;
- PSH:接收方应该尽快将这个报文交给应用层;
- RST:重置连接;
- SYN:建立一个新连接;
- FIN:断开一个连接。
对英文字母缩写的总结:Seq 是 Sequence 的缩写,表示序列;Ack(ACK) 是 Acknowledge 的缩写,表示确认;SYN 是 Synchronous 的缩写,愿意是“同步的”,这里表示建立同步连接;FIN 是 Finish 的缩写,表示完成。
多嘴两句,上面的数据序号以及确认序号和下面的标志位是对应的,比如现在发的是一个回复的ACK包,那么确认序号有对应的值以及下面的标志位ACK也要为1。
从数据报头可以看出来,在不包含报文的情况下,一个TCP数据包至少也是20字节。
连接的建立
客户端使用connect()函数申请建立连接时,会经过三次交互:
-
当客户端调用 connect() 函数后,TCP协议会组建一个数据包,并设置 SYN 标志位,表示该数据包是用来建立同步连接的。同时生成一个随机数字 1000,填充“序号(Seq)”字段,表示该数据包的序号。完成这些工作,开始向服务器端发送数据包,客户端就进入了
SYN-SEND
状态; -
服务端收到数据包后,会检查标志位,发现标志位中SYN置为1,就会直到这是客户端发送过来请求连接的,服务端也会创建一个数据包,并填充SYN和ACK标志位,TCP头部中序号仍旧是随机值,确认号则是之前接收到序号+1,然后回复客户端,然后服务端进入了
SYN-SEND
状态;; -
客户端收到数据包,发现标志位中ACK和SYN被置为1,就会检查ACK的确认号,确认服务端回复的确实是自己之前发出去的包;然后也会发最后的确认包,ACK标志位置为1,确认号为之前收到的SYN序号+1,客户端发出以后,就会进入
ESTABLISHED
状态,表示客户端这边算连接成功;服务端收到确认包后,也会进入ESTABLISHED
状态,表示服务端也算连接成功。
两端连接都成功后,后面就会开始收发数据。
为什么是三次握手?
个人理解,TCP是全双工的基于连接的可靠协议,可靠其实就是表示通信的通道是畅通的,就是客户端服务端双方都要确认自己的发送和接收通道是畅通的,所以都需要对方给个接收到信息后的确认信号,于是双方都需要测试发送-接收的过程,这样本来是需要四次,但服务端可以把申请连接和回复对方的确认放一起发出去,就成了3次。不知道在哪看到过一个评论是这样的,为了保证通信的可靠性,3次交互是理论上的最小值,也就是说这个其实不是TCP本身的要求,而是为了满足在“网络信道”这种不可靠传输方式上建立可靠传输所必须的交互。
试问,如果两次握手,会出现什么情况呢?
我这里想到两个可能的问题:
-
首先,就是无法确认全双工信道的可靠性。举个例子,如果缺少第三次确认,也就是服务端发送完第二个包后直接表示建立连接,而这个时候如果网络差错,导致这个包其实并没有发送到客户端,即客户端认为连接未建立,但服务端认为这个连接已经建立了,服务端就会持续阻塞住,浪费资源。
-
第二个就是对于失效的客户端连接,服务端也会直接建立连接,谢希仁的计算机网络上有讲,“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”,这里的错误其实指的就是服务端会建立一个或多个多余的连接。
其实本质上这两个问题就是``同一类问题,即不可靠传输导致的被动方无法确认的问题。