基于MCUXpesso SDK实现TCP Client/Server
来源:恩智浦MCU加油站 发布时间:2023-03-09 分享至微信

一、概述

TCP Client Server在服务器和客户端之间建立双向连接。它是HTTP、Telnet、FTP、SSH等应用程序使用的最常见的通信模型。

LwIP 是MCUXpresso SDK中的免费轻量级TCP/IP协议栈. 它有三个编程接口 (API):
  • RAW API: 它使用事件(event)回调函数来开发应用程序。
  • Netconn API: 它是一种高级的API,以来实时操作系统(RTOS)提供的服务, Netconn API 支持多线程开发
  • BSD Socket API: 它依赖Netconn API, 是为了与socket兼容而开发的。
在本文中,我将介绍如何使用LwIP Netconn API 来实现TCP client Server。一块EVK-RT1060 作为 TCP server, 另一块EVK-RT1060 作为TCP client。它们通过以太网路由器连接,通过TCP协议进行通信。
该例程实现一个简单功能,如果你按client板上的SW8按钮,它将toggle服务器板上的LED。

本文是针对LwIP和MCUXpresso SDK的初学者。

二、硬件配置

  • PHY 设置:

该例程使用phyksz8081, 这是EVK-RT1060 开发板的默认PHY。使用 ENET port 0。

/*! @brief The ENET PHY address. */
#define BOARD_ENET0_PHY_ADDRESS (0x02U) /* Phy address of enet port 0. */
/* Address of PHY interface. */
#define EXAMPLE_PHY_ADDRESS BOARD_ENET0_PHY_ADDRESS
/* MDIO operations. */
#define EXAMPLE_MDIO_OPS enet_ops
/* PHY operations. */
#define EXAMPLE_PHY_OPS phyksz8081_ops
/* ENET clock frequency. */
#define EXAMPLE_CLOCK_FREQ CLOCK_GetFreq(kCLOCK_IpgClk)
  • 配置外置PHY, 在RESET之前上拉ENET_INT:

GPIO_PinInit(GPIO1, 9, gpio_config);
GPIO_PinInit(GPIO1, 10, gpio_config);
/* pull up the ENET_INT before RESET. */
GPIO_WritePinOutput(GPIO1, 10, 1);
GPIO_WritePinOutput(GPIO1, 9, 0);
delay();
GPIO_WritePinOutput(GPIO1, 9, 1);
  • MAC 设置:
本例程中, MAC 地址用宏 configMAC_ADDR定义:
/* MAC address configuration. */
#define configMAC_ADDR \
{ \
0x02, 0x12, 0x13, 0x10, 0x15, 0x11 \
}


三、网络接口初始化

为了创建一个新的网络接口, 用户必须为一个新的网络接口结构 netif 分配空间,然后调用 netifapi_netif_add:
IP4_ADDR(fsl_netif0_ipaddr, configIP_ADDR0, configIP_ADDR1, configIP_ADDR2, configIP_ADDR3);
IP4_ADDR(fsl_netif0_netmask, configNET_MASK0, configNET_MASK1, configNET_MASK2, configNET_MASK3);
IP4_ADDR(fsl_netif0_gw, configGW_ADDR0, configGW_ADDR1, configGW_ADDR2, configGW_ADDR3);

netifapi_netif_add(fsl_netif0, fsl_netif0_ipaddr, fsl_netif0_netmask, fsl_netif0_gw, fsl_enet_config0, ethernetif0_init, tcpip_input);

将网络接口设置为默认接口

netifapi_netif_set_default(fsl_netif0);

启动网络接口,接下来可以处理数据了。

netifapi_netif_set_up(fsl_netif0);


四、LwIp初始化

调用tcpip_init 创建一个 tcpip_thread线程, 该线程实现LwIP 核心功能. 其他线程用消息队列与lwip线程通信。
tcpip_init(NULL, NULL);


五、TCP Client Server实现

