在线时间3664 小时
UID3441752
注册时间2017-11-21
NXP金币75142075
TA的每日心情 | 开心 昨天 09:35 |
---|
签到天数: 235 天 [LV.7]常住居民III
管理员
  
- 积分
- 27774
- 最后登录
- 2023-6-9
|
基于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。
- /*! @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线程通信。
五、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() 函数创建连接:
- 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)
- }
复制代码
|
|