Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
LWIP
tcp
TCP握手
Lwip TCP连接的握手与断开
发布于 2021-08-06 00:24:51 浏览:2285
订阅该版
[tocm] ## TCP连接 #### 1.1 TCP通信概述 TCP是一个用于可靠传输的、面向连接的通信协议,可以在复杂的网络环境中为端到端的数据流提供一个稳定、可靠的传输服务。TCP有一套严谨和完善的传输和异常处理机制,可以确保将数据完整无误的传输到了目标端。TCP和UDP都属于传输层的协议,这两个传输协议机制有比较大的差别。 UDP协议是想发就发,不关注数据是否到达或出错, 就好比是发摩尔斯电报,数据发出去了,对方是否接收到数据无法确认, 除非应用层编写一套通信机制;而TCP协议本身已经定义了完善的状态确认, 每次有效数据都有回应机制, 好比是打电话,对方有没有接听,有没有听到或想要断开通话都尽在掌握。 TCP 报文中每个字段如图所示: ![6-19110Q62344I5.gif](https://oss-club.rt-thread.org/uploads/20210806/b9b85bfab49b160c7642f51fd0f1af9b.gif) 每个TCP连接都有两个IP地址和两个端口号。 每个通信端都有一个IP地址和一个端口号做为标识。 SYN是TCP头部的一个选项位, 报文中含有此标志时, 代表要初始化一个TCP连接。 FIN是TCP头部的一个选项位, 发送报文中含有此标志时, 代表要关闭自己到对方方向的数据发送。 ACK也是TCP头部的一个选项位, 报文中含有此标志时, 表示这是一个确认报文, 报文中Ack字段的值是可用的。 Seq是TCP头部的一个32bit序列号, 用于标识发送端到接收端的数据流。 TCP连接通常分3个部分:建立连接、数据传输、断开连接。这里只讲述建立连接和断开连接过程。 #### 1.2 TCP建立连接 每个TCP连接都需要一个TCP控制块结构体tcp_pcb(宏展开后), 结构体内包含了TCP连接所需的各种信息, 如 ip地址和端口号, 连接状态和标志, 报文参数和数据指针等: ```c /** the TCP protocol control block */ struct tcp_pcb { /** common PCB members */ /* ip addresses in network byte order * ip_addr_t local_ip; ip_addr_t remote_ip; /* Socket options */ u8_t so_options; /* Type Of Service */ u8_t tos; /* Time To Live */ u8_t ttl /* link layer address resolution hint */ \ IP_PCB_ADDRHINT /** protocol specific PCB members */ type *next; /* for the linked list */ void *callback_arg; enum tcp_state state; /* TCP state */ //记录TCP连接所处的状态 u8_t prio; /* ports are in host byte order */ u16_t local_port /* ports are in host byte order */ u16_t remote_port; tcpflags_t flags; /* the rest of the fields are in host byte order as we have to do some math with them */ /* Timers */ u8_t polltmr, pollinterval; u8_t last_timer; u32_t tmr; /* receiver variables */ u32_t rcv_nxt; /* next seqno expected */ tcpwnd_size_t rcv_wnd; /* receiver window available */ tcpwnd_size_t rcv_ann_wnd; /* receiver window to announce */ u32_t rcv_ann_right_edge; /* announced right edge of window */ /* Retransmission timer. */ s16_t rtime; u16_t mss; /* maximum segment size */ /* RTT (round trip time) estimation variables */ u32_t rttest; /* RTT estimate in 500ms ticks */ u32_t rtseq; /* sequence number being timed */ s16_t sa, sv; /* @todo document this */ s16_t rto; /* retransmission time-out */ u8_t nrtx; /* number of retransmissions */ /* fast retransmit/recovery */ u8_t dupacks; u32_t lastack; /* Highest acknowledged seqno. */ /* congestion avoidance/control variables */ tcpwnd_size_t cwnd; tcpwnd_size_t ssthresh; /* sender variables */ u32_t snd_nxt; /* next new seqno to be sent */ u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last window update. */ u32_t snd_lbb; /* Sequence number of next byte to be buffered. */ tcpwnd_size_t snd_wnd; /* sender window */ tcpwnd_size_t snd_wnd_max; /* the maximum sender window announced by the remote host */ tcpwnd_size_t snd_buf; /* Available buffer space for sending (in bytes). */ #define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3) u16_t snd_queuelen; /* Number of pbufs currently in the send buffer. */ #if TCP_OVERSIZE /* Extra bytes available at the end of the last pbuf in unsent. */ u16_t unsent_oversize; #endif /* TCP_OVERSIZE */ /* These are ordered by sequence number: */ struct tcp_seg *unsent; /* Unsent (queued) segments. */ struct tcp_seg *unacked; /* Sent but unacknowledged segments. */ #if TCP_QUEUE_OOSEQ struct tcp_seg *ooseq; /* Received out of sequence segments. */ #endif /* TCP_QUEUE_OOSEQ */ struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */ #if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG struct tcp_pcb_listen* listener; #endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */ #if LWIP_CALLBACK_API /* Function to be called when more send buffer space is available. */ tcp_sent_fn sent; /* Function to be called when (in-sequence) data has arrived. */ tcp_recv_fn recv; /* Function to be called when a connection has been set up. */ tcp_connected_fn connected; /* Function which is called periodically. */ tcp_poll_fn poll; /* Function to be called whenever a fatal error occurs. */ tcp_err_fn errf; #endif /* LWIP_CALLBACK_API */ #if LWIP_TCP_TIMESTAMPS u32_t ts_lastacksent; u32_t ts_recent; #endif /* LWIP_TCP_TIMESTAMPS */ /* idle time before KEEPALIVE is sent */ u32_t keep_idle; #if LWIP_TCP_KEEPALIVE u32_t keep_intvl; u32_t keep_cnt; #endif /* LWIP_TCP_KEEPALIVE */ /* Persist timer counter */ u8_t persist_cnt; /* Persist timer back-off */ u8_t persist_backoff; /* KEEPALIVE counter */ u8_t keep_cnt_sent; #if LWIP_WND_SCALE u8_t snd_scale; u8_t rcv_scale; #endif }; ``` state是一个tcp_state型枚举变量, 它表示TCP所处的状态, 具体状态有: ```c enum tcp_state { CLOSED = 0, //关闭状态 LISTEN = 1, //监听状态, server端状态 SYN_SENT = 2, //建立TCP连接时, client发送SYN后的状态 SYN_RCVD = 3, //server端收到client的SYN报文后, 由listen状态转移过来 ESTABLISHED = 4, //创建TCP成功, 可以是client, 也可以时server FIN_WAIT_1 = 5, //主动断开者发送完FIN后的状态 FIN_WAIT_2 = 6, //主动断开者收到ACK后的状态 CLOSE_WAIT = 7, //被动断开者收到FIN之后, 自己发送FIN之前的状态 CLOSING = 8, //应该是主动断开者FIN_WAIT_1状态时,收到ACK前,先收到了对方FIN报文后的状态 LAST_ACK = 9, //被断开者发送FIN报文后的状态 TIME_WAIT = 10 //主动断开者收到被动断开者的FIN报文并回复ACK后的状态 }; ``` TCP建立连接时需要完成3个步骤,常称作三次握手: 1 客户端发送含SYN标志的报文,表示要建立一个连接, 初始化连接的同步序号seq; 2 服务器端接收到含SYN的报文后, 也将一条含SYN标志报文, 作为ACK回复给客户端, 一则表示收到客户端创建连接的请求,二则表示服务端也同时创建与客户端的连接; 3 客户端收到回复后再发送一条ACK报文, 表示收到服务端的创建连接请求。 不论是客户端还是服务端, 创建连接发送数据的时候数据包中都包含一个初始化序列号seq, 对方收到数据并回复时会将收到的seq加1作为ACK的数据。 这样发送端就可以判断是不是对本次数据的回复。 以后每次发数据都会在前一次seq的基础上+1作为本次的seq(注意, 建立连接和断开连接时seq是加1, 传输应用数据时seq加的是上次发送的数据长度)。 ![三次握手.png](https://oss-club.rt-thread.org/uploads/20210806/477346a674d54dd7130f1f6660010b70.png) 下图为三次握手抓包数据: ![连接.png](https://oss-club.rt-thread.org/uploads/20210806/99dabe0ed9b078c5067a36da8394a167.png) 客户端要建立网络连接时, 首先要发一个SYN标志的数据包。就是tcp_connect实现的,大概流程如下: ![af86128370b7228b13d1a2aa860a0aa8.png.webp](https://oss-club.rt-thread.org/uploads/20210806/af86128370b7228b13d1a2aa860a0aa8.png.webp) 服务端连接流程: ![da64d62d88398f476e4b5ea4c52274d4.png.webp](https://oss-club.rt-thread.org/uploads/20210806/da64d62d88398f476e4b5ea4c52274d4.png.webp) #### 1.3 TCP断开连接 TCP断开连接时需要4个步骤来完成断开, 俗称四次挥手: 1. 已经建立连接的其中一段想要断开连接,称为主动关闭方。其发送一个含FIN标志、seq值(假设为Q)和ACK值(假设为K, 来自上次收到的seq)的报文到连接对方,称为被关闭方; 2. 被关闭方收到含FIN标志的报文后,将K作为seq值,Q加1后作为ACK值回复给主动关闭方。 3. 被关闭方同时检查自身是否还有处理完的数据包,若没有则开始启动关闭操作,身份转变为主动关闭方,同样发送一个含FIN、seq(值仍为K)和ACK值(值仍为Q+1)初始化的报文到连接对方。 4. 原主动关闭方转变为被关闭方,收到报文后,回复一条seq为Q+1,ACK为K+1的报文。至此,完成TCP连接的断开。 ![四次挥手.png](https://oss-club.rt-thread.org/uploads/20210806/92510dfd2aaccc3053ec9fae0466b2f5.png) 下图为四次挥手抓包数据: ![断开.png](https://oss-club.rt-thread.org/uploads/20210806/e70c2cfbcffca372e430edf87d3bcd23.png) 正常的TCP断开连接一般是从ESTABLISHED状态开始, 就是说两个网络端已经建立了连接, 断开流程如下: 以下是主动关闭方流程: ![7b062d4169266a1936bb22aa28daa311.png.webp](https://oss-club.rt-thread.org/uploads/20210806/7b062d4169266a1936bb22aa28daa311.png.webp) 以下是被动关闭方流程: ![85289812cca056b0aa1100ae46185852.png.webp](https://oss-club.rt-thread.org/uploads/20210806/85289812cca056b0aa1100ae46185852.png.webp) TCP协议层的一些接口函数经过层层调用,最终对接到lwip的操作接口上。 ``` static const struct sal_socket_ops lwip_socket_ops = { inet_socket, lwip_close, lwip_bind, lwip_listen, lwip_connect, inet_accept, (int (*)(int, const void *, size_t, int, const struct sockaddr *, socklen_t))lwip_sendto, (int (*)(int, void *, size_t, int, struct sockaddr *, socklen_t *))lwip_recvfrom, lwip_getsockopt, //TODO fix on 1.4.1 lwip_setsockopt, lwip_shutdown, lwip_getpeername, inet_getsockname, inet_ioctlsocket, inet_poll, }; ``` ``` lwip接口又封装在此结构体内,lwip协议族 static const struct sal_proto_family lwip_inet_family = { AF_INET, AF_INET6, &lwip_socket_ops, &lwip_netdb_ops, }; ```
2
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
ZMQK_3560
这家伙很懒,什么也没写!
文章
2
回答
2
被采纳
0
关注TA
发私信
相关文章
1
RT-THREAD在STM32H747平台上移植lwip
2
{lwip}使能RT_LWIP_DHCP时可以获取到ip
3
stm32f103 LWIP 2.0.2 TCP收发问题
4
lwip2.1不重启修改IP
5
关于网络协议栈的测试
6
可否将LWIP升级到2.1.2 和 2.0.3?
7
socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
8
tcpclient 插拔网线问题?
9
两个tcpclient同时通讯可以吗?
10
SO_BINDTODEVICE 未定义该如何解决
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
keil_MDK
msh
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
812
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
2
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
2
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部