知识点总结(二)POSIX API 、 tcp/ip网络协议栈、dpdk

1. POSIX API 网络相关

  1. socket函数: 用来获取一个listenfd的系统调用,相当于开启一个监听服务器;
  2. bind函数: 用来绑定listenfd和ip、端口等相关的协议地址;
  3. listen函数: 根据绑定的协议地址,用listenfd开启监听;
  4. accept函数: 接收客户端的连接请求,获取一个clientfd;
  5. connect函数: 这是客户端请求服务端建立连接的方法;
  6. send/recv函数: 当fd就绪之后,用来从socket缓冲区读写数据的方法;

2. 内核协议栈收数据包的流程

网络协议栈是抽象概念,内核协议栈是实现网络协议栈的具体表现

  1. 网卡收到数据包,通过DMA将数据包写到内存(ringbuffer);
  2. 网卡向cpu发起硬件中断,cpu根据中断调用中断处理函数;
  3. 中断函数会将硬件中断屏蔽掉(先执行完当前数据包接收后再开启),发起软件中断;
  4. 内核线程负责处理软中断函数,从ringbuffer中逐个取出数据到sk_buffer(此时数据包带有各层头部信息);
  5. 核心步骤: 将sk_buffer中的数据层层解析,去掉帧头帧尾、ip头、tcp头或udp头,然后根据五元组找到对应的socket
  6. 将sk_buffer解析出来的数据包复制 根据指针移动到socket缓冲区(此处不涉及数据拷贝),如果是epoll模型,则会触发socket对应的回调函数通知与其连接的fd的EPOLLIN就绪;

3. 连接建立 -三次握手

注意: 对于客户端调用connect函数起, 2 中的数据包收发过程就一直在使用(SYN、SYN-ACK、ACK包的收发),只不过不会有sk_buffer向socket缓冲区写数据的操作,因为建立连接的包本身不含有应用数据。

posix Api在三次握手中的函数: 首先客户端发起需要调用connect,服务端此时需要listen对应端口,等三次握手完成后才可调用accpet接收应用数据包

3.1 tcp(传输层控制协议)头结构

  1. Source port : 源端口;
  2. Destination port : 目标端口;
  3. seq Number: 请求序列号,表示在此次连接中第一个数据字节的序列号;后续也需要表示当前发送的数据字节序列号;
  4. ack Number : 确认序列号,仅当ACK标志位1的时候有效,表示期望下次收到的序列号 也就是seq Nmuber + 字节数 -1
  5. SYN:同步序号,用于建立连接;
  6. ACK: 确认号字段有效;
  7. window size: 窗口大小,用于流量控制,表示接收方当前可用接受缓冲区的大小,发送方可根据此字段控制发送速率

3.2 三次握手具体表现


前提是服务端已经调用了listen监听端口了,此时客户端调用connect发起连接请求

  1. 客户端发送SYN报文 : SYN = 1 , seq = x ;状态:closed -> syn_sent
  2. 服务端回复ACK报文 : SYN = 1 , seq = y ,ACK = 1 , ack = x +1 ; 状态 : listen -> syn_recv;将连接放入半连接队列
  3. 客户端回复ACK报文 : ACK = 1 ,ack = y + 1 ; 状态:syn_sent -> establish

服务端收到客户端的最后一次ack确认之后:状态变为establish,并且从半连接队列转移入全连接队列,等accept()调用之后再从全连接队列中取走!

3.3 为什么是三次,而不是两次或四次

① 本质上是以最小确认轮次问题,TCP需要双方各自确认:

  • 第一次握手 : 让服务端确认 -- 能收到客户端 ;并且告知自己序列号;
  • 第二次握手 : 让客户端确认 -- 能收到服务端,且服务端能收到客户端;告知自己序列化并确认对端序列号;
  • 第三次握手 : 让服务端确认 -- 客户端能收到服务端;确认对端序列号;

这样的话双方就都确认了 ① 自己和对端都能发送/接收数据 ② 互相确认初始序列号

② 为什么不是两次

  • 如果ACK-SYN包发送出去后服务端就进入establish,那如果该数据包客户端并没有收到,服务端就会进入一直等待且永远收不到数据的情况。这样会导致客户端和服务端的状态不一致!
  • 如果网络延迟,服务端收到一个旧的SYN并回复SYN-ACK,并建立连接,但是客户端发现这是超时放弃的就会发送RST,在服务端收到RST之前这个连接的建立会浪费资源。

③ 为什么不是四次

  • 第三次握手已经足够确认双方的收发能力了,第四次握手完全没必要属于资源浪费。

3.4 如果第三次握手的ACK包丢失了怎么办?

  1. 超时重传:
    • 当服务端发送SYN-ACK包后,会启动一个定时器,等待客户端的ACK;
    • 若超时未收到ACK,则会重传ACK-SYN (通常重传 5 次左右,总时间约 1 分钟);
  2. 客户端进入ESTABLISHED
    • 在服务端重传之前发送数据包到服务器,此时也会建立连接;
    • 收到超时重传的包,则继续完成ACK包的发送;
    • 若一直没有收到超时重传的包,且也不发送数据,那么会收到服务器的RST包,重新建立连接

3.5 半连接和全连接队列

  1. 半连接队列(SYN Queue) : 存放已完成第一次握手(收到 SYN)、但未完成第三次握手(未收到 ACK)的连接,状态为 SYN_RCVD;作用: 半连接队列用于防止服务端过早分配资源,只有收到 ACK 后才移入全连接队列。
  2. 全连接队列(Accept Queue) : 存放已完成三次握手、但未被应用程序 accept() 取走的连接,状态为 ESTABLISHED;作用: 全连接队列用于缓冲已完成连接,等待应用层取用。若队列满,后续连接可能被丢弃或拒绝。