对于TCP 通信, 服务器监听所有的连接请求。当请求到达时,服务器接受它并在服务器和客户端之间传输数据。下图是一个TCP连接的建立过程。

在客户端建立建立TCP连接的步骤如下:

  1. 使用netconn_new() 函数创建一个netconn.

  2. 使用netconn_connect() 函数连接到服务器地址上。

  3. 通过netconn_send() 和netconn_recv() 发送和接收数据。

  4. 使用 netconn_close() 关闭连接.

在服务器端建立TCP连接的步骤如下:

  1. 使用netconn_new() 函数创建连接;

  2. 使用netconn_bind() 函数将连接绑定到地址

  3. 使用 netconn_listen() 函数监听连接

  4. 使用 netconn_accept() 函数接受连接,这会阻塞直到客户端连接

  5. 通过 netconn_send() and netconn_recv()函数发送和接收数据.

  6. 使用netconn_close() 函数关闭连接

TCP Server实现

netconn_new() 函数创建连接:

conn = netconn_new(NETCONN_TCP);

netconn_bind() 函数将连接绑定到地址

netconn_bind(conn, IP_ADDR_ANY, TCP_CUSTOM_PORT);

netconn_listen() 函数监听连接:

/* Tell connection to go into listening mode. */
netconn_listen(conn);

netconn_accept() 函数接受连接,这会阻塞直到客户端连接

err = netconn_accept(conn, newconn);

netconn_recv() 函数用来接收数据

netconn_send() 函数用来发送数据到远端的IP/Port.

在该例程中, 如果服务器接收到一个 ‘TOGGLE’ 消息, 这将会toggle 一个LED. (GPIO1/3, need to connect a LED manually)

while ((err = netconn_recv(newconn, buf)) == ERR_OK) {
PRINTF("Received %s\n", buf->p->payload);
do {
netbuf_data(buf, data, len);
tcp_rx_buf = (void *)data;
if (tcp_rx_buf[0] == 'T')
{
PRINTF("LED was toggled from client\r\n"); GPIO_PortToggle(EXAMPLE_LED_GPIO, 1u << EXAMPLE_LED_GPIO_PIN); netconn_write(newconn, TcpReply, sizeof(TcpReply) , NETCONN_COPY );
}
} while (netbuf_next(buf) >= 0);
netbuf_delete(buf);
} //end of while(( err

TCP Client实现

netconn_new() 函数创建连接:

conn = netconn_new(NETCONN_TCP);
memset(server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = PP_HTONS(TCP_CUSTOM_PORT);
server_addr.sin_addr.s_addr = server_ip_addr.addr;
err = connect(client_sock, (struct sockaddr *)server_addr, sizeof(server_addr));
LWIP_ASSERT("connect fail, please start TCP server first ! ", err == 0);

在该例程中,我们初始化一个GPIO, 使能一个中断。当SW8被按下时, 客户端board会发出一个’Toggle’命令给TCP server。

while (1)
{
if (g_InputSignal)
{
delay();
if (1 == GPIO_PinRead(EXAMPLE_SW_GPIO, EXAMPLE_SW_GPIO_PIN))
{
PRINTF("%s is turned on.\r\n", EXAMPLE_SW_NAME);
err = send(server_sock, Sendtext, sizeof(Sendtext) / sizeof(Sendtext[0]), 0);
}
/* Reset state of switch. */
g_InputSignal = false;
} //end of if (g_InputSignal)
}



恩智浦MCU加油站


这是由恩智浦官方运营的公众号,着重为您推荐恩智浦MCU的产品信息、开发技巧、教程文档、培训课程等内容。


长按二维码,关注我们



END



更多恩智浦AI-IoT市场和产品信息,邀您同时关注“NXP客栈”微信公众号




NXP客栈


恩智浦致力于打造安全的连接和基础设施解决方案,为智慧生活保驾护航。





长按二维码,关注我们

[ 新闻来源:恩智浦MCU加油站,更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj]
存入云盘 收藏
举报
全部评论

暂无评论哦,快来评论一下吧!