高性能网络编程 - 白话TCP 三次握手过程
文章目录
- 概述
- TCP协议头的格式
- TCP Finite State Machine (FSM) 状态机
- 三次握手
- 如何在 Linux 系统中查看 TCP 状态
概述
每一个抽象层建立在低一层提供的服务上,并且为高一层提供服务。
我们需要知道
- TCP在网络OSI的七层模型中的第四层——Transport层 --------------- 第四层的数据叫Segment
- IP在第三层——Network层 ---------------在第三层上的数据叫Packet
- ARP在第二层——Data Link层,在第二层上的数据叫Frame
我们程序的数据首先会打到TCP的Segment中,然后TCP的Segment会打到IP的Packet中,然后再打到以太网Ethernet的Frame中,传到对端后,各个层解析自己的协议,然后把数据交给更高层的协议处理。
网络中传输的数据包由两部分组成:
- 一部分是协议所要用到的首部
- 另一部分是上一层传过来的数据。
首部的结构由协议的具体规范详细定义。在数据包的首部,明确标明了协议应该如何读取数据。反过来说,看到首部,也就能够了解该协议必要的信息以及所要处理的数据。
我们用用户 A 发送,用户 B 接受来说说明:
① 用户 A 应用程序处理
首先应用程序会进行编码处理产生报文/消息(message)交给下面的 TCP 层。
② 用户 A TCP 模块的处理
TCP 根据应用的指示,负责建立连接、发送数据以及断开连接。TCP 提供将应用层发来的
数据顺利发送至对端的可靠传输。为了实现这一功能,需要将应用层数据封装为报文段
(segment)并附加一个 TCP 首部然后交给下面的 IP 层。
③ 用户 A IP 模块的处理
IP 将 TCP 传过来的 TCP 首部和 TCP 数据合起来当做自己的数据,并在 TCP 首部的前端
加上自己的 IP 首部生成 IP 数据报(datagram)然后交给下面的数据链路层。
④ 用户 A 数据链路层的处理
从 IP 传过来的 IP 包对于数据链路层来说就是数据。给这些数据附加上链路层首部封装为
链路层帧(frame),生成的链路层帧(frame)将通过物理层传输给接收端。
⑤ 用户 B 数据链路层的处理
用户 B 主机收到链路层帧(frame)后,首先从链路层帧(frame)首部找到 MAC 地址判断
是否为发送给自己的包,若不是则丢弃数据。
如果是发送给自己的包,则从以太网包首部中的类型确定数据类型,再传给相应的模块,如
IP、ARP 等。这里的例子则是 IP 。
⑥ 用户 B IP 模块的处理
IP 模块接收到 数据后也做类似的处理。从包首部中判断此 IP 地址是否与自己的 IP 地址
匹配,如果匹配则根据首部的协议类型将数据发送给对应的模块,如 TCP、UDP。这里的例
子则是 TCP。
⑦ 用户 B TCP 模块的处理
在 TCP 模块中,首先会计算一下校验和,判断数据是否被破坏。然后检查是否在按照序号
接收数据。最后检查端口号,确定具体的应用程序。数据被完整地接收以后,会传给由端口
号识别的应用程序。
⑧ 用户 B 应用程序的处理
接收端应用程序会直接接收发送端发送的数据。通过解析数据,展示相应的内容。
TCP协议头的格式
- 1)TCP的包是没有IP地址的,那是IP层上的事,但是有源端口和目标端口。
- 2)一个TCP连接需要四个元组来表示是同一个连接(
src_ip, src_port, dst_ip, dst_port
)准确说是五元组,还有一个是协议。但因为这里只是说TCP协议,所以,这里我们只说四元组。
注意【TCP协议头】图:
- 1)
Sequence Number
:是包的序号,用来解决网络包乱序(reordering)问题。 - 2)
Acknowledgement Number
:就是ACK——用于确认收到,用来解决不丢包的问题。 - 3)
Window
:又叫Advertised-Window,也就是著名的滑动窗口(Sliding Window),用于解决流控的。 - 4)
TCP Flag
:也就是包的类型,主要是用于操控TCP的状态机的。
一般来说,不管计算机中有多少网卡,每个网卡都会有自己的 MAC 地址,这个MAC 地址是不会变化的。而每个网卡在正常工作的情况下,都会有一个 IP 地址,这个 IP地址完全是可以变化的。而这台计算机中承载的各种应用程序可以拥有自己的端口号,然后通过服务器的网卡,正确地进行网络通信。
一台服务器上的不同网络应用程序必须有不同的端口号,A 程序启动了使用了端口 x,B 程序启动就不能使用端口 x,否则会报错“Address already in use
”。
总的来说,操作系统是通过源 IP 地址、目标 IP 地址、协议号(协议类型)、源端口号以及目标端口号这五个元素唯一性的识别一个网络上的通信
TCP Finite State Machine (FSM) 状态机
http://www.tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF-2.htm
三次握手
TCP 提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作。
所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。
在 socket 编程中,这一过程由客户端执行 connect 来触发,所以网络通信中,发起连接的一方我们称为客户端,接收连接的一方我们称之为服务端
一开始,客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口,处于 LISTEN 状态
第一次握手:客户端将请求报文标志位 SYN 置为 1,请求报文的 Sequence Number 字段(简称 seq)中填入一个随机值 J,并将该数据包发送给服务器端,客户端进入 SYN_SENT 状态,等待服务器端确认。
客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态
第二次握手:服务器端收到数据包后由请求报文标志位 SYN=1
知道客户端请求建立连接,服务器端将应答报文标志位 SYN
和 ACK
都置为 1,应答报文的 Acknowledgment Number
字段(简称 ack)中填入 ack=J+1
,应答报文的 seq
中填入一个随机值 K
,并将该数据包发送给客户端以确认连接请求,服务器端进入 SYN_RCVD
状态。
服务端收到客户端的
SYN
报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。
第三次握手:客户端收到应答报文后,检查 ack 是否为 J+1,ACK 是否为 1,如果正确则将第三个报文标志位 ACK 置为 1,ack=K+1,并将该数据包发送给服务器端,服务器端检查 ack 是否为 K+1,ACK 是否为 1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED 状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。
客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入
server_isn + 1
,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED 状态。
服务端收到客户端的应答报文后,也进入 ESTABLISHED 状态。
从上面的过程可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的。
一旦完成三次握手,双方都处于 ESTABLISHED
状态,此时连接就已建立完成,客户端和服务端就可以相互发送数据了。
如何在 Linux 系统中查看 TCP 状态
在 Linux 可以通过 netstat -napt 命令查看