要理解三次握手,有必要知道TCP报文头的结构组成。这里直接引用:
源端口和目的端口,各占2个字节,分别写入源端口和目的端口;
序号,占4个字节,TCP连接中传送的字节流中的每个字节都按顺序编号。例如,一段报文的序号字段值是 301 ,而携带的数据共有100字段,显然下一个报文段(如果还有的话)的数据序号应该从401开始;
确认号,占4个字节,是期望收到对方下一个报文的第一个数据字节的序号。例如,B收到了A发送过来的报文,其序列号字段是501,而数据长度是200字节,这表明B正确的收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701;
数据偏移,占4位,它指出TCP报文的数据距离TCP报文段的起始处有多远;
保留,占6位,保留今后使用,但目前应都位0;
紧急URG,当URG=1,表明紧急指针字段有效。告诉系统此报文段中有紧急数据;
确认ACK,仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1;
推送PSH,当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1;
复位RST,当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接;
同步SYN,在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1;
终止FIN,用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放;
窗口,占2字节,指的是通知接收方,发送本报文你需要有多大的空间来接受;
检验和,占2字节,校验首部和数据这两部分;
紧急指针,占2字节,指出本报文段中的紧急数据的字节数;
选项,长度可变,定义一些其他的可选的参数。
再次引用一个动图来表示三次握手过程:
最开始的时候客户端和服务器都是处于CLOSED状态。主动打开连接的为客户端,被动打开连接的是服务器
三次握手的主要意义:初始化连接并同步连接双方的序列号与确认好,交换窗口信息。
下面正式介绍三次握手:
- TCP服务端首先建立TCB(传输控制块),进入LISTEN状态,等待客户端程序连接。
- TCP客户端第一次“握手”。客户端创建TCB,发送连接请求报文段,包含SYN=1,seq=x(选择一个初始序号),此时,客户端完成第一次握手,进入SYN-SENT状态(此次报文段不可携带数据,但是还是会消耗一个序号)
- 服务端开始第二次“握手”。服务端收到客户端的连接请求时,如果同意连接,那么将回送报文段,ACK=1,SYN=1,ack=x+1(确认号),seq=y(一个自己的初始序号),此时服务端进入SYN-RCVD状态。
- 客户端开始第三次“握手”。在接到服务端连接报文段时,客户端需回复一个报文段ACK=1,seq=x+1,ack=y+1。此时连接成功建立,客户端进入ESTABLISHED状态。
- 服务端收到第三次握手的报文时,同样进入ESTABLISHED状态,双方可以开始通信了。
为何不是二次握手?
直接回复是:为了避免已经失效的连接请求突然又被服务端接收到时,造成错误和资源浪费。
其实我们可以这样想:如果是两次握手,那么在某一时间,客户端发送了一个连接请求,然而由于网络拥塞,服务端一直没有收到该请求。那么在等待一段时间之后,客户端将重新发送一段新的连接请求(这就是可靠连接的超时重传)。假如此次连接完成了两次握手,并且在一系列数据传输之后,正常关闭。然而在关闭过后,前一次拥塞的请求发送到了服务端,由于服务端并不知道这是失效的,那么由于是两次握手,服务端完成了第二次握手,那么可以认为连接成功建立!这样就造成了错误和资源浪费。