TCP/IP 通信

学习资料来源:正点原子STM32

目录

TCP/IP

TCP连接

TCP终止连接

MAC

LAN8720

DMA

LWIP内存分配

内存池

内存堆

数据包管理

pbuf介绍

数据包申请与释放

网络接口管理

ARP协议

 


TCP/IP

TCP是提供传输的可靠性的,TCP协议通过以下方式提供可靠性: 
1、应用数据会被分割成TCP认为最适合的发送的数据块,最大传输段MSS受MTU限制。(1500­
(40报头))lwipopts.h50行 
2、当TCP发出一个报文段后,会开启一个定时器,等待目的端确认收到这个报文。 
3、当TCP收到另一端发送过来的数据以后会想对方发送一个确认。 
4、采用分段传输的话到达目的主机的报文段可能顺序会乱,TCP会对这些报文段进行重新排序。 
5、TCP还提供流量控制。
TCP数据包结构:

URG紧急指针,代表帧数据紧急 
ACK确认序列号有效 
PSH尽快将报文交给应用层 
RST连接复位 
SYN发起连接 
FIN中止连接

TCP连接

TCP连接的建立就是著名的三次握手: 
1、客户端发送SYN标志置1 的TCP报文段,并且发送自己的初始序号ISN,假设为j。
2、服务器收到客户端发送的SYN包,然后返回一个SYN+ACK包。ACK报文包含了有效的确认序
号,这个值就是客户端发送的初始序号j+1。同时服务器还发送一个SYN报文,SYN表明服务器响应
连接,并且在报文中包含了服务器自身的初始序号ISN,假设为k。
3、客户端收到服务器发送来的SYN报文后会再次产生一个ACK报文,在这个ACK报文中包含了对
服务器发送来的SYN报文的有效确认号,该值为服务器发送的ISN加1,也就是k+1。

TCP终止连接

TCP连接断开是四次握手: 
1、首先发起方主动发起断开连接,发送FIN报文,用来关闭从客户端到服务器的数据传输,该报文
的序号字段假设为X。
2、服务器接收到客户端发来的FIN字报文,返回一个ACK报文,这个ACK报文的序号字段为X+1,
当客户端收到这个ACK后,那么客户端到服务器的连接就断开了。
3、服务器TCP向上层通知客户端断开操作,这样服务器会发送一个FIN报文来关闭服务器到客户端
的连接。FIN报文的序号字段假设为Y。
4、客户端也会返回一个ACK报文,此ACK报文的序号字段为Y+1。这样该方向上的连接也就断开
了。

TCP协议一共有11种状态,tcp.h文件中通过一个枚举类型

enum  tcp_state
{
CLOSED = 0, //连接断开,没有连接
LISTEN = 1, //服务器进入侦听状态,等待客户端的连接请求
SYN_SENT = 2, //连接请求已发送,等待确认
SYN_RCVD = 3, //已收到对方的连接请求
ESTABLISHED = 4, //连接已建立
FIN_WAIT_1 = 5, //程序已关闭该连接 FIN_WAIT_2 = 6, //另
一端已接收关闭该连接
CLOSE_WAIT = 7, //等待程序关闭连接
CLOSING = 8, //两端同时收到对方的关闭请求
LAST_ACK = 9, //服务器等待对方接受关闭操作
TIME_WAIT = 10 //关闭成功,等待网络中可能出现的剩余数据
};

MAC

红色框部分是stm32中的,蓝色框里面就是SMI接口,用于配置PHY,RMII和MII用于数据交换。 
F407的MAC有3种接口:SMI、MII和RMII。 
SMI称为站管理接口,程序中可以通过这个接口来访问PHY寄存器,SMI接口有两条线:数据线
MDIO和时钟线MDC,该接口支持访问多达32个PHY。
MDC:周期性时钟,提供以最大频率 2.5 MHz 传输数据时的参考时序,在空闲 状态下, SMI 管理
接口将 MDC 时钟信号驱动为低电平。 
MDIO:数据输入/输出比特流,用于通过 MDC 时钟信号向/从 PHY 设备同步传输状态 信息。
MII接口:介质独立接口 (MII) 定义了 10 Mbit/s 和 100 Mbit/s 的数据传输速率下 MAC 子层与 PHY
之间的互连。

TX_CLK和RX_CLK为发送和接收连续时钟,当速率为10Mbit/s时为2.5MHZ,速率为100Mbit/s时为
25MHZ。 TXD有4根,RXD也是,需要100M的时候,每根25M
RMII接口:介质独立接口 (RMII) 规范降低了 10/100 Mbit/s 下微控制器以太网外设与外部 PHY 间
的 引脚数。根据 IEEE 802.3u 标准, MII 包括 16 个数据和控制信号的引脚。 RMII 规范将引脚 数
减少为 7 个(引脚数减少 62.5%)。

