用dpdk实现udp、tcp数据包收发,tcp协议栈相关原理
一、用dpdk实现udp、tcp收发数据包
1.dpdk初始化--rte_eal_init(argc,argv),效果如下:
2.开辟内存池--rte_pktmbuf_pool_create
3.网口初始化--ustack_init_port(mbuf_pool):网卡数量、网口配置、队列配置、网卡启动
4.接收数据--rte_eth_rx_burst、解析以太网头、IP头、UDP/TCP头、拿到数据
循环接收数据包--逐个解析数据包(每个数据包包含:ether_hdr、ip_hdr、udp_hdr | tcp_hdr)
UDP解析:
TCP解析:
tcp头部长度怎么计算的?
data_off--8位(高四位:Header Lenght 、低四位-无关数据)
Header Lenght:表示偏移几个4字节,可以越过头部
为什么data_off右移4位能拿到高位?
如: 0101 0000(2进制)(十进制:5) 0x50(十六进制--0x前缀--1位十六进制代表4位2进制)
右移四位:0000 0101 (5) 0x05 左移四位:0000 0000 (0) 0x00
5*4(32位)=20字节 tcp头部为20字节 udp头为8字节
IP协议有total_len表示:IP头(20字节)+UDP总长(UDP头+data)| TCP总长(TCP头+data)
UDP协议有:dgram_len表示:UDP总长(UDP头+data)
为什么tcp协议没有记录长度?
因为tcp为流式传输
tcp数据包的长度可以通过:同端相邻发送的seqnum作差得到
若第一次seqnum=1234,第二次seqnum=1334---数据包大小为100
5.回发数据包(根据以太网头协议、IP协议、UDP协议/ TCP协议 -- 组装数据包 -- send)
UDP_SEND:
全局变量:
如:ip:192.168.202.134 -- 4字节 -- 32位
port:udp、tcp的port -- 2字节 -- 16位
获取两端mac、ip、port(组装msg的关键变量),组装msg,send
注:inet_ntoa(addr)--把网络字节序转为--人可读的点分十进制,如:192.168.202.134
参照网络协议:
以太网协议头:
IP协议:
UDP协议:
组装udp_msg:
TCP_SEND:
不同于UDP--TCP需要三次握手建立连接,并且建立连接后,涉及状态的切换
可引入全局变量:flags、seqnum、acknum查看数据数据包的状态
利用状态机:实现不同状态的不同行为
获取两端mac、ip、port;数据包的flags、seqnum、acknum
状态机控制行为:
TCP三次握手建立连接的关键是:正确的组织回发数据包
TCP协议:
组装tcp_msg:
关键在于:recv_ack、flags的正确组织
akc_num=seqnum+1 注意字节序的转换:seqnum -- ntohs-- +1 -- htons -- ack_num
二、TCP协议栈相关原理
对于数据发送:滑动窗口、慢启动、拥塞控制
对于数据接收:延时确认
1.滑动窗口、慢启动、拥塞控制
两端各维护两个窗口:数据接收窗口、数据发送窗口
接收窗口包含:已发送已确认的数据、已发送未确认的数据、未发送未确认的数据
当收到对端的acknum、window后,释放掉确认的数据,向后滑动窗口
acknum=1234,表示1234之前的包都收到了
window表示对端接收窗口大小
接收窗口大小动态变化:=min(cwnd,rwnd)
cwnd:发送方根据自身网络状况,设置的最大发送量
如:慢启动时:发送1,每收到一轮ack,发送量翻倍,cwnd逐渐增到,指数上涨
增加到一定阈值,为避免网络拥塞,进行拥塞控制,线性增长
当发生网络拥塞,cwnd减半,继续拥塞控制,线性增长
2.延时确认
由于发送速度的不同,并不是先发送到包先到,所有一个延时确认
如:发送方发送了100 101 102 ,接受方只接受到100 102 ,那回发acknum=101,100之后的包需要全部重发