前言:在FPGA上实现TCP协议实际是一个不太好的计划,因为FPGA最擅长的是流水线处理方式,而TCP存在交互,导致FPGA需要进行反馈式的处理。但是由于项目新增的附加要求而又只能在FPGA上去实现TCP协议,才有了这篇总结。
关于在FPGA上实现TCP的具体过程在另外一篇随笔上进行详细介绍。
TCP报文格式:
源端口号(16bit) |
目的端口号(16bit) |
|||||||
序列号(32bit) |
||||||||
确认序列号(32bit) |
||||||||
首部长度 (4bit) |
保留(6bit) |
URG |
ACK |
PSH |
RST |
SYN |
FIN |
窗口(16bit) |
校验和(16bit) |
紧急指针(16bit) |
|||||||
选项和填充 |
||||||||
数据 |
ARP报文格式:
以太网目的地址(48bit) |
以太网源地址(48bit) |
帧类型 (16bit) |
|||||
硬件类型(16bit) |
协议类型(16bit) |
硬件地址长度(8bit) |
协议地址长度(8bit) |
操作码(16bit) |
发送者硬件地址(48bit) |
||
发送者IP地址(32bit) |
目标硬件地址(48bit) |
目标IP地址(32bit) |
TCP数据传输流程:
搭建的测试环境很简单,就是用一根网线将两台笔记本连接起来,其中一台电脑上模拟客户端,另一台电脑模拟服务端,用wireshark进行报文抓取。
测试地址如下:
交互端 |
MAC地址 |
IP地址 |
TCP端口号 |
服务器(Server) |
a0:88:69:8f:2f:64 |
192.168.1.124 |
6920 (0x1b08) |
客户(Client) |
a0:88:b4:77:46:44 |
192.168.1.115 |
3005 (0x0bbd) |
1. 地址解析(ARP)
(1)客户端向服务器发送一个寻址ARP包。
客户端发送ARP报文,寻找ip地址为192.168.1.115的主机的mac地址是多少。
(2)服务器收到ARP包后,向客户端发送一个带地址的ARP包。
服务端告诉客户端,我的mac地址是a0:88:69:8f:2f:64。
ARP结束(在TCP数据传输的后续过程中还会进行多次这样的ARP过程)。
2.TCP交互过程:
序列号 |
确认序列号 |
SeqNum |
AckNum |
2.1 建链(三次握手)
使用socket调试工具进行tcp调试。
STEP1:客户端向服务器发送一个数据包(SYN),请求建立连接。Flags=0x02
初始SeqNum(ISN)由客户端随机产生,AckNum为0。
[SYN]包:SeqNum=0x753bb0dd AckNum=0x00000000
STEP2:服务器收到SYN请求数据包后,对客户端进行确认。Flags=0x12
初始SeqNum(ISN)由服务器随机产生,AckNum=客户端SeqNum + 1。
[SYN,ACK]包:SeqNum=0x98d82910 AckNum=0x753bb0de
STEP3:客户端收到确认包后,再对服务器进行确认。Flags=0x10
SeqNum=收到上一帧的AckNum,AckNum=收到上一帧的SeqNum + 1。
[ACK]包:SeqNum=0x753bb0de AckNum=0x98d82911
2.2 数据传输(数据包+ACK包)
STEP1:发送数据:客户端向服务器发送一个带有数据的数据包。Flags=0x18
SeqNum=收到上一帧的AckNum,AckNum=收到上一帧的SeqNum。
STEP2:确认收到:服务器收到该数据包,向客户端发送一个确认包。Flags=0x10
SeqNum=收到上一帧的AckNum,AckNum=收到上一帧的SeqNum + payload字节长度。
(1)发送长度为10字节的数据
[PUSH,ACK]包:SeqNum=0x753bb0de AckNum=0x98d82911
[ACK]包:SeqNum=0x98d82911 AckNum=0x753bb0e8
(2)发送长度为14字节的数据
[PUSH,ACK]包:SeqNum=0x753bb0e8 AckNum=0x98d82911
[ACK]包:SeqNum=0x98d82911 AckNum=0x753bb0f6
2.3 断链(四次挥手)
STEP1:客户端完成数据传输后,主动向服务器发送一个终止包(FIN)。Flags=0x11
SeqNum=收到上一帧的AckNum,AckNum=收到上一帧的SeqNum。
[FIN,ACK]包:SeqNum=0x753bb101 AckNum=0x98d82911
STEP2:服务器收到终止数据包后,向客户端发送确认包进行确认。Flags=0x10
SeqNum=收到上一帧的AckNum,AckNum=收到上一帧的SeqNum + 1。
[ACK]包:SeqNum=0x98d82911 AckNum=0x753bb102
STEP3:服务器完成数据传输后,向客户端发送一个终止包(FIN)。Flags=0x11
SeqNum=收到上一帧的AckNum,AckNum=收到上一帧的SeqNum。
[FIN,ACK]包:SeqNum=0x98d82911 AckNum=0x753bb102
STEP4:客户端收到终止数据包后,向服务器发送确认包进行确认。Flags=0x10
SeqNum=收到上一帧的AckNum,AckNum=收到上一帧的SeqNum + 1。
[ACK]包:SeqNum=0x753bb102 AckNum=0x98d82912
3.TCP重传机制
TCP重传目的是防止报文的丢失。在使用TCP进行数据传输时,启动重传计时器(retransmission timer),在收到ACK确认报文后,停止重传计时器。TCP重传过程中有两个重要的参数,往返时间(RTT)和重传超时(RTO)。RTT是指发送端口从报文发出直至收到ACK确认报文所花费的时间。通过对RTT的值多次测量求平均来确定RTO的值。在默认配置下,Windows系统最大的重传次数为5次,Linux系统最大的重传次数为15次。
4. 分片、分段
4.1. 最大传输单元(MTU)
分片是针对IP协议存在的,IP报文的分片与重组是在网络层完成的。在以太网中,MTU=1500字节,对长度大于MTU的报文进行分片处理,分片后的IP报文不一定按序到达对端。
4.2. 最大分段大小(MSS)
分段是针对TCP协议存在的,TCP报文的分段与重组是在传输层完成的。MSS是指TCP报文每一次可以传输的最大数据段,TCP协议在三次握手建立连接的前两次握手中会协商双方的MSS值,MSS值的配置在TCP协议的选项字段中,TCP连接的双方会在握手中通过MSS值通知对方自己能够接收数据段的最大长度。
在一般情况下,MSS=MTU-IP首部长度-TCP首部长度=1500-20-20=1460字节。如果在三次握手的过程中不对MSS值进行配置,默认的MSS为536字节。
TCP的options字段:
5. 滑动窗口技术
由于FPGA资源有限,在FPGA中一般无法实滑动窗口机制。
在发送端将数据分为四类:在窗口之外的已经成功发送并且被确认的数据、在窗口内的已经发送但未被确认的数据、在窗口内的接收端允许发送但未发送的数据、在窗口之外的接收端不允许发送的数据。
在接收端将数据分为三类:已经接收并回复确认但未被上层应用接收的数据、已经接收但未回复确认的数据、等待接收的数据。
注:在使用TCP协议进行数据传输中,并不是对于每一个报文段都回复ACK包,而是有可能对两个甚至多个报文段只回复一个ACK包。