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 
#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;
}

进程:

/*------------------------------------------------ ---------------------------* 
* 例程: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_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;
}
  1. 如果启用了DHCP,定义一个计数器 mscnt

  2. 如果目标平台是 XPS_BOARD_ZCU102,则执行 IicPhyReset(),这可能是与PHY相关的初始化。

  3. 调用 lwip_init() 进行lwIP协议栈的初始化。

  4. 通过 sys_thread_new 创建一个名为 "nw_thread" 的新线程,该线程执行 network_thread 函数。这个函数主要用于网络接口的配置和启动lwIP协议栈中的相关功能。

  5. 在启动网络线程后,通过 vTaskSuspend(NULL) 暂停当前任务,直到网络线程完成。

  6. 如果启用了DHCP且未启用IPv6,进入一个循环,等待DHCP请求成功。如果超时,输出错误信息并分配默认的IP地址。

  7. 如果未启用IPv6,通过 assign_default_ip 分配默认的IP地址、子网掩码和网关。

  8. 打印网络配置信息。

  9. 调用 print_app_header() 打印应用程序的头部信息。

  10. 调用 start_application() 启动应用程序。

  11. 通过 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, flags

hwaddr_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==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
}

  1. 定义了一个函数 network_thread,该函数接受一个 void* 类型的参数,通常用于线程传递。

  2. 初始化了一个以太网 MAC 地址 mac_ethernet_address

  3. 添加网络接口到 netif_list 中,并设置为默认接口。

  4. 如果启用了IPv6,配置IPv6地址。

  5. 将网络接口标记为 "up",表示网络接口已准备好接收和发送数据。

  6. 启动一个新的线程 xemacif_input_thread,用于处理接收到的网络数据包:监听EMAC;

  7. 标记网络线程已完成(complete_nw_thread = 1)。

  8. 恢复主线程的执行(vTaskResume(main_thread_handle))。

  9. 如果未启用IPv6且启用了DHCP,启动DHCP客户端并进入一个无限循环等待DHCP完成。

  10. 如果启用了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==1
	xil_printf("On Host: Run $iperf -V -c %s%% -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 */
}
  1. 使用 xil_printf 函数输出TCP服务器监听的端口号,通过 %d 显示 TCP_CONN_PORT 变量的值。

  2. 使用条件编译判断IPv6是否启用,如果启用,输出一条IPv6相关的命令提示,包括使用 iperf 工具进行测试的命令。其中,inet6_ntoa 函数用于将IPv6地址转换成可打印的字符串格式,并获取 server_netif 结构体中的第一个IPv6地址(server_netif.ip6_addr[0])。

  3. 如果未启用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==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);
	}
}

你可能感兴趣的:(tcp/ip,网络,服务器,fpga开发)