参考资料:
正点原子
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)接口进行连接。
例程模板:
cpp
/*
* 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
#endif
static int complete_nw_thread;
static sys_thread_t main_thread_handle;
void print_app_header();
void start_application();
#define THREAD_STACKSIZE 1024
struct 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));
}
#else
static 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==1
server_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;
}
}
#else
vTaskDelete(NULL);
#endif
}
void main_thread(void *p)
{
#if ((LWIP_IPV6==0) && (LWIP_DHCP==1))
int mscnt = 0;
#endif
#ifdef XPS_BOARD_ZCU102
IicPhyReset();
#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==1
while (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;
}
}
#else
assign_default_ip(&(server_netif.ip_addr), &(server_netif.netmask),
&(server_netif.gw));
#endif
print_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;
}
进程:
cpp
/*------------------------------------------------ ---------------------------*
* 例程: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 -- 指向每个线程超时的指针。
*------------------------------------------------- --------------------------*/
cpp
/*---------------------------------------------------------------------------*
* 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
*-------------------------------------------------------------------------*/
进程定义:
主进程
cpp
void main_thread(void *p)
{
#if ((LWIP_IPV6==0) && (LWIP_DHCP==1))
int mscnt = 0;
#endif
#ifdef XPS_BOARD_ZCU102
IicPhyReset();
#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==1
while (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;
}
}
#else
assign_default_ip(&(server_netif.ip_addr), &(server_netif.netmask),
&(server_netif.gw));
#endif
print_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博客
cpp
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, flags
hwaddr_len:表示硬件地址(MAC地址)的长度。
hwaddr[]:表示硬件地址(MAC地址)的数组。
mtu:表示最大传输单元(Maximum Transmission Unit),
即网络接口可以传输的最大数据包的大小。
flags:表示网络接口的标志。这可以包括各种有关网络接口状态或特性的标志。
这些字段的填充是在设备驱动程序的初始化函数中进行的,以确保网络接口能够正确地工作。
这是在lwIP协议栈中管理网络接口的基本信息的一种方式。
cpp
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==1
server_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;
}
}
#else
vTaskDelete(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
cpp
void print_app_header(void)
{
xil_printf("TCP server listening on port %d\r\n",
TCP_CONN_PORT);
#if LWIP_IPV6==1
xil_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);
#else
xil_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,失败则打印;
cpp
void start_application(void)
{
int sock, new_sd;
#if LWIP_IPV6==1
struct sockaddr_in6 address, remote;
#else
struct sockaddr_in address, remote;
#endif /* LWIP_IPV6 */
int size;
/* set up address to connect to */
memset(&address, 0, sizeof(address));
#if LWIP_IPV6==1
if ((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);
#else
if ((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);
}
}