0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

lwip协议栈代码分析

傅里叶的猫 来源:傅里叶的猫 2023-10-29 17:37 次阅读

lwIP(Lightweight IP)是一个为嵌入式系统设计的轻量级TCP/IP协议栈。它旨在为资源受限的环境提供完整的网络协议功能,同时保持低内存使用和代码大小。由于其模块化的设计,开发者可以根据需要选择包含或排除特定功能,以满足特定应用的资源要求。

Xilinx的lwIP是基于开源lwIP TCP/IP协议栈的一个适应版本,专门为Xilinx的硬件平台,如Zynq-7000和MicroBlaze,进行了优化和集成。Xilinx为其硬件平台提供了lwIP的库,使得开发者可以轻松地在其FPGA和SoC设计中实现网络通信功能。

以lwip TCP Perf Client为例,这是一个fpga作为TCP Client,像TCP Server发送批量数据,并测试传输性能的例程。

255d8102-7634-11ee-939d-92fbcf53809c.png

TCP参数

先看几个TCP相关的参数

TCP_CONN_PORT表示TCP的端口号,在Server中,需要指定该端口号,如果发现tcp一直不通,但ping是可以通的,多半原因是这个端口被占用了;

TCP_SERVER_IP_ADDRESS表示TCP Server的IP地址

2571fede-7634-11ee-939d-92fbcf53809c.png

FPGA的IP地址是在main.c里面指定的:

258f1e88-7634-11ee-939d-92fbcf53809c.png

如果TCP Server使用网络调试助手接收数据,设置如下:(需要注意,本地端口号应该是5001,跟代码中匹配)

259e9386-7634-11ee-939d-92fbcf53809c.png

main函数

main函数的内容如下:

intmain(void)
{
structnetif*netif;

/*themacaddressoftheboard.thisshouldbeuniqueperboard*/
unsignedcharmac_ethernet_address[]={
0x00,0x0a,0x35,0x00,0x01,0x02};

netif=&server_netif;
#ifdefined(__arm__)&&!defined(ARMR5)
#ifXPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT==1||
XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT==1
ProgramSi5324();
ProgramSfpPhy();
#endif
#endif

/*DefinethisboardspecificmacroinorderperformPHYreset
*onZCU102
*/
#ifdefXPS_BOARD_ZCU102
IicPhyReset();
#endif

init_platform();

xil_printf("

");
xil_printf("-----lwIPRAWModeTCPClientApplication-----
");

/*initializelwIP*/
lwip_init();

/*Addnetworkinterfacetothenetif_list,andsetitasdefault*/
if(!xemac_add(netif,NULL,NULL,NULL,mac_ethernet_address,
PLATFORM_EMAC_BASEADDR)){
xil_printf("ErroraddingN/Winterface
");
return-1;
}

netif_set_default(netif);

/*nowenableinterrupts*/
platform_enable_interrupts();

/*specifythatthenetworkifisup*/
netif_set_up(netif);
assign_default_ip(&(netif->ip_addr),&(netif->netmask),&(netif->gw));
print_ip_settings(&(netif->ip_addr),&(netif->netmask),&(netif->gw));

xil_printf("
");

/*printappheader*/
print_app_header();

/*starttheapplication*/
start_application();
xil_printf("
");

while(1){
if(TcpFastTmrFlag){
tcp_fasttmr();
TcpFastTmrFlag=0;
}
if(TcpSlowTmrFlag){
tcp_slowtmr();
TcpSlowTmrFlag=0;
}
xemacif_input(netif);
transfer_data();
}

/*neverreached*/
cleanup_platform();

return0;
}

在main函数中,首先就是定义各种网口接口相关的变量,并定义了MAC地址。

netif

这个netif的指针,需要多关注一下。

在lwIP中,netif(网络接口)是一个核心的结构体,它代表了一个网络接口,例如以太网接口、Wi-Fi接口等。netif结构体用于定义和管理这些接口,使lwIP可以在多个接口上运行并进行路由决策。

具体来说,netif结构体包括了以下几个主要的部分:

硬件地址:例如MAC地址。

IP地址、子网掩码和网关:这些用于IP层的路由和地址决策。

状态标志:表示接口的状态,例如是否激活、是否为默认接口等。

输入和输出函数指针:这些函数用于处理从该接口接收到的数据包或向该接口发送数据包。

其他驱动特定的数据:例如用于DMA的描述符、缓冲区等。

当你在lwIP中添加一个新的网络接口时,你通常会初始化一个netif结构体并使用netif_add()函数将其添加到lwIP的接口列表中。这样,lwIP就可以开始在该接口上接收和发送数据包了。

简而言之,netif是lwIP中用于表示和管理网络接口的关键结构体。

init_platform

在init_platform()函数中,初始化定时器和中断。

25b878c8-7634-11ee-939d-92fbcf53809c.png

接下来就是lwip的初始化,这三个初始化都是在platform的库里面写好的,直接调用就行。

xemac_add

后面xemac_add的原型如下,可以简单理解为设置网口的mac地址,此处没有设置IP的信息,可以看到传进去的参数都是NULL。

structnetif*
xemac_add(structnetif*netif,
ip_addr_t*ipaddr,ip_addr_t*netmask,ip_addr_t*gw,
unsignedchar*mac_ethernet_address,
UINTPTRmac_baseaddr)

netif_set_default

netif_set_default函数在lwIP中用于设置默认的网络接口。在一个系统中可能存在多个网络接口,但通常只有一个被视为默认接口。当lwIP需要发送数据包,但不知道应该通过哪个接口发送时,它会选择默认接口。

函数原型如下:

/**
*@ingroupnetif
*Setanetworkinterfaceasthedefaultnetworkinterface
*(usedtooutputallpacketsforwhichnospecificrouteisfound)
*
*@paramnetifthedefaultnetworkinterface
*/
void
netif_set_default(structnetif*netif)
{
LWIP_ASSERT_CORE_LOCKED();

if(netif==NULL){
/*removedefaultroute*/
mib2_remove_route_ip4(1,netif);
}else{
/*installdefaultroute*/
mib2_add_route_ip4(1,netif);
}
netif_default=netif;
LWIP_DEBUGF(NETIF_DEBUG,("netif:settingdefaultinterface%c%c
",
netif?netif->name[0]:''',netif?netif->name[1]:'''));
}

其中,netif是你希望设置为默认的网络接口的指针。

当你调用这个函数时,传入的netif结构体会被设置为默认网络接口。这意味着,除非有特定的路由决策指示其他接口,否则所有的出站数据包都会通过这个接口发送。

例如,如果你有一个以太网接口和一个Wi-Fi接口,并且你希望所有的通信默认通过Wi-Fi接口进行,那么你会在初始化Wi-Fi接口后调用netif_set_default函数,并传入Wi-Fi接口的netif结构体指针。

这个函数对于确保正确的网络通信行为非常重要,特别是在存在多个网络接口的系统中。

platform_enable_interrupts

这个函数就很容易理解了,就是使能中断,函数原型如下:

voidplatform_enable_interrupts()
{
/*
*Enablenon-criticalexceptions.
*/
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
XScuTimer_EnableInterrupt(&TimerInstance);
XScuTimer_Start(&TimerInstance);
return;
}

netif_set_up

netif_set_up函数在lwIP中用于激活一个网络接口。当你初始化一个网络接口并准备好开始接收和发送数据时,你需要调用这个函数来标记该接口为"up"状态。

函数原型如下:

void
netif_set_up(structnetif*netif)
{
LWIP_ASSERT_CORE_LOCKED();

LWIP_ERROR("netif_set_up:invalidnetif",netif!=NULL,return);

if(!(netif->flags&NETIF_FLAG_UP)){
netif_set_flags(netif,NETIF_FLAG_UP);

MIB2_COPY_SYSUPTIME_TO(&netif->ts);

NETIF_STATUS_CALLBACK(netif);

#ifLWIP_NETIF_EXT_STATUS_CALLBACK
{
netif_ext_callback_args_targs;
args.status_changed.state=1;
netif_invoke_ext_callback(netif,LWIP_NSC_STATUS_CHANGED,&args);
}
#endif

netif_issue_reports(netif,NETIF_REPORT_TYPE_IPV4|NETIF_REPORT_TYPE_IPV6);
#ifLWIP_IPV6
nd6_restart_netif(netif);
#endif/*LWIP_IPV6*/
}
}

其中,netif是你希望激活的网络接口的指针。

当你调用netif_set_up函数时,它会执行以下操作:

设置netif结构体中的flags字段,标记该接口为"up"状态。

如果配置了lwIP的相关回调,例如NETIF_STATUS_CALLBACK,那么这些回调函数也会被触发,通知应用程序该接口的状态已经改变。

通常,在你完成网络接口的硬件初始化、分配了必要的资源,并确信接口已经准备好进行通信后,你会调用netif_set_up函数。这样,lwIP就知道它可以开始在该接口上接收和发送数据包了。

相反地,如果你需要将一个接口标记为"down"状态,例如在接口遇到错误或需要进行维护时,你可以调用netif_set_down函数。这会告诉lwIP停止在该接口上的通信,直到接口再次被设置为"up"状态。

assign_default_ip

从名字也可以看到出来,就是设置ip地址、Netmask和gate way

函数原型也非常直观,不做过多解释了

staticvoidassign_default_ip(ip_addr_t*ip,ip_addr_t*mask,ip_addr_t*gw)
{
interr;

xil_printf("ConfiguringdefaultIP%s
",DEFAULT_IP_ADDRESS);

err=inet_aton(DEFAULT_IP_ADDRESS,ip);
if(!err)
xil_printf("InvaliddefaultIPaddress:%d
",err);

err=inet_aton(DEFAULT_IP_MASK,mask);
if(!err)
xil_printf("InvaliddefaultIPMASK:%d
",err);

err=inet_aton(DEFAULT_GW_ADDRESS,gw);
if(!err)
xil_printf("Invaliddefaultgatewayaddress:%d
",err);
}

start_application

start_application函数是一个启动网络应用的函数。在很多lwIP的示例应用中,这个函数被用来初始化和启动特定的网络应用,例如启动一个HTTP服务器、TCP客户端、UDP回声服务等。具体的功能和行为取决于应用的需求和设计。这个函数可能会初始化所需的网络资源,设置回调函数,并开始监听网络事件。

初始化变量:函数开始时,初始化了一些变量,如err用于错误处理,pcb代表TCP控制块,remote_addr用于存储远程服务器的IP地址,以及一个循环计数器i。

设置远程服务器的IP地址

如果启用了IPv6(LWIP_IPV6==1),则使用inet6_aton函数将TCP_SERVER_IPV6_ADDRESS字符串转换为IPv6地址格式并存储在remote_addr中。

如果未启用IPv6,则使用inet_aton函数将TCP_SERVER_IP_ADDRESS字符串转换为IPv4地址格式。

检查IP地址的有效性:如果IP地址转换失败,函数会打印错误消息并返回。

创建TCP控制块(PCB):使用tcp_new_ip_type函数为客户端创建一个新的TCP控制块。

连接到远程服务器:使用tcp_connect函数尝试连接到远程服务器的指定IP地址和端口TCP_CONN_PORT。如果连接成功,tcp_client_connected回调函数将被注册,以便在连接建立后进行处理。

错误处理:如果在上述步骤中出现任何错误,函数会打印相应的错误消息并关闭TCP连接。

初始化发送缓冲区:为send_buf缓冲区填充数据,数据内容是0到9的数字字符。

总的来说,start_application函数的主要目的是初始化一个TCP客户端,尝试连接到指定的远程服务器,并准备发送数据。

函数原型如下:

voidstart_application(void)
{
err_terr;
structtcp_pcb*pcb;
ip_addr_tremote_addr;
u32_ti;

#ifLWIP_IPV6==1
remote_addr.type=IPADDR_TYPE_V6;
err=inet6_aton(TCP_SERVER_IPV6_ADDRESS,&remote_addr);
#else
err=inet_aton(TCP_SERVER_IP_ADDRESS,&remote_addr);
#endif/*LWIP_IPV6*/

if(!err){
xil_printf("InvalidServerIPaddress:%d
",err);
return;
}

/*CreateClientPCB*/
pcb=tcp_new_ip_type(IPADDR_TYPE_ANY);
if(!pcb){
xil_printf("ErrorinPCBcreation.outofmemory
");
return;
}

err=tcp_connect(pcb,&remote_addr,TCP_CONN_PORT,
tcp_client_connected);
if(err){
xil_printf("Errorontcp_connect:%d
",err);
tcp_client_close(pcb);
return;
}
client.client_id=0;

/*initializedatabufferbeingsentwithsameasusediniperf*/
for(i=0;i< TCP_SEND_BUFSIZE; i++)
  send_buf[i] = (i % 10) + '0';

 return;
}

tcp_fasttmr和tcp_slowtmr

在lwip的TCP视线中,快速定时器(tcp_fasttmr)和慢速定时器(tcp_slowtmr)都是为了TCP连接的维护而存在的,但它们关注的方面和执行频率是不同的。

运行频率

快速定时器:通常每250毫秒被调用一次(这是默认值,但可以配置)。

慢速定时器:通常每500毫秒被调用一次(这也是默认值,但同样可以配置)。

关注的方面

连接的生命周期管理:例如,关闭那些已经结束但还没有完全关闭的连接。

持续活动检测:例如,检查长时间没有活动的连接,并可能发送探测数据段来检查对方是否仍然活跃。

超时管理:管理那些因为长时间没有响应而需要关闭的连接。

拥塞控制:调整窗口大小和其他与流量控制相关的参数。

重传管理:如果一个数据段没有得到确认,它会被重新发送。快速定时器负责处理这些重传。

延迟确认:TCP不会立刻确认每一个接收到的数据段,而是稍作延迟,以期待有数据可以与确认一同发送,从而减少网络的数据包数量。快速定时器可以触发这些延迟确认的发送。

快速定时器 (tcp_fasttmr)

主要关注:

慢速定时器 (tcp_slowtmr)

主要关注:

简而言之,快速定时器主要关注与数据传输直接相关的事务,如重传和确认,而慢速定时器则更多地关注连接的维护、超时和流控制。

tcp_write

tcp_write 函数用于将数据排入到一个TCP连接的发送队列。它是应用程序与 lwIP TCP层之间的一个关键接口,允许应用程序发送数据到其TCP连接。

以下是关于 tcp_write 函数的一些关键点:

非阻塞:与某些TCP/IP实现不同,tcp_write 是非阻塞的。这意味着,如果当前没有足够的可用缓冲区来容纳你想发送的数据,函数将不会阻塞,而是返回一个错误。

排队,不是直接发送:当你调用 tcp_write 时,你实际上是将数据放入发送队列,而不是立即发送数据。真正的数据传输将在后续的 lwIP 处理中进行,这可能涉及与其他TCP机制的交互,如拥塞控制。

参数:该函数通常接受以下参数:

pcb:代表TCP连接的控制块。

data:指向要发送数据的指针。

len:要发送的数据的长度。

flags:与数据发送相关的标志。例如,TCP_WRITE_FLAG_COPY 表示应从应用程序的数据缓冲区复制数据(而不是直接引用)。

确认机制:使用 tcp_write 发送的数据将在对方确认收到之后才从发送队列中移除。这意味着,即使你已经调用了 tcp_write,你也需要确保你的应用程序继续处理(例如,通过调用 tcp_output 或等待 lwIP 的主循环)来确保数据被实际发送和确认。

合适的调用时间:为了避免不必要的网络拥塞和效率低下,建议在连接建立后或在接收到数据或发送缓冲区有可用空间时(通过相关的TCP回调函数)再调用 tcp_write。

tcp_write 是 lwIP 的TCP API的一部分,与其他函数(如 tcp_connect, tcp_listen, tcp_close 等)一起,提供了完整的TCP功能。在使用它时,重要的是要理解其工作原理,以及与其他TCP操作的交互方式。







审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1602

    文章

    21324

    浏览量

    593215
  • 嵌入式系统
    +关注

    关注

    40

    文章

    3431

    浏览量

    128221
  • SoC设计
    +关注

    关注

    1

    文章

    142

    浏览量

    18667
  • TCP
    TCP
    +关注

    关注

    8

    文章

    1272

    浏览量

    78299
  • LwIP协议栈
    +关注

    关注

    0

    文章

    19

    浏览量

    7259

原文标题:lwip代码分析

文章出处:【微信号:傅里叶的猫,微信公众号:傅里叶的猫】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    LwIP协议的设计与实现_中文译稿

    LwIP协议的设计与实现_中文译稿
    发表于 08-20 08:18

    LwIP协议源码详解

    LwIP协议源码详解
    发表于 08-20 23:17

    为什么加了LWIP协议会时通时不通?

    加了LWIP协议的部分,已经可以ping通了但是 有时候会断断续续的时通时不通不知道应该从哪里查一下问题。。。 求指教
    发表于 07-28 22:53

    LwIP协议的设计与实现资料分享!

    LwIP协议的设计与实现_中文译稿LwIP协议的设计与实现_中文译稿.pdf (493.54
    发表于 07-31 23:47

    为什么LWIP双向发送大量数据时网络协议会崩溃?

    鼠标坐标信息,LWIP协议就在几秒内挂掉,LWIP通过串口留下的遗书是以下两个的其中之一(不会两个都同时出现):Assertion"netconn_accept:invalid
    发表于 11-04 03:19

    怎么使用stm32的LWIP协议进行报文调度算法的开发?

    各位朋友好,我的导师要求我设计一个新的报文调度算法,能够实现不同优先级的报文在发送的过程中,实现高优先级报文的低延时和低抖动。要求使用stm32的LWIP协议进行报文调度算法的开发,请问要实现
    发表于 04-07 04:35

    LwIP协议开发嵌入式网络的三种方法有何关系

    LwIP协议开发嵌入式网络的三种方法分析 轻量级的TCP/IP协议
    发表于 08-05 07:55

    LwIP协议开发嵌入式网络有哪几种方法

    LwIP协议开发嵌入式网络的三种方法分析摘要 轻量级的TCP/IP协议
    发表于 08-06 07:33

    网络协议-lwip2.1.2移植

    本篇目标:上一篇freertos的优化(stm32f4_os_app)中,RTOS的移植已经告一段落了,接下来就是移植重要的网络协议-lwip2.1.2!硬件配置:STM32_ETH以太网
    发表于 08-16 08:08

    怎样将LWIP协议移植到FreeRTOS上呢

    怎样将LWIP协议移植到FreeRTOS上呢?需要修改的文件有哪些呢?
    发表于 10-27 07:37

    lwIP协议是什么?

    lwIP协议是什么?
    发表于 12-20 06:17

    RTOS环境下的RAW API与LwIP协议进行交互

    是RAW API与LwIP协议进行交互的。用户通过注册回调函数的方式告诉协议,当某些事件发生时需要做什么
    发表于 01-19 06:48

    基于LwIP轻量级网络协议实现一个UDP协议通信例程

    本 demo 基于 BL706 ETH 开发板,并基于 LwIP 轻量级网络协议,实现了一个 UDP 协议通信的例程。以太网 PHY 芯片这里使用的是 PHY8720硬件准备一块 B
    发表于 06-17 17:49

    Lwip协议栈的设计方案

    LWIP是TCP/IP协议栈的一种实现。LWIP的主要目的是减少存储器利用量和代码尺寸,使LWIP适合应用于小的、资源有限的处理器如嵌入式系
    发表于 09-16 15:18 33次下载
    <b class='flag-5'>Lwip</b><b class='flag-5'>协议</b>栈的设计方案

    lwip协议中文版

    LWIP是TCP/IP协议栈的一种实现。LWIP的主要目的是减少存储器利用量和代码尺寸,使LWIP适合应用于小的、资源有限的处理器如嵌入式系
    发表于 02-03 16:47 0次下载
    <b class='flag-5'>lwip</b><b class='flag-5'>协议</b>中文版