LAN8720

确定双工连接方式,网速,连接状态等。支持自动选择最佳连接状态 
LAN8720支持通过RMII接口与以太网MAC层通信,内置10­BASE­T/100BASE­TX全双工传输模
块,支持10Mbps和100Mbps,LAN8720可以通过自协商的方式与目的主机最佳的连接方式(速度和
双工模式)
地址: 
SMI最多可以控制32个PHY芯片,通过不同的PHY芯片地址来对不同的PHY操作。LAN8720通
过设置RXER/PHYAD0引脚来设置其PHY地址,默认情况下为0,其地址设置如下表所示。我们
STM32F407开发板使用的是默认地址,也就是0X00。

寄存器:共有32个寄存器,IEEE 802.3定义了0~15这16个寄存器的功能,16~31寄存器由芯片
制造商自由定义。 
常用:BCR控制寄存器,BSR状态寄存器,31特殊功能寄存器(8720的工作状态,速度)

DMA

接收发送缓冲区都可以运用DMA传输。 
共有两个描述符:一个用于接收,一个用于发送。描述符是一种链表,最后一个描述符会指回第一
个描述符以构成环形结构。描述符列表位于主机的物理存储空间,两个列表的基址分别写入
ETH_DMARDLAR寄存器和ETH_DMATDLAR寄存器中。每个描述符最多可指向两个缓冲区,描述
符一共有两种结构:环形结构和链接结构。ST使用的链接结构

1、一个以太网数据包可以跨越一个或多个DMA描述符 
2、一个DMA描述符只能用于一个以太网数据包
3、DMA描述符列表中的最后一个描述符指向第一个,形成链式结构

typedef struct {
__IO uint32_t Status; //状态
uint32_t ControlBufferSize; //控制和buffer1,buffer2的长度
uint32_t Buffer1Addr; //buffer1地址
uint32_t Buffer2NextDescAddr; //buffer2地址或下一个描述符地址
//一下只有增强的以太网DMA描述符含有
#ifdef USE_ENHANCED_DMA_DESCRIPTORS
uint32_t ExtendedStatus; //增强描述符状态
uint32_t Reserved1; //保留
uint32_t TimeStampLow; //时间戳低位
uint32_t TimeStampHigh; //时间戳高位
#endif
} ETH_DMADESCTypeDef;

我们使用的是常规描述符。 
中文参考手册P865页,DMA描述符。

常规发送描述符:

由于是链式结构,TSES3表示下一个描述符的地址,由RDES0中的一个位决定
常规接收描述符: 
手册P873 

ST中的DMA描述符:

PHY芯片LAN8720为物理层,设置网线的传输速度,状态等。STM32自带的MAC层相当于数据链路层,用于配
置地址连接ARM板与计算机。LWIP提供网络层,传输层的功能,配置IP地址,将数据发送给不同IP。
PHY到MAC层通过DMA结构体,MAC打包成pbuf到LWIP

LWIP内存分配

内存池

使用内存池分配内存的优点在于速度快,效率高,不会产生内存碎片,但是缺点在于只能分配各种
固定大小的内存空间,LWIP必须实现知道用户要使用哪些类型的POOL,每种类型的POOL数量,
然后根据这个需求建立内存池。 
LWIP中的内存池(POOL)分配策略简单,但是内存的分配、释放效率高,可以有效的防止内存碎片
的产生。在内存的策略下用户只能申请固定大小的空间,内存池方法主要用于LWIP内核中固定数据
结构的分配,比如UDP控制块,TCP控制块等。LWIP内核在初始化的时候已经为每个数据结构类型
都初始化了一定数量的POOL,文件memp.c和memp.h就是内存池相关内容。内存池是以链表的形
式存在。
与内存池相关的数据结构: 
memp_t数据类型:用来给每个内存池取名字编号

typedef enum
{
MEMP_ RAW_PCB,
MEMP_ UDP_PCB,
MEMP_ TCP_PCB,
MEMP_ TCP_PCB_LISTEN,
MEMP_ TCP_SEG,
MEMP_ REASSDATA,
…….
MEMP_MAX
} memp_t;

memp_tab全局指针数组,数组中的元素为每类POOL的第一个POOL(每类POOL是以 链表的形式
存在)

static struct  memp *memp_tab[MEMP_MAX];

memp_sizes全局数组:每种类型POOL的大小

