lwip --- (十六)TCP建立流程

2023-05-16

  这一节我们就看看如何在我们的LWIP上实现一个http服务器的过程,结合连接建立过程来理解TCP状态转换图TCP控制块中各个字段的意义。这里先讲解一些与TCP相关的最基础的函数,至于是怎样将这些函数合理高效的组织起来以方便实际应用,这里先不涉及。

  第一个函数是tcp_new函数,该函数简单的调用tcp_alloc函数为一个连接分配一个TCP控制块tcp_pcbtcp_alloc函数首先为新的tcp_pcb分配内存空间,若内存空间不够,则函数会释放处于TIME-WAIT状态的TCP或者优先级更低的PCB(在PCB控制块的prio字段)以为新的PCB分配空间。当内存空间成功分配后,函数会初始化新的tcp_pcb的内容,源码如下:


if (pcb != NULL) {

	memset(pcb, 0, sizeof(struct tcp_pcb));      // 清0所有字段的值
	
	pcb->prio = TCP_PRIO_NORMAL;                 // 设置PCB的优先级为64,优先级在1~127之间
	pcb->snd_buf = TCP_SND_BUF;                  // TCP发送数据缓冲区剩余大小
	pcb->snd_queuelen = 0;                       // 发送缓冲中的数据包pbuf个数
	pcb->rcv_wnd = TCP_WND;                      // 接收窗口大小
	pcb->rcv_ann_wnd = TCP_WND;                  // 通告窗口大小
	pcb->tos = 0;                                // IP报头部TOS字段
	pcb->ttl = TCP_TTL;                          // IP报头部TTL字段
	pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;  // 设置最大段大小,不能超过536字节
	pcb->rto = 3000 / TCP_SLOW_INTERVAL;         // 初始超时时间值,为6s
	pcb->sa = 0;                                 // 估计出的RTT平均值??
	pcb->sv = 3000 / TCP_SLOW_INTERVAL;          // 估计出的RTT方差??
	pcb->rtime = -1;                             // 重传定时器,当该值大于rto时则重传发生
	pcb->cwnd = 1;                               // 阻塞窗口
	
	iss = tcp_next_iss();                        // iss为一个临时变量,保存该连接的初始数据序列号
	pcb->snd_wl2 = iss;                          // 上一个窗口更新时收到的ACK号
	pcb->snd_nxt = iss;                          // 下一个将要发送的数据编号
	pcb->snd_max = iss;                          // 发送了的最大数据编号
	pcb->lastack = iss;                          // 上一个ACK编号
	pcb->snd_lbb = iss;                          // 下一个将要缓冲的数据编号
	 
	pcb->tmr = tcp_ticks;                        // tcp_ticks是一个全局变量,记录了当前协议时钟滴答
	pcb->polltmr = 0;                            // 未解???
	
	#if LWIP_CALLBACK_API
	pcb->recv = tcp_recv_null;                   // 注册默认的接收回调函数
	#endif
	
	pcb->keep_idle  = TCP_KEEPIDLE_DEFAULT;
	
	#if LWIP_TCP_KEEPALIVE                       // 保活定时器相关设置。。未解??
	pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
	pcb->keep_cnt   = TCP_KEEPCNT_DEFAULT;
	#endif
	
	pcb->keep_cnt_sent = 0;

}

  上面有很多晕的地方,这些将在后续一一讲解。PCB中的还有一些函数字段如发送、接收函数等是在具体应用中初始化的。

  当一个新建的PCB被初始化好后,tcp_bind函数将会被调用,用来将IP地址及端口号与该TCP控制块绑定。该函数的输入参数很明显有三个,即TCP控制块、IP地址和端口号tcp_bind函数的工作也很简单,就是将两个参数的值赋值给TCP控制块中local_iplocal_port的字段。但这里有个前提,就是这个组合没有被使用。所以,函数需要先遍历各个pcb链表,以保证这个组合没有被其他PCB使用,这里的pcb链表有好几种:处于侦听状态的链表tcp_listen_pcbs、处于稳定状态的链表tcp_active_pcbs、已经绑定完毕的PCB链表tcp_bound_pcbs、处于TIME-WAIT状态的PCB链表tcp_tw_pcbs。如果遍历完这些链表后,都没有找到相应的对,则说明该对可用,则可进行上面说的赋值操作,最后,函数将这个PCB加入绑定完毕的PCB链表tcp_bound_pcbs

  上面一共说了四种PCB链表,现在看看它们各自用来链接了处于哪种状态的PCB控制块。tcp_bound_pcbs链表用来连接新创建的控制块,可以认为新建的控制块处于closed状态。tcp_listen_pcbs链表用来连接处于LISTEN状态的控制块,tcp_tw_pcbs链表用来连接处于TIME_WAIT状态的控制块,tcp_active_pcbs链表用来连接处于TCP状态转换图中其他所有状态的控制块。

  从状态转换图可以知,服务器端需进入LISTEN状态等待客户端的连接。因此,服务器端此时需要调用函数tcp_listen使相应TCP控制块进入LISTEN状态。可以直接的想象,要把一个控制块置为LISTEN状态很简单,先将其从tcp_bound_pcbs链表上取下来,将其state字段置为LISTEN,最后再将该PCB挂接到链表tcp_listen_pcbs。但事实上,LWIP的实现有一定的区别,它引入了一个叫tcp_pcb_listen的结构,该结构与tcp_pcb结构相近,但是去掉了其中在LISTEN阶段用不到的传输控制字段,这样tcp_pcb_listen的结构更小,更可以节省内存空间。所以,其实tcp_listen是这样做的,先申请一个tcp_pcb_listen的结构,然后将tcp_pcb参数中的有用字段拷贝进来,然后将这个tcp_pcb_listen的结构挂接到链表tcp_listen_pcbs

  到这里服务器就等待客户端发送来的SYN数据包进行连接了,要等待外面的数据包,这就和以前讨论过的ip_input函数相关了,ip_input函数会判断IP包头部的协议字段,并把TCP数数据包通过tcp_input函数传递到TCP层。SYN数据包当然是TCP层数据包,当然也要经过tcp_input函数进行处理并递交上层,现在就来看看tcp_input函数。

  tcp_input函数开始会对IP层递交进来的数据包进行一些基础操作,如移动数据包的payload指针、丢弃广播或多播数据包、数据和校验、提取TCP头部各个字段的值等等。接下来,函数根据接收到的TCP包的对遍历tcp_active_pcbs链表,寻找匹配的PCB控制块,若找到,则调用tcp_process函数对该数据包进行处理。若找不到,则再分别到tcp_tw_pcbs链表和tcp_listen_pcbs中寻找,找到则调用各自的数据包处理函数tcp_timewait_inputtcp_listen_input对数据包进行处理,若到这里都还未找到匹配的TCP控制块,tcp_input函数会调用函数tcp_rst向源主机发送一个TCP复位数据包

  这里我们的TCP控制块处于LISTEN状态,连接在tcp_listen_pcbs上,正在等待一个SYN数据包。因此,当等到该数据包后,函数tcp_listen_input应该被调用。从状态转换图上可以看出,处于LISTEN状态的TCP控制块只能响应SYN握手包,所以,tcp_listen_input函数对非SYN握手包返回一个TCP复位数据包若一个数据包不是SYN包,则其TCP包头中的ACK字段通常会被置1,所以tcp_listen_input函数是通过检验该位来实现的。接下来,函数通过验证SYN位来确认该包是否为SYN握手包。若是,则需要新建一个tcp_pcb结构,因为处于tcp_listen_pcbs上的控制块结构是tcp_pcb_listen结构的,而其他链表上的控制块结构是tcp_pcb结构的,所以这里新建一个tcp_pcb结构,并将相应tcp_pcb_listen结构拷贝至其中,同时在tcp_active_pcbs链表中添加这个新的tcp_pcb结构。这样新的TCP控制块就处在tcp_active_pcbs中了,注意此时的这个tcp_pcb结构的state字段应该设置为SYN_RCVD,表示进入了收到SYN状态。注意tcp_listen_pcbs链表中的这个tcp_pcb_listen结构还一直存在,它并不会被删除,以等待其他客户端的连接,服务器正是需要这样的功能。

  到这里,函数tcp_listen_input还没完。它应该从收到的SYN数据报中提取TCP头部中选项字段的值,并设置自己的TCP控制块。这里要被调到用的函数叫tcp_parseopt,它目前仅能够做的是提取选项中的MSS(最长报文大小)字段,在LWIP以后的更高版本中,该函数将被扩充,以支持更多的TCP选项。此后,函数还可以调用tcp_eff_send_mss来设置控制块中mss字段的值,该函数可直译为“有效发送最长报文大小”,所谓有效,就是指收到SYN数据包中的MSS值不能大于我的硬件支持的最大发送报文长度,即硬件的MTU。因此当收到的MSS值更大时,设置控制块中mss字段值会被设置为MTU,而不是MSS

  最后,函数需要向源端返回一个带SYN和ACK标志的握手数据包,并可以向源端通告自己的MSS大小。发送数据包是通过tcp_enqueuetcp_output函数共同完成的。关于数据包的发送,将在以后介绍。

  最最后,来看看函数tcp_listen_input内部的关键源代码部分,这几行代码涉及到TCP控制块内部各个字段值的设置,其中很重要的就是滑动窗口相关的字段

	ip_addr_set(&(npcb->local_ip), &(iphdr->dest));   // 复制本地IP地址
	npcb->local_port = pcb->local_port;               // 复制本地端口
	
	ip_addr_set(&(npcb->remote_ip), &(iphdr->src));   // 复制源IP地址
	npcb->remote_port = tcphdr->src;                  // 复制源端口
	npcb->state = SYN_RCVD;                           // 设置TCP状态
	npcb->rcv_nxt = seqno + 1;                        // 期望接收到的下一个字节序号
	npcb->snd_wnd = tcphdr->wnd;                      // 设置发送窗口大小
	npcb->ssthresh = npcb->snd_wnd;                   // 快速启动阈值设为和发送窗口大小相同??
	npcb->snd_wl1 = seqno - 1;                        // 该字段??
	npcb->callback_arg = pcb->callback_arg;           // 该字段??
	
	#if LWIP_CALLBACK_API
	npcb->accept = pcb->accept;                       // 接收回调函数
	#endif

  其中npcb表示新建的tcp_pcb结构,还有很多不懂的地方,为啥仅仅拷贝保留了这几个字段,其他字段直接被忽略?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

