TCP/IP LWIP FPGA 笔记
参考资料:
正点原子
LwIP 之 网络接口 netif(ethernetif.c、netif.c)-CSDN博客
IPv4/IPv6、DHCP、网关、路由_ipv6有网关的概念吗-CSDN博客
TCP/IP
TCP/IP 协议中文名为传输控制协议/因特网互联协议,又名网络通讯协议,是 Internet 最基本的协议、 Internet 国际互联网络的基础,由网络层的 IP 协议和传输层的 TCP 协议组成。TCP/IP 定义了电子设备如何 连入因特网,以及数据如何在它们之间传输的标准。协议采用了 4 层的层级结构,每一层都呼叫它的下一 层所提供的协议来完成自己的需求。
通俗而言:TCP 负责发现传输的问题,一有问题就发出信号,要求重 新传输,直到所有数据安全正确地传输到目的地。而 IP 是给因特网的每一台联网设备规定一个地址。
TCP/IP 协议不是 TCP 和 IP 这两个协议的合称,而是指因特网整个 TCP/IP 协议族。从协议分层模型方 面来讲,TCP/IP 由四个层次组成:网络接口层、网络层、传输层、应用层。OSI(Open System Interconnection) 是开放式系统互连参考模型,该模型将 TCP/IP 分为七层:物理层、数据链路层、网络层、传输层、会话层、 表示层和应用层。TCP/IP 模型与 OSI 模型对比如表 32.1.1 所示。
LWIP
LWIP 是瑞典计算机科学院(SICS)的 Adam Dunkels 等开发的一个小型开源的 TCP/IP 协议栈,是 TCP/IP 的一种实现方式。LWIP 是轻量级 IP 协议,有无操作系统的支持都可以运行。
LWIP 实现的重点是在保持 TCP协议主要功能的基础上减少对 RAM的占用,它只需十几KB的RAM和40K左右的ROM就可以运行, 这 使 LWIP 协议栈适合在低端的嵌入式系统中使用。关于 LWIP 的详细信息大家可以去 http://savannah.nongnu.org/projects/lwip/这个网站去查阅。
LWIP 的主要特性如下:
•IGMP 协议,用于网络组管理,可以实现多播数据的接收
•Internet 协议(IP),包括 IPv4 和 IPv6,支持 IP 分片与重装,包括通过多个网络接口的数据包转发
•用于网络维护和调试的 Internet 控制消息协议(ICMP)
•用户数据报协议(UDP)
•传输控制协议(TCP)拥塞控制,往返时间(RTT)估计,快速恢复和重传
•DNS,域名解析
•SNMP,简单网络管理协议
•动态主机配置协议(DHCP)
•以太网地址解析协议(ARP)
•AUTOIP,IP 地址自动配置
•PPP,点对点协议,支持 PPPoE
lwip212_v1_1
lwip212_v1_1是一个基于开源 lwIP 库版本 2.1.2 构建的库(Vivado 2019.2 版本)。 lwip212_v1_1 库为 Ethernetlite(axi_ethernetlite)、TEMAC(axi_ethernet)以及千兆以太网控制器和 MAC (GigE)内核提供适配器。该库可以在 MicroBlaze、ARM Cortex-A9、ARM Cortex-A53 和 ARM Cortex-R5 处理器上运行。Ethernetlite 和 TEMAC 核心适用于 MicroBlaze 系统。千兆以太网控制器和 MAC(GigE)内核仅适用于 ARM Cortex-A9(MPSOC-7000 处理器设备)、ARM Cortex-A53 和 ARM Cortex-R5 系统(MPSOC UltraScale+ MPSoC)。
lwip212_v1_1 提供二种用户编程接口方式:
raw API 和 socket API
Raw API:是为高性能和低内存开销而定制的。这种类型的 API 把网络协议栈和应用程序放在一个进程里,连接网络协议和应用程序的纽带是回调函数,回调函数实际上是一个普通的 C 函数。为了接收数据, 应用程序会首先向协议栈注册一个回调函数,当关联的连接有一个信息到达时,该回调函数就被协议栈调 用。这种实现方式即有优点也有缺点。
优点是数据的接收和发送不会导致进程的切换,提供了最好的性能, 执行速度快,而且消耗的内存资源少;
缺点是应用程序无法进行连续运算,因为网络协议的处理和运算是 在同一进程中完成的,二者无法并行发生。Raw API 是资源较少的嵌入式系统的首选方法,也是在没有操作系统的情况下运行 lwIP 时唯一可用的 API。
Socket API:提供了一个基于 open-read-write-close 模块的 BSD socket-style 接口,需要操作系统,是标准的网络开发的API,正常情况下是基于进程方式开发的(LWIP裸机情况下没有进程的概念,没有优先级关系,只有嵌套关系)。
此接口在性能和内存要求方面不如 Raw API 高效,不适用于小型嵌入式系统,但移植性更好,占的资源更多。
PS 的千兆以太网控制器
在介绍 PS 的千兆以太网控制器之前,我们首先了解下 MAC 与 PHY 芯片及 GMII 与 RGMII 接口。
以太网卡工作在 OSI 模型的最后两层,物理层和数据链路层,物理层定义了数据传送与接收所需要的 电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。
物理层的芯 片称之为 PHY。此外 PHY 还提供了和对端设备连接的重要功能并通过 LED 灯显示出当前的连接的状态和 工作状态。
当我们给网卡接入网线的时候,PHY 不断发出的脉冲信号检测到对端有设备,它们通过一套标准的语言交流,互相协商并确定连接速度、工作模式、是否采用流控等。
通常情况下,协商的结果是两个设备中能同时支持的最大速度和最好的双工模式。
这个技术被称为 AutoNegotiation,即自协商。
数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接 口等功能。
以太网卡中数据链路层的芯片称之为 MAC 控制器。
MAC 控制器与 PHY 通过 MII(Medium Independent Interface)接口进行连接。MII 接口有很多类型, 千兆以太网多使用GMII(Gigabit Medium Independent Interface)或RGMII(Reduced Gigabit Media Independent Interface)接口进行连接。
例程模板:
/** Copyright (C) 2018 - 2019 Xilinx, Inc.* All rights reserved.** Redistribution and use in source and binary forms, with or without modification,* are permitted provided that the following conditions are met:** 1. Redistributions of source code must retain the above copyright notice,* this list of conditions and the following disclaimer.* 2. Redistributions in binary form must reproduce the above copyright notice,* this list of conditions and the following disclaimer in the documentation* and/or other materials provided with the distribution.* 3. The name of the author may not be used to endorse or promote products* derived from this software without specific prior written permission.** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY* OF SUCH DAMAGE.**/#include <sleep.h>
#include "netif/xadapter.h"
#include "platform_config.h"
#include "xil_printf.h"
#include "lwip/init.h"
#include "lwip/inet.h"#if LWIP_IPV6==1
#include "lwip/ip6_addr.h"
#include "lwip/ip6.h"
#else#if LWIP_DHCP==1
#include "lwip/dhcp.h"
extern volatile int dhcp_timoutcntr;
err_t dhcp_start(struct netif *netif);
#endif
#define DEFAULT_IP_ADDRESS "192.168.1.10"
#define DEFAULT_IP_MASK "255.255.255.0"
#define DEFAULT_GW_ADDRESS "192.168.1.1"
#endif /* LWIP_IPV6 */#ifdef XPS_BOARD_ZCU102
#ifdef XPAR_XIICPS_0_DEVICE_ID
int IicPhyReset(void);
#endif
#endifstatic int complete_nw_thread;
static sys_thread_t main_thread_handle;void print_app_header();
void start_application();#define THREAD_STACKSIZE 1024struct netif server_netif;#if LWIP_IPV6==1
static void print_ipv6(char *msg, ip_addr_t *ip)
{print(msg);xil_printf(" %s\n\r", inet6_ntoa(*ip));
}
#elsestatic void print_ip(char *msg, ip_addr_t *ip)
{xil_printf(msg);xil_printf("%d.%d.%d.%d\n\r", ip4_addr1(ip), ip4_addr2(ip),ip4_addr3(ip), ip4_addr4(ip));
}static void print_ip_settings(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw)
{print_ip("Board IP: ", ip);print_ip("Netmask : ", mask);print_ip("Gateway : ", gw);
}static void assign_default_ip(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw)
{int err;xil_printf("Configuring default IP %s \r\n", DEFAULT_IP_ADDRESS);err = inet_aton(DEFAULT_IP_ADDRESS, ip);if(!err)xil_printf("Invalid default IP address: %d\r\n", err);err = inet_aton(DEFAULT_IP_MASK, mask);if(!err)xil_printf("Invalid default IP MASK: %d\r\n", err);err = inet_aton(DEFAULT_GW_ADDRESS, gw);if(!err)xil_printf("Invalid default gateway address: %d\r\n", err);
}
#endif /* LWIP_IPV6 */void network_thread(void *p)
{
#if ((LWIP_IPV6==0) && (LWIP_DHCP==1))int mscnt = 0;
#endif/* the mac address of the board. this should be unique per board */u8_t mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };xil_printf("\n\r\n\r");xil_printf("-----lwIP Socket Mode TCP Server Application------\r\n");/* Add network interface to the netif_list, and set it as default */if (!xemac_add(&server_netif, NULL, NULL, NULL, mac_ethernet_address,PLATFORM_EMAC_BASEADDR)) {xil_printf("Error adding N/W interface\r\n");return;}#if LWIP_IPV6==1server_netif.ip6_autoconfig_enabled = 1;netif_create_ip6_linklocal_address(&server_netif, 1);netif_ip6_addr_set_state(&server_netif, 0, IP6_ADDR_VALID);print_ipv6("\n\rlink local IPv6 address is:",&server_netif.ip6_addr[0]);
#endif /* LWIP_IPV6 */netif_set_default(&server_netif);/* specify that the network if is up */netif_set_up(&server_netif);/* start packet receive thread - required for lwIP operation */sys_thread_new("xemacif_input_thread",(void(*)(void*))xemacif_input_thread, &server_netif,THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);complete_nw_thread = 1;/* Resume the main thread; auto-negotiation is completed */vTaskResume(main_thread_handle);#if ((LWIP_IPV6==0) && (LWIP_DHCP==1))dhcp_start(&server_netif);while (1) {vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS);dhcp_fine_tmr();mscnt += DHCP_FINE_TIMER_MSECS;if (mscnt >= DHCP_COARSE_TIMER_SECS*1000) {dhcp_coarse_tmr();mscnt = 0;}}
#elsevTaskDelete(NULL);
#endif
}void main_thread(void *p)
{
#if ((LWIP_IPV6==0) && (LWIP_DHCP==1))int mscnt = 0;
#endif#ifdef XPS_BOARD_ZCU102IicPhyReset();
#endif/* initialize lwIP before calling sys_thread_new */lwip_init();/* any thread using lwIP should be created using sys_thread_new */sys_thread_new("nw_thread", network_thread, NULL,THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);/* Suspend Task until auto-negotiation is completed */if (!complete_nw_thread)vTaskSuspend(NULL);#if LWIP_IPV6==0
#if LWIP_DHCP==1while (1) {vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS);if (server_netif.ip_addr.addr) {xil_printf("DHCP request success\r\n");break;}mscnt += DHCP_FINE_TIMER_MSECS;if (mscnt >= 10000) {xil_printf("ERROR: DHCP request timed out\r\n");assign_default_ip(&(server_netif.ip_addr),&(server_netif.netmask),&(server_netif.gw));break;}}#elseassign_default_ip(&(server_netif.ip_addr), &(server_netif.netmask),&(server_netif.gw));
#endifprint_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask),&(server_netif.gw));
#endif /* LWIP_IPV6 */xil_printf("\r\n");/* print all application headers */print_app_header();xil_printf("\r\n");/* start the application*/start_application();vTaskDelete(NULL);return;
}int main()
{main_thread_handle = sys_thread_new("main_thread", main_thread, 0,THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);vTaskStartScheduler();while(1);return 0;
}
进程:
/*------------------------------------------------ ---------------------------*
* 例程:sys_thread_new
*----------------- -------------------------------------------------- -----*
* 描述:
* 启动一个优先级为“prio”的新线程,该线程将在函数“thread()”中开始执行。 “arg”参数将作为参数传递给 thread() 函数。返回新
* 线程的 id。 id 和优先级均取决于系统。
* 输入:
* char *name:线程的名称。
* void (* thread)(void *arg):指向要运行的函数的指针。这个函数接受一个 void* 类型的参数。
* void *arg:传递给线程函数的参数。
* int stacksize:线程所需的堆栈大小(以字节为单位)。
* int prio:线程的优先级。
* 输出:
* sys_thread_t -- 指向每个线程超时的指针。
*------------------------------------------------- --------------------------*/
/*---------------------------------------------------------------------------** Routine: sys_thread_new*---------------------------------------------------------------------------** Description:* Starts a new thread with priority "prio" that will begin its* execution in the function "thread()". The "arg" argument will be* passed as an argument to the thread() function. The id of the new* thread is returned. Both the id and the priority are system* dependent.* Inputs:* char *name -- Name of thread* void (* thread)(void *arg) -- Pointer to function to run.* void *arg -- Argument passed into function* int stacksize -- Required stack amount in bytes* int prio -- Thread priority* Outputs:* sys_thread_t -- Pointer to per-thread timeouts.*---------------------------------------------------------------------------*/
sys_thread_t sys_thread_new( const char *pcName,
void( *pxThread )( void *pvParameters ),
void *pvArg, int iStackSize, int iPriority )
{
xTaskHandle xCreatedTask;
portBASE_TYPE xResult;
sys_thread_t xReturn;xResult = xTaskCreate( pxThread, ( const char * const) pcName, iStackSize, pvArg, iPriority, &xCreatedTask );if( xResult == pdPASS ){xReturn = xCreatedTask;}else{xReturn = NULL;}return xReturn;
}/** Prints an assertion messages and aborts execution.*/
void sys_assert( const char *pcMessage )
{(void) pcMessage;for (;;){}
}
/*-------------------------------------------------------------------------** End of File: sys_arch.c*-------------------------------------------------------------------------*/
进程定义:
主进程
void main_thread(void *p)
{
#if ((LWIP_IPV6==0) && (LWIP_DHCP==1))int mscnt = 0;
#endif#ifdef XPS_BOARD_ZCU102IicPhyReset();
#endif/* initialize lwIP before calling sys_thread_new */lwip_init();/* any thread using lwIP should be created using sys_thread_new */sys_thread_new("nw_thread", network_thread, NULL,THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);/* Suspend Task until auto-negotiation is completed */if (!complete_nw_thread)vTaskSuspend(NULL);#if LWIP_IPV6==0
#if LWIP_DHCP==1while (1) {vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS);if (server_netif.ip_addr.addr) {xil_printf("DHCP request success\r\n");break;}mscnt += DHCP_FINE_TIMER_MSECS;if (mscnt >= 10000) {xil_printf("ERROR: DHCP request timed out\r\n");assign_default_ip(&(server_netif.ip_addr),&(server_netif.netmask),&(server_netif.gw));break;}}#elseassign_default_ip(&(server_netif.ip_addr), &(server_netif.netmask),&(server_netif.gw));
#endifprint_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask),&(server_netif.gw));
#endif /* LWIP_IPV6 */xil_printf("\r\n");/* print all application headers */print_app_header();xil_printf("\r\n");/* start the application*/start_application();vTaskDelete(NULL);return;
}
-
如果启用了DHCP,定义一个计数器
mscnt
。 -
如果目标平台是 XPS_BOARD_ZCU102,则执行
IicPhyReset()
,这可能是与PHY相关的初始化。 -
调用
lwip_init()
进行lwIP协议栈的初始化。 -
通过
sys_thread_new
创建一个名为 "nw_thread" 的新线程,该线程执行network_thread
函数。这个函数主要用于网络接口的配置和启动lwIP协议栈中的相关功能。 -
在启动网络线程后,通过
vTaskSuspend(NULL)
暂停当前任务,直到网络线程完成。 -
如果启用了DHCP且未启用IPv6,进入一个循环,等待DHCP请求成功。如果超时,输出错误信息并分配默认的IP地址。
-
如果未启用IPv6,通过
assign_default_ip
分配默认的IP地址、子网掩码和网关。 -
打印网络配置信息。
-
调用
print_app_header()
打印应用程序的头部信息。 -
调用
start_application()
启动应用程序。 -
通过
vTaskDelete(NULL)
结束当前线程。
总体而言,这段代码是一个lwIP网络应用程序的入口点,负责初始化lwIP协议栈、创建网络线程、进行网络配置和启动应用程序。在网络线程完成初始化后,主线程通过 vTaskSuspend
暂停,等待网络线程完成后继续执行。
子进程 :网络配置进程
LwIP 之 网络接口 netif(ethernetif.c、netif.c)-CSDN博客
struct netif
Generic data structure used for all lwIP network interfaces.
The following fields should be filled in by the initialization
function for the device driver: hwaddr_len, hwaddr[], mtu, flagshwaddr_len:表示硬件地址(MAC地址)的长度。hwaddr[]:表示硬件地址(MAC地址)的数组。mtu:表示最大传输单元(Maximum Transmission Unit),
即网络接口可以传输的最大数据包的大小。flags:表示网络接口的标志。这可以包括各种有关网络接口状态或特性的标志。这些字段的填充是在设备驱动程序的初始化函数中进行的,以确保网络接口能够正确地工作。
这是在lwIP协议栈中管理网络接口的基本信息的一种方式。
void network_thread(void *p)
{
#if ((LWIP_IPV6==0) && (LWIP_DHCP==1))int mscnt = 0;
#endif/* the mac address of the board. this should be unique per board */u8_t mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };xil_printf("\n\r\n\r");xil_printf("-----lwIP Socket Mode TCP Server Application------\r\n");/* struct netif *xemac_add(struct netif *netif, ip_addr_t *ipaddr, * ip_addr_t *netmask, ip_addr_t *gw, * unsigned char *mac_ethernet_address, UINTPTR mac_baseaddr) *//* 将网络接口添加到netif_list,并将其设置为默认*//* Add network interface to the netif_list, and set it as default */if (!xemac_add(&server_netif, NULL, NULL, NULL, mac_ethernet_address,PLATFORM_EMAC_BASEADDR)) {xil_printf("Error adding N/W interface\r\n");return;}#if LWIP_IPV6==1server_netif.ip6_autoconfig_enabled = 1;netif_create_ip6_linklocal_address(&server_netif, 1);netif_ip6_addr_set_state(&server_netif, 0, IP6_ADDR_VALID);print_ipv6("\n\rlink local IPv6 address is:",&server_netif.ip6_addr[0]);
#endif /* LWIP_IPV6 */netif_set_default(&server_netif);/* specify that the network if is up */netif_set_up(&server_netif);/* The input thread calls lwIP to process any received packets.* This thread waits until a packet is received (sem_rx_data_available), * and then calls xemacif_input which processes 1 packet at a time.*//* start packet receive thread - required for lwIP operation */sys_thread_new("xemacif_input_thread",(void(*)(void*))xemacif_input_thread, &server_netif,THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);complete_nw_thread = 1;/* Resume the main thread; auto-negotiation is completed */vTaskResume(main_thread_handle);#if ((LWIP_IPV6==0) && (LWIP_DHCP==1))dhcp_start(&server_netif);while (1) {vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS);dhcp_fine_tmr();mscnt += DHCP_FINE_TIMER_MSECS;if (mscnt >= DHCP_COARSE_TIMER_SECS*1000) {dhcp_coarse_tmr();mscnt = 0;}}
#elsevTaskDelete(NULL);
#endif
}
-
定义了一个函数
network_thread
,该函数接受一个void*
类型的参数,通常用于线程传递。 -
初始化了一个以太网 MAC 地址
mac_ethernet_address
。 -
添加网络接口到
netif_list
中,并设置为默认接口。 -
如果启用了IPv6,配置IPv6地址。
-
将网络接口标记为 "up",表示网络接口已准备好接收和发送数据。
-
启动一个新的线程
xemacif_input_thread
,用于处理接收到的网络数据包:监听EMAC; -
标记网络线程已完成(
complete_nw_thread = 1
)。 -
恢复主线程的执行(
vTaskResume(main_thread_handle)
)。 -
如果未启用IPv6且启用了DHCP,启动DHCP客户端并进入一个无限循环等待DHCP完成。
-
如果启用了IPv6或未启用DHCP,通过
vTaskDelete(NULL)
结束当前线程。
总体而言,这段代码负责配置网络接口,启动lwIP协议栈的相关功能,包括处理接收到的数据包和启动DHCP客户端。在网络配置完成后,它通过 vTaskResume
恢复主线程的执行。
DHCP
IPv4/IPv6、DHCP、网关、路由_ipv6有网关的概念吗-CSDN博客
DHCP(Dynamic Host Configuration Protocol)是一种网络协议,用于自动分配IP地址和其他网络配置信息给计算机或设备。以下是DHCP的基本原理和功能:
自动IP地址分配: DHCP允许计算机或设备在加入网络时自动获取IP地址,而无需手动配置。这有助于简化网络管理,特别是在大型网络中。
配置信息分发: 除了IP地址外,DHCP还可以分配其他网络配置信息,包括子网掩码、网关、DNS服务器等。这使得设备能够在连接到网络时获取所有必要的配置信息。
动态分配: DHCP支持动态IP地址分配,其中IP地址是从一个地址池中动态分配的,而不是静态分配给特定设备。这有助于更有效地管理IP地址资源。
租约机制: DHCP分配的IP地址是有限期的,称为租约。设备在租约到期前可以续租,否则将释放该IP地址,返回到地址池中供其他设备使用。这有助于防止未使用的IP地址占用网络资源。
减少配置错误: DHCP减少了手动配置IP地址和相关网络信息的可能性,从而降低了配置错误的风险。
在上述代码中,DHCP被用于自动获取IP地址,并通过循环定期调用DHCP的定时器函数来处理DHCP过程。DHCP的执行过程会在后台自动完成,直到获取到有效的IP地址或超时。
LwIP 之 网络接口 netif(ethernetif.c、netif.c)-CSDN博客
LwIP使用netif
来描述一个硬件网络接口,但是由于网络接口是直接与硬件打交道的,硬件不同则处理可能不同,必须由用户提供最底层接口。
LwIP的网络驱动有一定的模型,/src/netif/ethernetif.c
文件即为底层接口的驱动的模版,用户为自己的网络设备实现驱动时应参照此模块。
该文件中的函数通常为与硬件打交道的函数,当有数据接收的时候被调用,以使接收到的数据进入tcpip协议栈。
简单来说,netif
是LwIP抽象出来的各网络接口,协议栈可以使用多个不同的接口,而ethernetif
则提供了netif
访问硬件的各接口,每个不同的接口有不同的ethernetif
。
print_app_header
void print_app_header(void)
{xil_printf("TCP server listening on port %d\r\n",TCP_CONN_PORT);
#if LWIP_IPV6==1xil_printf("On Host: Run $iperf -V -c %s%%<interface> -i %d -t 300 -w 2M\r\n",inet6_ntoa(server_netif.ip6_addr[0]),INTERIM_REPORT_INTERVAL);
#elsexil_printf("On Host: Run $iperf -c %s -i %d -t 300 -w 2M\r\n",inet_ntoa(server_netif.ip_addr),INTERIM_REPORT_INTERVAL);
#endif /* LWIP_IPV6 */
}
-
使用
xil_printf
函数输出TCP服务器监听的端口号,通过%d
显示TCP_CONN_PORT
变量的值。 -
使用条件编译判断IPv6是否启用,如果启用,输出一条IPv6相关的命令提示,包括使用
iperf
工具进行测试的命令。其中,inet6_ntoa
函数用于将IPv6地址转换成可打印的字符串格式,并获取server_netif
结构体中的第一个IPv6地址(server_netif.ip6_addr[0]
)。 -
如果未启用IPv6,输出一条IPv4相关的命令提示,同样包括使用
iperf
工具进行测试的命令。其中,inet_ntoa
函数用于将IPv4地址转换成可打印的字符串格式,并获取server_netif
结构体中的IPv4地址(server_netif.ip_addr
)。
这样,该函数为用户提供了与TCP服务器通信的建议命令,方便用户在主机上运行相应的 iperf
命令进行性能测试。
start_application
1.启动Socket,失败则打印;
2.
void start_application(void)
{int sock, new_sd;
#if LWIP_IPV6==1struct sockaddr_in6 address, remote;
#elsestruct sockaddr_in address, remote;
#endif /* LWIP_IPV6 */int size;/* set up address to connect to */memset(&address, 0, sizeof(address));
#if LWIP_IPV6==1if ((sock = lwip_socket(AF_INET6, SOCK_STREAM, 0)) < 0) {xil_printf("TCP server: Error creating Socket\r\n");return;}address.sin6_family = AF_INET6;address.sin6_port = htons(TCP_CONN_PORT);address.sin6_len = sizeof(address);
#elseif ((sock = lwip_socket(AF_INET, SOCK_STREAM, 0)) < 0) {xil_printf("TCP server: Error creating Socket\r\n");return;}address.sin_family = AF_INET;address.sin_port = htons(TCP_CONN_PORT);address.sin_addr.s_addr = INADDR_ANY;
#endif /* LWIP_IPV6 */if (bind(sock, (struct sockaddr *)&address, sizeof (address)) < 0) {xil_printf("TCP server: Unable to bind to port %d\r\n",TCP_CONN_PORT);close(sock);return;}if (listen(sock, 1) < 0) {xil_printf("TCP server: tcp_listen failed\r\n");close(sock);return;}size = sizeof(remote);while (1) {if ((new_sd = accept(sock, (struct sockaddr *)&remote,(socklen_t *)&size)) > 0)sys_thread_new("TCP_recv_perf thread",tcp_recv_perf_traffic, (void*)&new_sd,TCP_SERVER_THREAD_STACKSIZE,DEFAULT_THREAD_PRIO);}
}
数据收发相关函数:
lwip_recvfrom
int s: 套接字描述符,标识特定的套接字。
void *mem: 用于存储接收数据的缓冲区的指针。
size_t len: 缓冲区的长度,表示 mem 所指向的缓冲区的最大容量。
int flags: 一些标志,用于指定接收操作的行为。struct sockaddr *from: 用于存储发送方地址信息的结构体指针。
在UDP套接字中,此参数用于获取发送方的地址信息。
socklen_t *fromlen: 指向存储发送方地址长度的变量的指针。
在UDP套接字中,用于获取发送方地址信息的长度。接收来源EMAC的数据,来自EMAC的数据会有一部分缓冲于此,
与void *mem: 用于存储接收数据的缓冲区的指针。这个缓冲区存在差别,此处存放的是解析后的数据。
通常不需要指定。
lwip_recvfrom(int s, void *mem, size_t len, int flags,struct sockaddr *from, socklen_t *fromlen)
{struct lwip_sock *sock;ssize_t ret;LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));sock = get_socket(s);if (!sock) {return -1;}
#if LWIP_TCPif (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {ret = lwip_recv_tcp(sock, mem, len, flags);lwip_recv_tcp_from(sock, from, fromlen, "lwip_recvfrom", s, ret);done_socket(sock);return ret;} else
#endif{u16_t datagram_len = 0;struct iovec vec;struct msghdr msg;err_t err;vec.iov_base = mem;vec.iov_len = len;msg.msg_control = NULL;msg.msg_controllen = 0;msg.msg_flags = 0;msg.msg_iov = &vec;msg.msg_iovlen = 1;msg.msg_name = from;msg.msg_namelen = (fromlen ? *fromlen : 0);err = lwip_recvfrom_udp_raw(sock, flags, &msg, &datagram_len, s);if (err != ERR_OK) {LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",s, lwip_strerr(err)));sock_set_errno(sock, err_to_errno(err));done_socket(sock);return -1;}ret = (ssize_t)LWIP_MIN(LWIP_MIN(len, datagram_len), SSIZE_MAX);if (fromlen) {*fromlen = msg.msg_namelen;}}sock_set_errno(sock, 0);done_socket(sock);return ret;
}
lwip_read
ssize_t
lwip_read(int s, void *mem, size_t len)
{return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
}
lwip_send
int s: 套接字描述符,标识特定的套接字。
const void *data: 指向要发送数据的缓冲区的指针。
size_t size: 要发送的数据的字节数。
int flags: 一些标志,用于指定发送操作的行为。
ssize_t
lwip_send(int s, const void *data, size_t size, int flags)
{struct lwip_sock *sock;err_t err;u8_t write_flags;size_t written;LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",s, data, size, flags));sock = get_socket(s);if (!sock) {return -1;}if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
#if (LWIP_UDP || LWIP_RAW)done_socket(sock);
#if LWIP_UDP_OPT_BLOCK_TX_TILL_COMPLETEreturn lwip_sendto_blocking(s, data, size, flags, NULL, 0);
#elsereturn lwip_sendto(s, data, size, flags, NULL, 0);
#endif
#else /* (LWIP_UDP || LWIP_RAW) */sock_set_errno(sock, err_to_errno(ERR_ARG));done_socket(sock);return -1;
#endif /* (LWIP_UDP || LWIP_RAW) */}write_flags = (u8_t)(NETCONN_COPY |((flags & MSG_MORE) ? NETCONN_MORE : 0) |((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));written = 0;err = netconn_write_partly(sock->conn, data, size, write_flags, &written);LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));sock_set_errno(sock, err_to_errno(err));done_socket(sock);/* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */return (err == ERR_OK ? (ssize_t)written : -1);
}
相关文章:
TCP/IP LWIP FPGA 笔记
参考资料: 正点原子 LwIP 之 网络接口 netif(ethernetif.c、netif.c)-CSDN博客 IPv4/IPv6、DHCP、网关、路由_ipv6有网关的概念吗-CSDN博客 TCP/IP TCP/IP 协议中文名为传输控制协议/因特网互联协议,又名网络通讯协议…...

