STM32 实现 TCP 服务器与多个设备通信

发布于:2024-10-15 ⋅ 阅读:(48) ⋅ 点赞:(0)

目录

一、引言

二、硬件准备

三、软件准备

四、LWIP 协议栈的配置与初始化

五、创建 TCP 服务器

1.创建 TCP 控制块

2.绑定端口 

3. 进入监听状态

4.设置接收回调函数 

六、处理多个客户端连接 

七、数据处理与通信管理 

八、错误处理与资源管理 

九、总结 

 


一、引言

        在嵌入式系统开发中,常常需要实现设备之间的网络通信。STM32 作为一款广泛应用的微控制器,结合网络通信功能可以实现与多个设备的交互。本文将介绍如何在 STM32 上实现 TCP 服务器端,以便与多个设备进行通讯。

二、硬件准备

  1. STM32 开发板:选择一款带有以太网接口的 STM32 开发板,如 STM32F429 系列等。
  2. 以太网模块(可选):如果开发板没有内置以太网接口,可以选择一个外接的以太网模块,如 W5500 等。
  3. 网络连接:将开发板连接到同一局域网内,确保其他设备可以通过网络访问到 STM32 开发板。

三、软件准备

  1. 开发环境:如 Keil MDK、IAR Embedded Workbench 等。
  2. LWIP(Lightweight IP)协议栈:LWIP 是一个轻量级的 TCP/IP 协议栈,适用于嵌入式系统。可以从 LWIP 官方网站下载并集成到开发环境中。
  3. STM32 库:使用 STM32 的官方库或者其他第三方库来进行硬件驱动和开发。

四、LWIP 协议栈的配置与初始化

  1. 将 LWIP 协议栈的源文件添加到 STM32 项目中,并设置正确的编译选项。
  2. 在项目的初始化代码中,调用 LWIP 的初始化函数lwip_init(),完成协议栈的初始化。
  3. 根据实际需求,配置 LWIP 的参数,如 IP 地址、子网掩码、默认网关等。可以通过修改lwipopts.h文件来实现。

五、创建 TCP 服务器

1.创建 TCP 控制块

使用 LWIP 的 API 函数tcp_new()创建一个新的 TCP 控制块。

  struct tcp_pcb *tcp_server_pcb;
   tcp_server_pcb = tcp_new();

2.绑定端口 

使用tcp_bind()函数将 TCP 控制块绑定到一个特定的端口号。通常选择一个未被其他应用程序占用的端口。

   err_t err = tcp_bind(tcp_server_pcb, IP_ADDR_ANY, SERVER_PORT);
   if (err!= ERR_OK) {
       // 处理绑定失败的情况
   }

这里的SERVER_PORT是服务器监听的端口号。

3. 进入监听状态

使用tcp_listen()函数使 TCP 控制块进入监听状态,等待客户端的连接请求。

 tcp_server_pcb = tcp_listen(tcp_server_pcb);

4.设置接收回调函数 

使用tcp_accept()函数设置一个接收回调函数,当有客户端连接请求时,该回调函数将被调用。

tcp_accept(tcp_server_pcb, tcp_accept_callback);

tcp_accept_callback是自定义的回调函数,用于处理客户端的连接请求。

六、处理多个客户端连接 

1.在接收回调函数中,接受客户端的连接请求,并为每个连接创建一个新的 TCP 控制块来处理与该客户端的通信。

void tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
   {
       if (err == ERR_OK) {
           // 处理新的连接
           // 可以为每个连接分配一些资源,如缓冲区等
           tcp_recv(newpcb, tcp_receive_callback, NULL);
       }
   }

 

2.为每个连接设置接收回调函数,以便在有数据到达时进行处理。

void tcp_receive_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
   {
       if (err == ERR_OK && p!= NULL) {
           // 处理接收到的数据
           // 可以根据需要将数据转发给其他连接或者进行其他处理
           tcp_write(tpcb, p->payload, p->len, TCP_WRITE_FLAG_COPY);
           // 释放接收到的数据包缓冲区
           pbuf_free(p);
       }
   }