lwip --- (十六)TCP建立流程 的相关文章

  • 10、IEEE802.3和Ethernet II帧的区别, 0x0806 ARP协议分析

    一 IEEE802 3和Ethernet II帧的区别 概念 xff1a 1 以太网上使用两种标准帧格式 第一种是上世纪80年代提出的DIX v2格式 xff0c 即Ethernet II格式 第二种是1983年提出的IEEE 802 3格
  • W10: Warning: Changing a readonly file使用vi/vim报错问题解决(使用管理员也不能修改)

    使用vi vim编辑文件的时候出现W10 Warning Changing a readonly file报错 解决方法 xff1a 一 强制保存退出 xff1a wq 二 ll 查询文件属主 xff0c 使用属主赋予权限 chmod u
  • 使用Galileo SDK跨局域网遥控机器人

    原链接 GalileoSDK是由我们开发的机器人导航系统SDK 通过SDK用户能够方便的操控机器人 现在小强用户已经可以免费体验到SDK的功能了 SDK内部集成了物联网连接功能 设置完成后用户能够通过SDK跨局域网遥控和获取机器人状态 下面
  • zcu102网口不通

    新建工程采用的板子默认配置 xff0c 启动后执行命令 xff1b dmesg grep ethernet 1 545671 macb ff0e0000 ethernet Not enabling partial store and for
  • zcu102开发板实现 虚拟jtag

    开发板zcu102 xff1b petalinux2020 1 需要自备一个FMC转JTAG的子卡 xff1b 此篇记录自己实现的过程 xff0c 以备查询 xff1b 1 整个流程连接 xff1a https xilinx wiki at
  • 查看ubuntu系统的版本信息、内存大小及使用、磁盘大小等常用命令

    一 ubuntu系统的版本信息 xff1a 方法 步骤 xff1a 1 进入Ubuntu系统 xff0c 点击左侧图标栏中的黑框 xff0c 打开终端 xff1b 2 左侧没有终端图标的情况 xff0c 可以搜索按钮 图中左侧第一个 xff
  • CentOS7 无法进入图形界面 黑屏

    问题现象描述 xff1a CentOS有界面的系统 xff0c 开机后进入界面 xff0c 在开机界面背景为 7 的地方卡住 xff0c 然后界面就黑掉 解决办法 xff1a 1 开机后 xff0c 按下 ctrl 43 alt 43 F2
  • 重装win10提示“Windows无法安装到这个磁盘,选中的磁盘具有MBR分区表,在EFI系统上,windows只能安装...”

    背景 xff1a 因为GHOST系统会捆绑大量软件 xff0c 主页被强制修改 xff0c 并且系统还可能存在删减的可能性 xff0c 所以越来越多的用户考虑安装官方MSDN原版系统 xff0c 但是呢 xff0c 近期总是有用户称自己在安
  • 在petalinux下提示:Failed to menu config project component....

    现象 xff1a 在petalinux下配置硬件描述语言时 xff0c 提示错误 xff1a 命令 xff1a petalinux config get hw description 61 hdf path 错误提示 xff1a ERROR
  • petalinux下修改系统空间笔记

    该文档是自己记录的笔记 xff0c 方便后面自己查看 在petalinux设置系统空间中需要注意的点 xff01 1 在menuconfig界面下设置系统空间为了在内核启动输出显示 xff1b Subsystem AUTO Hardware
  • PetaLinux 去除用户登陆 (Login into target without pass word)

    PetaLinux 自动登录登陆 1 初始化 PetaLinux 运行环境 xff1a source PetaLinux安装目录 settings sh 2 更改 PetaLinux 配置 xff1a 1 cd 到项目工程目录下 xff1b
  • PetaLinux 使用外部代码 (u-boot)

    背景 xff1a 在petalinux编译工程时 xff0c 我们可能有修改u boot源码的需求 xff0c 这时就需要我们使用外部的代码进行编译 实现步骤 xff1a 在配置前 xff0c 请确认我们已经在github上把对应的uboo
  • 异常的抛出和处理

    自定义输出异常 package error import java util ArrayList public class Exception public static void main String args TODO Auto ge
  • 在ROS中使用UDP进行通信

    原链接 ROS的网络通信提供了两种方式 xff0c 一种是TCP协议 xff0c 一种是UDP协议 默认采用TCP进行通信 但是在实际的WIFI网络使用中发现用户经常反馈客户端和机器人连接中断且无法重新建立连接 在ROS wiki中官方也有
  • 网络编程TCP/IP和UDP以及HTTP协议

    OSI的七层模型和TCP IP的四层模型 TCP IP协议是从OSI的七层模型中简化出来的 四层模型的详图 什么是HTTP协议 HTTP称为 超文本传输协议 是一种基于应用层的通信协议 xff0c 它允许将超文本标记语言 HTML 文档从W
  • 网络字节序之大小端(字节序与比特序)

    引言 xff1a 最近在网上看了很多博客 xff0c 想要深入了解大小端问题 xff0c 主要是做毕设时 xff0c RTP包协议的结构体定义有两种方式 xff0c 即大端和小端 但是一些博客并没有讲到理解大小端的本质问题 xff0c 在这
  • C++的编译流程

    C C 43 43 的编译流程 编译流程分为四个阶段 xff1a 预处理 编译 汇编 链接 以Linux系统下g 43 43 编译为例 xff1a 通过g 43 43 的选项可以查看过程中的每一步 预处理 xff1a 处理一些 号定义的命令
  • 【Autoware规控】Lattice规划节点

    文章目录 1 Lattice规划介绍2 相关代码 1 Lattice规划介绍 Lattice Planner 是一种基于栅格地图的规划算法 xff0c 通过搜索和优化实现路径规划的目的 Lattice Planner 的核心思想是将路径规划
  • 【Autoware规控】OpenPlanner规划节点

    文章目录 1 OpenPlanner介绍2 相关代码 1 OpenPlanner介绍 OpenPlanner是Autoware种使用的一种运动规划算法 xff0c 通过对全局路径采样后生成一系列的候选路径 xff0c 结合矢量地图 传感器结
  • 【C++】8.高效编程:Effective C++学习

    Effective C 43 43 改变程序与设计的55个具体做法 文章目录 1 将C 43 43 视为federation of languages xff08 语言联合体 xff09 2 用consts enums 和 inlines取

