| 
在线时间4856 小时
UID3441752
注册时间2017-11-21
NXP金币72759 
 TA的每日心情|  | 开心 2025-7-11 08:53
 | 
|---|
 签到天数: 301 天 连续签到: 2 天 [LV.8]以坛为家I 管理员   
 
	积分40909 
最后登录2025-10-24 | 
 
| 基于MCUXpesso SDK实现TCP Client/Server一、概述 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。
 
 配置外置PHY, 在RESET之前上拉ENET_INT:复制代码/*! @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)
 MAC 设置:复制代码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 地址用宏 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);
 四、LwIp初始化复制代码netifapi_netif_set_up(&fsl_netif0);
 调用tcpip_init 创建一个 tcpip_thread线程, 该线程实现LwIP 核心功能. 其他线程用消息队列与lwip线程通信。
 五、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()函数发送和接收数据.
 4、使用netconn_close() 函数关闭连接
 TCP Server实现
 netconn_new() 函数创建连接:
 
 netconn_bind() 函数将连接绑定到地址复制代码conn = netconn_new(NETCONN_TCP);
 netconn_listen() 函数监听连接:复制代码netconn_bind(conn, IP_ADDR_ANY, TCP_CUSTOM_PORT);
 netconn_accept() 函数接受连接,这会阻塞直到客户端连接复制代码/* Tell connection to go into listening mode. */
netconn_listen(conn);
 netconn_recv() 函数用来接收数据复制代码err = netconn_accept(conn, &newconn);
netconn_send() 函数用来发送数据到远端的IP/Port.
 
 
 在该例程中, 如果服务器接收到一个 ‘TOGGLE’ 消息, 这将会toggle 一个LED. (GPIO1/3, need to connect a LED manually)
 
 TCP Client实现复制代码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
netconn_new() 函数创建连接:
 
 在该例程中,我们初始化一个GPIO, 使能一个中断。当SW8被按下时, 客户端board会发出一个’Toggle’命令给TCP server。复制代码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);
 复制代码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)
}
 
 | 
 |