3.使用数据结构管理多个连接 

可以使用链表、数组等数据结构来管理多个客户端连接。例如,可以创建一个链表,每个节点存储一个客户端连接的信息,包括 TCP 控制块、客户端地址等。

typedef struct _client_connection
   {
       struct tcp_pcb *pcb;
       ip_addr_t client_ip;
       u16_t client_port;
       struct _client_connection *next;
   } client_connection_t;

   client_connection_t *client_list = NULL;

在连接建立时,将新的连接添加到链表中;在连接关闭时,从链表中删除相应的节点。

void add_client_connection(struct tcp_pcb *pcb, const ip_addr_t *client_ip, u16_t client_port)
   {
       client_connection_t *new_connection = (client_connection_t *)malloc(sizeof(client_connection_t));
       new_connection->pcb = pcb;
       new_connection->client_ip = *client_ip;
       new_connection->client_port = client_port;
       new_connection->next = client_list;
       client_list = new_connection;
   }

   void remove_client_connection(struct tcp_pcb *pcb)
   {
       client_connection_t *prev = NULL;
       client_connection_t *current = client_list;
       while (current!= NULL) {
           if (current->pcb == pcb) {
               if (prev == NULL) {
                   client_list = current->next;
               } else {
                   prev->next = current->next;
               }
               free(current);
               break;
           }
           prev = current;
           current = current->next;
       }
   }

七、数据处理与通信管理 

1.在接收回调函数中,可以根据接收到的数据进行相应的处理。例如,可以解析数据、执行特定的操作或者将数据转发给其他连接。

void tcp_receive_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
   {
       if (err == ERR_OK && p!= NULL) {
           // 处理接收到的数据
           char *data = (char *)p->payload;
           // 根据数据内容进行处理
           // 可以将数据转发给其他连接
           client_connection_t *current = client_list;
           while (current!= NULL) {
               if (current->pcb!= tpcb) {
                   tcp_write(current->pcb, data, strlen(data), TCP_WRITE_FLAG_COPY);
               }
               current = current->next;
           }
           // 释放接收到的数据包缓冲区
           pbuf_free(p);
       }
   }

2.如果需要向特定的客户端发送数据,可以遍历链表找到相应的客户端连接,并使用tcp_write函数进行数据发送。

void send_data_to_client(const ip_addr_t *client_ip, u16_t client_port, char *data)
   {
       client_connection_t *current = client_list;
       while (current!= NULL) {
           if (ip_addr_cmp(&current->client_ip, client_ip) && current->client_port == client_port) {
               tcp_write(current->pcb, data, strlen(data), TCP_WRITE_FLAG_COPY);
               break;
           }
           current = current->next;
       }
   }

八、错误处理与资源管理 

1.在整个通信过程中,需要进行适当的错误处理。例如,当连接失败、数据发送失败或接收超时等情况发生时,需要采取相应的措施,如重新连接、报告错误或释放资源等。

void tcp_error_callback(void *arg, err_t err)
   {
       // 处理错误情况
       struct tcp_pcb *tpcb = (struct tcp_pcb *)arg;
       remove_client_connection(tpcb);
   }

2.当连接关闭时,需要及时释放相关的资源,如缓冲区、TCP 控制块等,以避免内存泄漏。

 void tcp_close_callback(void *arg, err_t err)
   {
       // 处理连接关闭情况
       struct tcp_pcb *tpcb = (struct tcp_pcb *)arg;
       remove_client_connection(tpcb);
   }

九、总结 

通过以上步骤,我们可以在 STM32 上实现一个 TCP 服务器,与多个设备进行通信。在实际应用中,可以根据具体的需求进行进一步的优化和扩展,例如添加安全认证、数据加密、流量控制等功能。同时,还需要注意网络稳定性和可靠性,以确保通信的正常进行。

希望本文对大家在 STM32 上实现 TCP 服务器与多个设备通信有所帮助。如果有任何问题或建议,欢迎在评论区留言交流。

 

 

 

 


网站公告

今日签到

点亮在社区的每一天
去签到