当前位置: 首页 > news >正文

【LwIP源码学习3】TCP协议栈分析——数据接收流程

前言

本文介绍代码在lwip的tcp_in.c文件中,主要介绍TCP协议栈中数据的接收流程。

正文

1、一个正常的TCP数据,首先会传入到

tcp_input(struct pbuf *p, struct netif *inp)

函数,其中指针p指向传入的数据流。

2、从数据流中获取TCP头部

tcphdr = (struct tcp_hdr *)p->payload;

TCP头部数据结构为:
在这里插入图片描述
3、获取TCP头部中重要信息:

  tcphdr->src = lwip_ntohs(tcphdr->src);tcphdr->dest = lwip_ntohs(tcphdr->dest);seqno = tcphdr->seqno = lwip_ntohl(tcphdr->seqno);ackno = tcphdr->ackno = lwip_ntohl(tcphdr->ackno);tcphdr->wnd = lwip_ntohs(tcphdr->wnd);flags = TCPH_FLAGS(tcphdr);

seqno是序号,是发送方告诉接受方正在发送的报文段的序号。
ackno是确认号,是接收方告诉发送方已经接受到的最后一个报文段序号。
flags是6个控制位,其中的SYN与ACK在3次握手时使用如下:
在这里插入图片描述
客户端发起请求时,SYN为1,ACK为0。
服务器回复时,SYN为1,ACK为1。
客户端再次回复时,SYN为0,ACK为1。
ACK为0时,确认号无效(也就是ackno),建立连接后所有报文ACK置1。

flags中的FIN控制位置1,表示报文段的发送方数据已发送完毕,并要求释放运输连接。
释放TCP连接过程如下:
在这里插入图片描述

4、从 tcp_active_pcbs链表中寻找对应的pcb,寻找方式主要是看端口号和IP号是否能对应上:

if (pcb->remote_port == tcphdr->src &&pcb->local_port == tcphdr->dest &&ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) &&ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr()))

tcp_active_pcbs链表上的pcb都是正在等待接受数据或者发送数据的。

5、将输入数据流放到输入报文段里:

inseg.next = NULL;
inseg.len = p->tot_len;
inseg.p = p;
inseg.tcphdr = tcphdr;

因为lwip每次只处理一个输入数据流,所以输入报文段inseg是一个全局变量,同时也不会出现访问冲突。
报文段的管理如下:
在这里插入图片描述
tcp_active_pcbs链表上有很多活跃pcb,每个pcb有一个unsent指针和一个unacked指针,用于维护TCP协议中的发送窗口,unsent指向发送窗口中还未发送的报文段,unacked指向发送窗口中已经发送但是还没收到响应确认号的报文段。
发送窗口如下:
在这里插入图片描述

6、然后执行:

err = tcp_process(pcb);

进行TCP状态机处理。
TCP状态机转换过程如下:
在这里插入图片描述

7、一个已经建立连接的正常通信的pcb状态是ESTABLISHED
处理代码为:

case ESTABLISHED:tcp_receive(pcb);if (recv_flags & TF_GOT_FIN) { /* passive close */tcp_ack_now(pcb);pcb->state = CLOSE_WAIT;}break;

可以看到是调用tcp_receive()函数对输入数据做进一步处理。这时输入数据仍然在inseg输入报文段这个全局变量里。

8、接下来进入到tcp_receive()函数,首先根据新发送来TCP报文段中的窗口大小更新本pcb的发送窗口大小。

pcb->snd_wnd = SND_WND_SCALE(pcb, tcphdr->wnd);

9、查看接收到的报文段中显示的报文序号是否在接收窗口内:

if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,pcb->rcv_nxt + pcb->rcv_wnd - 1)) 

pcb->rcv_nxt表示下一个要接收的报文序号。
pcb->rcv_wnd表示接收窗口大小。
其中pcb在初始化时,设置接收窗口大小代码如下:

pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND);

在lwipopts.h文件中设置

#define TCP_WND                         (2*TCP_MSS)
#define TCP_MSS                         (1500 - 40) 

也就是窗口大小为两个报文段。

10、查看接收到的报文段序号是否就是期望的序号:

      if (pcb->rcv_nxt == seqno) 

如果是则更新期望报文段序号:

pcb->rcv_nxt = seqno + tcplen;

把收到的数据放到全局变量recv_data里:

recv_data = inseg.p;

11、然后回到tcp_input()函数
首先判断是否接受到了数据:

if (recv_data != NULL)

有数据的话将数据传给应用层:

TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);

然后再试试能不能发点数据出去:

tcp_output(pcb);
http://www.lryc.cn/news/460514.html

相关文章:

  • 【bug】finalshell向远程主机拖动windows快捷方式导致卡死
  • 基于SpringBoot剧本杀管理系统 【附源码】
  • Linux 命令 —— grep、tail、head、cat、more、less(查看日志常用命令)
  • 知识见闻 - 美国连线杂志
  • 多线程的状态及切换流程
  • [Python学习日记-47] Python 中的系统调用模块—— os 与 sys
  • Linux系统——lvm逻辑卷
  • 一键快捷回复软件助力客服高效沟通
  • 初识Linux之指令(二)
  • 在深度学习中,Epoch、迭代次数、批次大小(Batch Size)和学习速率(Learning Rate)是影响模型训练效果的重要超参数。
  • 研究学习的循环递进三段论
  • Linux下如何将代码提交至Gitee
  • 【MATLAB源码-第181期】基于matlab的32QAM调制解调系统频偏估计及补偿算法仿真,对比补偿前后的星座图误码率。
  • 24年856电子线路专业课考场回忆
  • el-table表格里面有一条横线
  • QT通过QLocalSocket和QSharedMemory实现进程间通信
  • Python中的数据可视化艺术:用Matplotlib和Seaborn讲故事
  • python机器学习(手写数字识别)
  • 如何针对项目中的技术难点准备面试?——黑马点评为例
  • ARP欺骗的多种手法
  • HCIA——one
  • 【vue】⾃定义指令+插槽+商品列表案例
  • 多线程——线程的等待通知
  • 模态与非模态的对话框
  • C语言练习
  • CyberRt实践之Hello Apollo(Apollo 9.0版本)
  • 【JavaScript】LeetCode:61-65
  • 【SpringAI】(一)从实际场景入门大模型——适合Java宝宝的大模型应用开发
  • 植物大战僵尸杂交版
  • live2d 实时虚拟数字人形象页面显示,对接大模型