679|0

107

帖子

1

TA的资源

一粒金砂(高级)

楼主
 

【兆易GD32H759I-EVAL】 FreeRTOS_tcpudp例程学习与TCP测试 [复制链接]

  本帖最后由 尹小舟 于 2024-6-11 22:37 编辑

前面分析了main函数,我们现在继续学习分析LWIP;

/*!
    \brief      initializes the LwIP stack
    \param[in]  none
    \param[out] none
    \retval     none
*/
void lwip_stack_init(void)
{
    ip_addr_t ipaddr;
    ip_addr_t netmask;
    ip_addr_t gw;

    /* create tcp_ip stack thread */
    tcpip_init( NULL, NULL );

    /* IP address setting */
#ifdef USE_DHCP
    ipaddr.addr = 0;
    netmask.addr = 0;
    gw.addr = 0;
#else
    IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
    IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
    IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
  
#endif /* USE_DHCP */

#ifdef USE_ENET0
    /* - netif_add(struct netif *netif, ip_addr_t *ipaddr,
              ip_addr_t *netmask, ip_addr_t *gw,
              void *state, err_t (* init)(struct netif *netif),
              err_t (* input)(struct pbuf *p, struct netif *netif))

     Adds your network interface to the netif_list. Allocate a struct
    netif and pass a pointer to this structure as the first argument.
    Give pointers to cleared ip_addr structures when using DHCP,
    or fill them with sane numbers otherwise. The state pointer may be NULL.

    The init function pointer must point to a initialization function for
    your ethernet netif interface. The following code illustrates it's use.*/

    netif_add(&g_mynetif0, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
    /* registers the default network interface */
    netif_set_default(&g_mynetif0);
    netif_set_status_callback(&g_mynetif0, lwip_netif_status_callback);

    /* when the netif is fully configured this function must be called */
    netif_set_up(&g_mynetif0);

#endif /* USE_ENET0 */

#ifdef USE_ENET1
    /* - netif_add(struct netif *netif, ip_addr_t *ipaddr,
              ip_addr_t *netmask, ip_addr_t *gw,
              void *state, err_t (* init)(struct netif *netif),
              err_t (* input)(struct pbuf *p, struct netif *netif))

     Adds your network interface to the netif_list. Allocate a struct
    netif and pass a pointer to this structure as the first argument.
    Give pointers to cleared ip_addr structures when using DHCP,
    or fill them with sane numbers otherwise. The state pointer may be NULL.

    The init function pointer must point to a initialization function for
    your ethernet netif interface. The following code illustrates it's use.*/

    netif_add(&g_mynetif1, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
    /* registers the default network interface */
    netif_set_default(&g_mynetif1);
    netif_set_status_callback(&g_mynetif1, lwip_netif_status_callback);

    /* when the netif is fully configured this function must be called */
    netif_set_up(&g_mynetif1);

#endif /* USE_ENET1 */
}

函数定义

void lwip_stack_init(void)  
{  
    // ...  
}

变量定义

ip_addr_t ipaddr;  
ip_addr_t netmask;  
ip_addr_t gw;

这三个变量用于存储 IP 地址、子网掩码和网关地址。

lwIP 栈初始化

tcpip_init( NULL, NULL );

tcpip_init() 函数用于初始化 lwIP 的 TCP/IP 核心模块。它通常创建一个或多个线程来处理 lwIP 的内部操作。在这个例子中,tcpip_init() 被调用时没有传递任何参数(NULL, NULL),这可能意味着使用了默认的参数,这些参数对于当前配置不是必需的。

IP 地址设置

#ifdef USE_DHCP
    ipaddr.addr = 0;
    netmask.addr = 0;
    gw.addr = 0;
#else
    IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
    IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
    IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
  
#endif /* USE_DHCP */

如果定义了 USE_DHCP,则 IP 地址、子网掩码和网关地址被初始化为 0,表示这些值将通过 DHCP 协议自动获取。

如果没有定义 USE_DHCP,则使用静态配置的 IP 地址、子网掩码和网关。这里使用 IP4_ADDR 宏来设置这些值。

网络接口添加

#ifdef USE_ENET0  
    netif_add(&g_mynetif0, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);  
    netif_set_default(&g_mynetif0);  
    netif_set_status_callback(&g_mynetif0, lwip_netif_status_callback);  
    netif_set_up(&g_mynetif0);  
#endif /* USE_ENET0 */

如果定义了 USE_ENET0,则使用 netif_add() 函数将网络接口 g_mynetif0 添加到 lwIP 的网络接口列表中。这个函数需要网络接口结构体的指针、IP 地址、子网掩码、网关地址、一个用户定义的状态指针(在这里是 NULL)、初始化函数 ethernetif_init 和输入函数 tcpip_input。然后,netif_set_default() 函数将 g_mynetif0 设置为默认的网络接口。这意味着在没有明确指定网络接口的情况下,lwIP 将使用此接口发送数据。netif_set_status_callback() 函数用于设置网络接口的状态回调函数 lwip_netif_status_callback。当网络接口的状态发生变化时(例如,链接上或链接下),这个回调函数将被调用。最后,netif_set_up() 函数将网络接口设置为启动状态,使其能够发送和接收数据包。
         lwip_stack_init() 函数通过调用 tcpip_init() 初始化 lwIP 栈,并根据是否定义了 USE_DHCP 来选择使用 DHCP 还是静态配置来设置 IP 地址、子网掩码和网关。然后,如果定义了 USE_ENET0,它将添加网络接口 g_mynetif0 到 lwIP 的网络接口列表中,并设置其默认状态和状态回调函数。最后,它将网络接口设置为启动状态。

 

lwip_netif_status_callback

该函数是LWIP(Lightweight IP)网络栈中网络接口状态变化时的回调函数。当网络接口完全配置并启动后,该函数会被调用以初始化telnet服务器、TCP客户端和UDP回显服务。

/*!
    \brief      after the netif is fully configured, it will be called to initialize the function of telnet, client and udp
    \param[in]  netif: the struct used for lwIP network interface
    \param[out] none
    \retval     none
*/
void lwip_netif_status_callback(struct netif *netif)
{
    if(((netif->flags & NETIF_FLAG_UP) != 0) && (0 != netif->ip_addr.addr)) {
        /* initilaize the tcp server: telnet 8000 */
        hello_gigadevice_init();
        /* initilaize the tcp client: echo 10260 */
        tcp_client_init();
        /* initilaize the udp: echo 1025 */
        udp_echo_init();
    }
}
  • 函数名
    • void lwip_netif_status_callback(struct netif *netif):函数没有返回值(void),并接受一个指向netif结构的指针作为参数。netif结构包含了网络接口的配置信息。
  • 函数体
    • 首先,函数检查netif的两个状态标志:
      • NETIF_FLAG_UP:确保网络接口是启动的(up)。
      • netif->ip_addr.addr:确保网络接口已分配了一个非零的IP地址。
    • 如果上述两个条件都满足,函数会初始化以下服务:
      • hello_gigadevice_init():这个函数用于初始化TCP服务器:,监听在8000端口上。

      • tcp_client_init():这个函数是用于初始化一个TCP客户端的,端口10260

      • udp_echo_init():这个函数用于初始化一个UDP回显服务,监听在1025端口上。UDP回显服务是一个简单的服务,它接收客户端发送的UDP数据包,并将完全相同的数据包发送回客户端。

 

hello_task


/*!
    \brief      hello task
    \param[in]  arg: user supplied argument
    \param[out] none
    \retval     none
*/
static void hello_task(void *arg)
{
    int ret;
    int sockfd = -1, newfd = -1;
    uint32_t len;
    int tcp_port = 8000;
    int recvnum;
    struct sockaddr_in svr_addr, clt_addr;
    char buf[50];

    /* bind to port 8000 at any interface */
    svr_addr.sin_family = AF_INET;
    svr_addr.sin_port = htons(tcp_port);
    svr_addr.sin_addr.s_addr = htons(INADDR_ANY);

    name_recv.length = 0;
    name_recv.done = 0;

    while(1) {
        /* create a TCP socket */
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(sockfd < 0) {
            continue;
        }

        ret = bind(sockfd, (struct sockaddr *)&svr_addr, sizeof(svr_addr));
        if(ret < 0) {
            lwip_close(sockfd);
            sockfd = -1;
            continue;
        }

        /* listen for incoming connections (TCP listen backlog = 1) */
        ret = listen(sockfd, 1);
        if(ret < 0) {
            lwip_close(sockfd);
            continue;
        }

        len = sizeof(clt_addr);

        /* grab new connection */
        newfd = accept(sockfd, (struct sockaddr *)&clt_addr, (socklen_t *)&len);
        if(-1 != newfd) {
            send(newfd, (void *)&GREETING, sizeof(GREETING), 0);
        }

        while(-1 != newfd) {
            /* reveive packets, and limit a reception to MAX_NAME_SIZE bytes */
            recvnum = recv(newfd, buf, MAX_NAME_SIZE, 0);
            if(recvnum <= 0) {
                lwip_close(newfd);
                newfd = -1;
                break;
            }
            hello_gigadevice_recv(newfd, buf, recvnum);
        }

        lwip_close(sockfd);
        sockfd = -1;
    }
}

这个hello_task函数是一个基于LWIP(Lightweight IP)库的TCP服务器任务。它监听在特定端口(8000)上的TCP连接,并在连接建立后发送一个问候消息(GREETING,这个变量在函数外部定义,没有在此段代码中给出),然后接收并处理来自客户端的数据。以下是对该函数的详细解释:

  1. 变量定义

    • sockfd 和 newfd:分别用于监听套接字和与客户端通信的套接字。
    • tcp_port:服务器监听的TCP端口号(8000)。
    • svr_addr 和 clt_addr:分别用于存储服务器和客户端的地址信息。
    • buf:用于存储从客户端接收到的数据。
    • recvnum:表示从客户端接收到的数据字节数。
    • len:用于accept函数,表示客户端地址结构的长度。
    • ret:用于存储系统调用的返回值。
  2. 设置服务器地址

    • 使用AF_INET(IPv4)作为地址族。
    • 监听端口设置为8000。
    • 绑定到任何可用的网络接口(INADDR_ANY)。
  3. 无限循环

    • 在这个循环中,服务器不断地尝试创建套接字、绑定、监听,并接受新的连接。
  4. 创建套接字

    • 使用socket函数创建一个TCP套接字。
  5. 绑定套接字

    • 使用bind函数将套接字绑定到指定的地址和端口。
    • 如果绑定失败,关闭套接字并继续下一次循环。
  6. 监听连接

    • 使用listen函数开始监听连接。
    • 如果监听失败,关闭套接字并继续下一次循环。
  7. 接受连接

    • 使用accept函数接受一个新的连接。
    • 如果成功接受连接,向客户端发送一个问候消息(GREETING)
      #define GREETING           "\n\r======= HelloGigaDevice =======\
                                  \n\r== GD32 ==\
                                  \n\r== Telnet SUCCESS==\
                                  \n\rHello. What is your name?\r\n"
      #define HELLO              "\n\rGigaDevice PORT Hello "

       

  8. 接收和处理数据

    • 在内部循环中,使用recv函数从客户端接收数据。
    • 如果接收到的数据长度小于或等于0(表示连接已关闭或出错),则关闭与客户端的套接字并退出内部循环。
    • 否则,调用hello_gigadevice_recv函数(这个函数在代码段中没有给出,可能是处理接收到的数据的自定义函数)。
  9. 关闭套接字

    • 这是为了重新进入循环并尝试再次绑定和监听。但通常,服务器只需要一个监听套接字,并在整个运行期间保持打开状态。这里的关闭和重新创建可能是出于某种特定的需求或测试目的。

实验现象

 

点赞 关注
 
 

回复
举报
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
[吐血推荐]^_^单片机多媒体教程-下载地址

http://wstatic.xunlei.com/web/download.htm?cid=446712C427207D83DBBD6E963CF09CC55BBF9BA2迅雷上的,速度很快.分享一下,没人反 ...

单片机应用实例:数字钟源程序

1.实验任务 (1).开机时,显示12:00:00的时间开始计时; (2).P0.0/AD0控制“秒”的调整,每按一次加1秒; (3).P0.1/A ...

谁可以仔细讲一讲51单片机复位电路的工作原理 ?

谁可以仔细讲一讲51单片机复位电路的工作原理 ?

「ADI模拟大学堂」电源管理基础知识(2014.3.9)

143760「ADI模拟大学堂」电源管理基础知识(2014.3.9) (每日一份资料)由于「ADI模拟大学堂」差分放大器以及相关设计软件的基础 ...

颁奖啦|您是如何采购或选择电子元器件的?

515866 恭喜!恭喜! 恭喜以下小伙伴获得20元京东卡,我们将会把e卡直接发送到小伙伴的邮箱中! 若参加过此活动的 ...

华大半导体MCU M0+系列产品IAP参考

ISP和IAP的区别: ISP(In-System Programming)在系统可编程,指电路板上的空白器件可以编程写入最终用户代码, 而不需要从电 ...

请问有没有ADR3433可以PINTOPIN替换的芯片

目前电路板上用的基准芯片是ADI的ADR3433,现在嘉立创上从6块多涨价到18块一片,涨的太狠了,想问下有没有可以PIN TO PIN替换的 ...

请问2个二极管中间1个电容,这样的电路会烧电容吗?

左边+7V是常用电源,电流比较小,但是始终在,右边+5V正常悬空,有时候会插入,电流较大, 右边的+5V会另外通过个MOS管控制断 ...

新唐NUC100开发板上自带的nu-link能否用来烧写新唐其他系列的单片机

请教:新唐NUC100开发板上自带的nu-link能否用来烧写新唐其他系列的单片机,例如新唐8051系列?

颁奖:邀您云逛展《TE Connectivity 线上工博会》报名有好礼!

颁奖:邀您云逛展《TE Connectivity 线上工博会》报名有好礼!活动颁奖啦 名单详见下方列表。请获奖者务必 在2024年11 ...

关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表