2024年海外优青项目申报指南
国家自然科学基金优秀青年科学基金(海外)项目(简称“海外优青项目”),一直备受海外优秀青年学者(包括博士后研究人员)关注,被看作是回国发展最为重要的资助项目之一。知识人网小编现…...
threejs之常用贴图
在三维图形和游戏开发中,高光贴图、凹凸贴图、法线贴图和环境光遮蔽贴图是常用的技术,用于增加虚拟物体表面的细节和真实感,而无需增加更多的几何体。这些技术可以帮助开发者和艺术家创造出既详细又性能高效的场景。 高光贴图(Sp…...

Unity类银河恶魔城学习记录3-1 EnemyStateMachine源代码 P47
Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Enemy.cs using System.Collections; using System.Collections.Generic;…...

使用webstorm调试vue 2 项目
学习目标: 使用webstorm调试vue 2 项目 笔者环境: npm 6.14.12 webstorm 2023.1 vue 2 学习内容: 例如: 正常启动npm 项目 配置javaScruot dubug 配置你的项目地址就好 使用dubug运行你配置的调式页 问题 如果进入了js页无…...

深度学习缝模块怎么描述创新点?(附写作模板+涨点论文)
深度学习缝了别的模块怎么描述创新点、怎么讲故事写成一篇优质论文? 简单框架:描述自己这个领域,该领域出现了什么问题,你用了什么方法解决,你的方法有了多大的性能提升。 其中,重点讲清楚这两点…...

html,css,js速成
准备:vscode配好c,python,vue环境,并下载live server插件。 1. html hypertext markup language(超文本标记语言) 1. 基础语法 一个html元素由开始标签,填充文本,结束标签构成。 常见标签说明<b>…...
《Docker极简教程》--Docker基础--基础知识(一)
在这篇文章中我们先大致的了解以下Docker的基本概念,在后续的文章中我们会详细的讲解这些概念以及使用。 一、容器(Container) 1.1 容器的定义和特点 容器的定义 容器是一种轻量级、可移植的软件打包技术,用于打包应用及其依赖项和运行环境,…...

Web html和css
目录 1 前言2 HTML2.1 元素(Element)2.1.1 块级元素和内联(行级)元素2.1.2 空元素 2.2 html页面的文档结构2.3 常见标签使用2.3.1 注释2.3.2 标题2.3.3 段落2.3.4 列表2.3.5 超链接2.3.6 图片2.3.7 内联(行级)标签2.3.8 换行 2.4 属性2.4.1 布尔属性 2.5 实体引用2.6 空格2.7 D…...

Three.js学习6:透视相机和正交相机
一、相机 相机 camera,可以理解为摄像机。在拍影视剧的时候,最终用户看到的画面都是相机拍出来的内容。 Three.js 里,相机 camera 里的内容就是用户能看到的内容。从这个角度来看,相机其实就是用户的视野,就像用户的眼…...

❤ React18 环境搭建项目与运行(地址已经放Gitee开源)
❤ React项目搭建与运行 环境介绍 node v20.11.0 react 18.2 react-dom 18.2.0一、React环境搭建 第一种普通cra搭建 1、检查本地环境 node版本 18.17.0 检查node和npm环境 node -v npm -v 2、安装yarn npm install -g yarn yarn --version 3、创建一个新的React项目…...

2024 RTE行业(实时互动行业)人才发展学习总结
解决方案 人才画像 开发者人才素质要求: 具备多个领域的技术知识注重团队合作,具备协作能力以用户为导向的用户体验意识具备创新思维和解决问题的能力需快速响应行业变化和持续的学习能力具备项目管理能力 学习和吸收新知识的渠道 RTE人才分类...

92.网游逆向分析与插件开发-游戏窗口化助手-显示游戏数据到小助手UI
内容参考于:易道云信息技术研究院VIP课 上一个内容:游戏窗口化助手的UI设计-CSDN博客 码云地址(游戏窗口化助手 分支):https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号:e8116af3a7b0186adba…...

Stable Diffusion 模型下载:majicMIX fantasy 麦橘幻想
文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十 下载地址 模型介绍 非常推荐的一个非常绚丽、充满幻想的大模型,由国人“Merjic”发布,下载量颇高。这个模型风格炸裂,远距离脸部需要inpaint以达成…...
docker compose安装minio
要使docker-compose管理的容器(如MinIO)在系统启动时自动启动,你需要使用Docker的重启策略。在你的docker-compose.yml文件中为MinIO服务添加restart策略即可实现这一目标。restart: always指令确保了在容器退出时总是重新启动容器࿰…...

二、SSM 整合配置实战
本章概要 依赖整合和添加控制层配置编写(SpringMVC 整合)业务配置编写(AOP/TX 整合)持久层配置编写(MyBatis 整合)容器初始化配置类整合测试 2.1 依赖整合和添加 数据库准备 数据库脚本 CREATE DATABASE mybatis-example;USE mybatis-example;CREATE TABLE t_emp(emp_id INT…...

『运维备忘录』之 Yum 命令详解
运维人员不仅要熟悉操作系统、服务器、网络等只是,甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作,持续给大家更新运维工作所需要接触到的知识点,希望大…...
CSS中可继承与不可继承属性有哪些
一、无继承性的属性 1.display:规定元素应该生成的框的类型 属性值作用none元素不显示,并且会从文档流中移除。block块类型。默认宽度为父元素宽度,可设置宽高,换行显示。inline行内元素类型。默认宽度为内容宽度,不…...

Zephyr NRF7002 实现AppleJuice
BLE的基础知识 ble的信道和BR/EDR的信道是完全不一样的。但是范围是相同的,差不多也都是2.4Ghz的频道。可以简单理解为空中有40个信道0~39信道。两个设备在相同的信道里面可以进行相互通信。 而这些信道SIG又重新编号: 这个编号就是把37 38 39。 3个信道…...

(已解决)vue+element-ui实现个人中心,仿照原神
差一个个人中心页面,看到了这个博主的个人中心,真的很不错 地址:vueelement仿原神实现好看的个人中心 最终效果:...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...

自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...