随机推荐

  • 【Qt web】内嵌CEF制作浏览器

    文章目录 1 CEF介绍2 环境配置3 示例程序 1 CEF介绍 Qt自带QWebEngine模块 xff0c 可以快速实现浏览器 xff0c 但在实际使用中 xff0c 某些AMD显卡电脑运行使用了QWebEngine的qt软件 xff0
  • 【C++】9.web应用:oatpp-web框架入门

    说到web开发 xff0c 大家肯定会想到JS Python xff0c 甚至Java xff0c 但应该不会想到C 43 43 用C 43 43 开发web也不是不行 xff0c 这不 xff0c oatpp就是一个轻量 跨平台 高性能的
  • 【Python】Excel数据上下翻转

    遇到一个问题 xff0c 需要将excel表格的数据上下翻转 xff0c 不是升序或者降序 xff0c 不然就不需要程序来实现了 网上也看了有些插件有这个功能 xff0c 但插件过于老旧 xff0c 下载都有问题 记录一下程序实现的过程 x
  • 【IPOPT】Ubuntu安装IPOPT非线性优化求解器

    IPOPT Interior Point OPTimizer 是一款开源的非线性优化求解器 xff0c 在自动驾驶规控中会用到 xff0c 之前在这里卡了很久 xff0c 现在终于装上了 ipopt文档 xff1a https coin o
  • 使用systemback制作Ubuntu自定义系统镜像和系统备份

    原链接 xff1a https community bwbot org topic 167 运行测试平台 小强ROS机器人 Systemback是一个Ubuntu系统中用于发布自定义系统镜像和系统备份的软件 有时候我们对自己的Ubuntu做
  • orangePi3 TLS tf卡分区、格式化、手动挂载和开机自动挂载

    orangePi3 TLS tf卡分区 格式化 手动挂载和开机自动挂载 适用于所有linux系统 TF卡新建分区 查看磁盘情况 fdisk l root 64 orangepi3 lts fdisk l Disk dev mmcblk0 5
  • 【PyQt】PyQt5开发环境搭建

    PyQt是基于python来开发Qt可视化窗口的简称 xff0c Qt本身是基于C 43 43 开发 xff0c 性能较好 xff0c Qt与Python结合后 xff0c 在Python的支持下可以快速地开发桌面应用程序 文章目录 1 P
  • 【C++】9.GIS应用:开源GIS平台开发入门(MapServer+QGIS+PostGIS+OpenLayers)

    GIS地理信息处理相关 文章目录 1 GIS软件工具2 MapServer服务器3 QGIS桌面软件QGIS加载csv数据 4 PostGIS数据库5 OpenLayers JS 浏览器客户端 1 GIS软件工具 在GIS数据处理时 xff
  • 【C++】6.网络编程:socket实现通信(文字、语音)

    常见的通信方式有文本 语音 xff0c 下面用C 43 43 实现 xff1a 参考 xff1a https blog csdn net Robot hfut article details 102862052 https blog csd
  • 【ros】6.ros激光雷达SLAM(建图定位)

    百行业为先 xff0c 万恶懒为首 梁启超 文章目录 smirk 1 激光SLAM blush 2 二维激光SLAM satisfied 3 三维激光SLAM x1f60f 1 激光SLAM SLAM xff08 同步定位与地图构建 xff
  • 【ros】7.ros导航navigation(定位规划)

    物竞天择 xff0c 优胜劣汰 xff1b 苟不自新 xff0c 何以获存 梁启超 文章目录 smirk 1 ros导航 blush 2 2d导航 satisfied 3 3d导航 x1f60f 1 ros导航 ros机器人有个导航功能 x
  • 【两周年】我的创作纪念日(水)

    机缘 两年前的今天 xff0c 处于离职状态 xff0c 准备去另一个城市工作 xff0c 同时开始学习编程知识 IT技能 xff0c CSDN让我发现了一群热爱学习和分享的小伙伴 xff0c 也萌发了在这里扎根的想法 收获 不知不觉已经两
  • AI模型部署概述

    心口如一 xff0c 犹不失为光明磊落丈夫之行也 梁启超 文章目录 smirk 1 AI模型部署方法 blush 2 AI模型部署框架ONNXNCNNOpenVINOTensorRTMediapipe如何选择 satisfied 3 AI模
  • 【C++】1.语言基础:八股文

    心口如一 xff0c 犹不失为光明磊落丈夫之行也 梁启超 文章目录 smirk 1 语言基础内存分配指针参数传递和引用参数传递四种强制转换面向对象的三大特性并举例 define 和别名 typedef 的区别 blush 2 标准库STL介
  • 【VSLAM】ORB-SLAM3安装部署与运行

    心口如一 xff0c 犹不失为光明磊落丈夫之行也 梁启超 文章目录 smirk 1 ORB SLAM3介绍 blush 2 代码安装部署1 安装ros与opencv2 安装Pangolin作为可视化和用户界面3 安装Eigen3一个开源线性
  • 【Linux运维】ACPI BIOS Error问题解决

    今天帮朋友装个ubuntu系统 xff0c 遇到一个问题记录一下 报错与现象 xff1a ACPI BIOS Error 电脑花屏 解决方法 xff1a 插入启动盘 xff0c 当进入引导界面后 xff0c 键盘输入 e xff0c 编辑L
  • catkin_make的时候发生了什么

    原链接http community bwbot org topic 182 运行测试平台 小强ROS机器人 这是一个比较复杂的问题 xff0c 但是有时候会有莫名其妙的编译错误 xff0c 在找错误的过程中会非常需要了解这个过程 首先说一下
  • 【ros】8.有限状态机

    心口如一 xff0c 犹不失为光明磊落丈夫之行也 梁启超 文章目录 smirk 1 有限状态机认识 blush 2 一个简单的示例 satisfied 3 自动驾驶如何用有限状态机 x1f60f 1 有限状态机认识 有限状态机 xff08
  • 【C++】8.编译:CMake工具入门

    x1f60f xffe3 xffe3 x1f60f 这篇文章主要介绍CMake工具的入门使用 学其所用 xff0c 用其所学 梁启超 欢迎来到我的博客 xff0c 一起学习知识 xff0c 共同进步 x1f95e 喜欢的朋友可以
  • lwip --- (十六)TCP建立流程

    这一节我们就看看如何在我们的LWIP上实现一个http服务器的过程 xff0c 结合连接建立过程来理解TCP状态转换图和TCP控制块中各个字段的意义 这里先讲解一些与TCP相关的最基础的函数 xff0c 至于是怎样将这些函数合理高效的组织起