const u16_t memp_sizes[MEMP_MAX] =
{
LWIP_MEM_ALIGN_SIZE(sizeof(struct  raw_pcb )),
LWIP_MEM_ALIGN_SIZE(sizeof(struct  udp_pcb )),
LWIP_MEM_ALIGN_SIZE(sizeof(struct  tcp_pcb )),
LWIP_MEM_ALIGN_SIZE(sizeof(struct  tcp_pcb_listen )),
LWIP_MEM_ALIGN_SIZE(sizeof(struct  tcp_seg )),
…….
}

memp_num:
memp_num为一个全局数组,用来记录每类POOL中POOL的个数,每类POLOL链表中的结构体个
数,用户可以在lwipopts.h文件中定义,LWIP在opt.h中已经配置了默认值

const u16_t memp_num[MEMP_MAX] =
{
MEMP_NUM_RAW_PCB,
MEMP_NUM_UDP_PCB,
MEMP_NUM_TCP_PCB,
MEMP_NUM_TCP_PCB_LISTEN,
MEMP_NUM_TCP_SEG
……
};

memp_desc[]全局型指针数组: 
memp_desc为一个全局型指针数组,指向每类POOL的描述符 :memp_desc中的每个元素指向了
一个字符串,这些字符串在统计信息输出中可能用到。

static const char *memp_desc[MEMP_MAX] = {
("RAW_PCB"),
("UDP_PCB"),
("TCP_PCB"),
("TCP_PCB_LISTEN"),
("TCP_PCB_LISTEN"),
…….
};

memp_memory为一个数组,这个数组才是真正的内存池(每种内存池总的大小和,

static u8_t memp_memory
[
MEM_ALIGNMENT – 1
+((MEMP_NUM_RAW_PCB) * (MEMP_SIZE +
MEMP_ALIGN_SIZE(sizeof(struct  raw_pcb )) ))
+((MEMP_NUM_UDP_PCB) * (MEMP_SIZE +
MEMP_ALIGN_SIZE(sizeof(struct  udp_pcb )) ))
+((MEMP_NUM_TCP_PCB) * (MEMP_SIZE +
MEMP_ALIGN_SIZE(sizeof(struct  tcp_pcb )) ))
……..
];

内存堆

使用内存堆策略的话用户申请的内存大小有最小限制,所申请的内存大小不能小于MIN_SIZE,
LWIP默认的MIN_SIZE为12个字节。该值用户可以自行定义。使用内存堆策略,其有点事内存浪费
小,比较简单,适合于小内存的管理,但是缺点就是如果频繁的进行动态内存申请和释放的话,可
能会造成严重的内存碎片

数据包管理

pbuf介绍

LWIP在处理网络数据时,只能处理pbuf类型的数据,所以我们要将数据打包成pbuf结构。pbuf 的
结构:

struct pbuf
{
struct pbuf *next; //构成链表的时候指向下一个pbuf
void *payload; //指向数据缓冲区
u16_t tot_len; //pbuf链表中所有pbuf的数据长度
u16_t len; //当前bpuf中的数据长度
u8_t type; //pbuf类型
u8_t flags; //状态
u16_t ref;
};

注意其中tot_len表示当前pbuf长度加上后面所有pbuf长度。

pbuf有多种类型,不同的类型用于不同的场合,如下

typedef enum
{
PBUF_RAM, //pbuf数据紧跟着pbuf的结构存储,数据存储在ram中 ,用于网络发送数据
PBUF_ROM, //pbuf数据存储在rom中
PBUF_REF, //pbuf数据存储在ram中,但是与pbuf结构的位置无关
PBUF_POOL //pbuf结构和其数据存储在同一个内存池中,用于网络接收数据
} pbuf_type;

PBUF_RAM类型(待发送数据)

其中next与payload是指针,占32位,tot_len与len各占16位,type和flags为8位,ref16位 
箭头指向真实的缓存区,但是前面有一行空白是留给offset偏移,这个offset里面用来存储一些首部
字段,如TCP报文首部,IP首部等等

//p指向结构体前面,也就是指向结构体的地址
case PBUF_RAM:
p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE\
(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
if (p == NULL) {
return NULL;
}
p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF +
offset));
p->len = p->tot_len = length;
p->next = NULL;
p->type = type;
break;

PBUF_POOL类型(接收数据) 
既然PBUF_POOL类型是在内存池中申请的,那么就必须得有对应的POOL类型,在LWIP初始
化的时候就会自动的两类与pbuf相关的POOL:MEMP_PBUF和MEMP_PBUF_POOL(在
memp_std.h中),其中MEMP_PBUF是用于PBUF_REF和PBUF_ROM这两类的,
MEMP_PBUF_POOL是用于PBUF_POOL类型的。MEMP_PBUF和MEMP_PBUF_POOL的定
义在memp_std.h中102行 
事实上应用程序发送和接收的数据量可能很大,但是内存池类型的内存分配每次分配到的大小
是固定的,因此可能会需要进行多次分配,最终的分配成功的PBUF_POOL类型的pbuf如下图
(PBUF_POOL结构体后面包括了数据):