3.6 什么是syn泛洪,如何防范

**SYN 攻击:**攻击者伪造大量不同源 IP 的 SYN 报文发送给服务器,服务器回复 SYN-ACK 后将这些半连接放入半连接队列,但攻击者不回复 ACK,导致半连接队列迅速占满,正常用户的 SYN 被丢弃,服务不可用。

防范措施:

  • 增大半连接队列
  • 缩短半连接存活时间
  • 启动syn cookie:syn收到后不分配资源,而是生成一个cookie作为序列号返回,客户端ack的时候鞋带,验证通过后建立连接
  • 防火墙过滤可疑ip

4. 连接断开 -四次挥手

首先确定,四次挥手可以由客户端发起,也可以由服务端发起!

以客户端发起断开为例,客户端close调用

  1. 第一次挥手(发送FIN报文):
    • 客户端: 调用 close(),发送 FIN 报文,序列号为 u(等于之前已传送数据的最后一个字节的序号加 1)。客户端状态由 ESTABLISHED 变为 FIN_WAIT_1,表示主动关闭连接,不再发送数据。
    • 服务端: 仍处于 ESTABLISHED,收到 FIN。
  2. 第二次挥手(发送ACK报文):
    • 服务端: 收到 FIN 后,立即回复 ACK 报文,确认号为 u+1。服务端状态由ESTABLISHED变为CLOSE_WAIT,表示被动关闭,此时应用程序还可以继续发送数据(半关闭状态)。
    • 客户端: 收到 ACK 后,状态由 FIN_WAIT_1 变为 FIN_WAIT_2,等待服务端的 FIN。
  3. 第三次挥手(发送FIN报文):
    • 服务端: 当应用程序也调用 close() 时,服务端发送 FIN 报文,序列号为 v(可能不同于 u),确认号仍为 u+1(因为之前已确认)。服务端状态由 CLOSE_WAIT 变为 LAST_ACK,等待客户端的最终 ACK。
    • 客户端: 处于 FIN_WAIT_2,等待 FIN。
  4. 第四次挥手(发送ACK报文):
    • 客户端: 收到服务端的 FIN 后,回复 ACK 报文,确认号为 v+1。客户端状态由 FIN_WAIT_2 变为 TIME_WAIT,进入等待状态(2MSL,即两倍最大报文段生存时间)。
    • 服务端: 收到 ACK 后,状态由 LAST_ACK 变为 CLOSED,连接彻底关闭。
    • 客户端: 在 TIME_WAIT 状态等待 2MSL 后,自动变为 CLOSED。

4.1 为什么不能合并为三次?

服务端有剩余数据未传递完毕: 服务端收到FIN时还有数据要发送,不能立即关闭;ack和syn中间可继续传输剩余数据

4.2 TIME_WAIT的作用(2MSL)?

确保最后的ACK被对方收到 ,如果丢失,服务端会重新发送FIN包;
让旧链接的报文在网络中消失 如果没有这个时间,新连接恰好用到了同样的四元组建立连接,并且序列化又恰巧出现在接受窗口,这样会扰乱新连接的数据。
1MSL问题: 因为1MSL只能保证一个方向报文消失,但是tcp是双向的。主动关闭方发送的最后一个 ACK 和被动力可能重传的 FIN 都需要时间消失,且报文可能来回传播,因此需要 2MSL 来覆盖两个方向的最大延迟。

5 滑动窗口 用来做流量控制

在接收端回复给客户端的tcp头部会有window这个字段,用来表示接收端还能最大接收的数据量,此时发送端会根据该字段调整三个指针的位置控制发送数据大小,实现流量控制。

6 超时重传

以三次握手的第二次为例:

服务端发送syn-ack包后,设置一个定时器时间设置为RTO =1秒(因为没有参考时间,linux默认),如果超过这个时间服务端没有收到客户端ack报文,则会重新发送syn-ack包,然后重置定时器但此时基于指数退避原则,每次超时重传后RTO =2 * RTO。

7. 拥塞控制

7.1 慢启动

初始的cwnd很小,可以为1,每次收到ack包后就翻倍,达到阈值或丢包为止。

7.2 拥塞避免

8. dpdk

8.1 什么是dpdk

由于在内核协议栈中 ① 数据包经过内核的DMA缓冲区拷贝到内核的sk_buf ,② sk_buff 通过指针移动到socket缓冲区后通过recv/read 拷贝到用户空间这两次拷贝,因此效率相对降低。DPDK可以拦截网卡数据,将数据包映射到在用户空间配置好的巨页中,省去了内核作为桥梁,但是需要自己实现数据包的解析:也就是用户态网络协议栈。

dpdk入门

相关推荐
主角1 72 小时前
Nginx性能优化与监控
网络·nginx·性能优化
NGC_66112 小时前
从URL输入到页面显示:浏览器背后的完整工作流程解析
网络
川石课堂软件测试2 小时前
接口测试需要注意的一些BUG
网络·数据库·python·单元测试·bug·压力测试·tornado
小糖学代码2 小时前
计算机网络理论:2.物理层
网络·计算机网络
Xzq2105092 小时前
Reactor模式
linux·网络
Yan-英杰3 小时前
远程控制软件哪个安全?2026 ToDesk/向日葵/RayLink加密、隐私与防护全面对比评测
网络·人工智能·网络协议·tcp/ip·http
爱丽_3 小时前
把 HTTP 讲清楚
网络·网络协议·http
NGC_66113 小时前
TCP可靠传输怎么实现的
服务器·网络·php
橘子133 小时前
ICMP协议
运维·服务器·网络