注意看,上图中只有第一个pbuf有offset,这是因为这都是一个数据包的,因此只需要一个offset来
存储有关数据包的信息,其他的pbuf就不需要了! 
修改pbuf内存池的大小与数量在lwipopts.h中36行
PBUF_ROM和PBUF_REF类型 
PBUF_ROM和PBUF_REF类型的pbuf空间也是从LWIP的内存池中申请得到的,分配方法都一
样的,他们使用内存池MEMP_PBUF,这两种类型申请的是指pbuf结构体的内存空间,并不包
含数据空间。这种内存池大小和数量在lwipopts.h中16行,因为没有数据内存区,所以内存池
大小为0

这两种类型的数据空间可以在其他地方储存。
在初始化内存池时,也会初始化两类与pbuf密切相关的POOL,他们的名字是MEMP_POOL和
MEMP_PBUF_POOL,前者是专门用来存放pbuf结构的,主要在PBUF_REF和PBUF_ROM中使用,
而后者主要是在PBUF_POOL中使用,不仅包含了pbuf结构,还包含数据的缓存空间

多种类型pbuf混合使用

数据包申请与释放

数据包在程序中动态申请内存来完成,pbuf的申请和释放通过函数pbuf_alloc()和pbuf_free()来完
成,pbuf_alloc()函数和pbuf_free()函数原型如下:

pbuf_alloc()函数有两个重要的参数:layer和type,layer决定是协议栈的哪一层申请的,type决定
申请的pbuf类型,layer决定了pbuf中的offset,也就是pbuf数据区中卫协议预留的首部空间

typedef enum {
PBUF_TRANSPORT,//传输层
PBUF_IP,//网络层
PBUF_LINK,//链路层
PBUF_RAW//原始层,不预留空间
} pbuf_layer;

网络接口管理

TCP/IP参考模型中网络分为4层:应用层、传输层、网络层和网络接口层。网络接口层就对应着我
们实际的网卡,8720PHY,stm32中的MAC构成了网络接口层。LWIP是网络层,利用结构体netif来
管理管理网卡。当上层有数据要发送的时候LWIP会从netif_list链表中选择一个合适的网卡来将数据
发送出去。
ntif结构体: 
next:该字段指向下一个neitif类型的结构体,因为LWIP可以支持多个网络接口,当设备有多个
网络接口的话LWIP就会把所有的netif结构体组成链表来管理这些网络接口。 
ipaddr,netmask和gw:分别为网络接口的IP地址、子网掩码和默认网关。 
input:此字段为一个函数,这个函数将网卡接收到的数据交给IP层。 
output:此字段为一个函数,当IP层向接口发送一个数据包时调用此函数。这个函数通常首先
解析硬件地址,然后发送数据包。 
linkoutput:此字段为一个函数,该函数被ARP模块调用,完成网络数据的发送。上面说的
etharp_output函数将IP数据包封装成以太网数据帧以后就会调用linkoutput函数将数据发送出
去。 
state:用来定义一些关于接口的信息,用户可以自行设置。 
mtu:网络接口所能传输的最大数据长度,一般设置为1500。 
hwaddr_len:网卡MAC地址长度,6个字节。 
hwaddr:MAC地址。 
flags:网络的接口状态,属性信息字段。 name:网卡的名字。 
num:此字段为协议栈为每个网络接口设置的一个编号,编号从0开始。

ARP协议

ARP协议是网络层的部分,两个主机之间的通讯是通过IP地址来通信,但是在底层数据链路层的数
据传输有自己的一套寻址机制,是通过MAC地址传输。简单说就是当一个主机另一个主机发送数据
时,它只知道目的主机的IP地址,而在协议底层接口发送数据时,需要将该IP地址转换为MAC地
址,才能在数据链路中将正确的数据包发送出去,而这种转换就是ARP协议。 
总结说就是TCP协议是以IP为地址寻址,而底层链路层的是以MAC地址寻址,每个网卡都有一个独
立的MAC。ARP就是位上层IP地址绑定各自的MAC地址,以便上层因特网可以灵活的使用IP地址通
信。 
ARP就是一个数据缓存表,由数据缓存表项组成。包括IP地址和MAC地址(主要),数据包发送控
制,数据缓存表项管理的状态和控制信息。

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值