STM32F407+LAN8720A +LWIP +FreeRTOS 实现TCP服务器

STM32F407+LAN8720A +LWIP +FreeRTOS UDP通讯

上一篇实现了UDP通讯

本篇实现TCP服务器

实现如下功能:

TCP服务器接收数据并把数据发送回去,实现回环。

1.代码

STM32CUBEIDE配置方式和udp相同,只是代码不同。

TCP服务器实现的代码如下:

c 复制代码
// tcp接收发送缓存
//

#define TCP_RX_LEN  8192
uint8_t TCP_RX_BUF[TCP_RX_LEN];
volatile uint16_t TCP_RX_STA = 0;  /* bit15:有无数据标志位        bit14-0:数据量计数 */
struct tcp_pcb* tcppcb = NULL;


static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
	if (p == NULL) /* 接收到空包表示对方断开连接 */
	{
		tcp_server_disconnect(tpcb);
		err = ERR_CLSD;
	}
	else if (err != ERR_OK) /* 收到非空包但是出现错误 */
	{
		pbuf_free(p);
	}
	else /* 接收数据正常,遍历pbuf拷贝出接收到的数据 */
	{
		struct pbuf* it = p;

		if ((TCP_RX_STA & 0x8000) == 0) /* 当前缓存为空  */
		{
			for (it = p; it != NULL; it = it->next)
			{
				if (TCP_RX_STA + it->len > TCP_RX_LEN) /* 缓存满了 */
					break;
				memcpy(TCP_RX_BUF + TCP_RX_STA, it->payload, it->len);  /* 将接收到的数据拷贝到自己的缓存中  */
				TCP_RX_STA += it->len;
			}
			TCP_RX_STA |= 0x8000;    /* 标记有数据收到 */
		}
		tcp_recved(tpcb, p->tot_len); /* 滑动TCP窗口 */
		pbuf_free(p); /* 释放pbuf */
	}
	return err;
}


static err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
	if (tcppcb == NULL)
	{
		if (err == ERR_OK)
		{
			tcppcb = newpcb;
			tcp_arg(newpcb, NULL);
			tcp_recv(newpcb, recv_callback);

			printf("%s:%d connect.\r\n", inet_ntoa(newpcb->remote_ip), newpcb->remote_port);
		}
		else
		{
			tcp_server_disconnect(newpcb);
		}
	}
	else
	{
		tcp_abort(newpcb);
		printf("already connected.\r\n");
	}
	return err;
}

static void tcp_server_disconnect(struct tcp_pcb *tpcb)
{
	tcp_arg(tpcb, NULL);
	tcp_recv(tpcb, NULL);

	tcp_abort(tpcb); /* 关闭连接并释放tpcb控制块 */
	tcppcb = NULL;
	printf("disconnected.\r\n");
}

static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len)
{
	uint32_t nwrite = 0, total = 0;
	const uint8_t* p = (const uint8_t *) buf;
	err_t err = ERR_OK;
	if (!tpcb)
		return 0;
	while ((err == ERR_OK) && (len != 0) && (tcp_sndbuf(tpcb) > 0))
	{
		nwrite = tcp_sndbuf(tpcb) >= len ? len : tcp_sndbuf(tpcb);
		err = tcp_write(tpcb, p, nwrite, 1);
		if (err == ERR_OK)
		{
			len -= nwrite;
			total += nwrite;
			p += nwrite;
		}
		tcp_output(tpcb);
	}
	return total;
}



//extern int tcp_server_start(uint16_t port);
//extern int user_senddata(const void* buf,uint32_t len);
//extern int transfer_data();


/**
 * 启动TCP服务器
 * @param  port 本地端口号
 * @return      成功返回0
 */
int tcp_server_start(uint16_t port)
{
	int ret = 0;
	struct tcp_pcb* pcb = NULL;
	err_t err = ERR_OK;

	/* create new TCP PCB structure */
	pcb = tcp_new();
	if (!pcb)
	{
		printf("Error creating PCB. Out of Memory\r\n");
		ret = -1;
		goto __exit;
	}

	/* bind to specified @port */
	err = tcp_bind(pcb, IP_ADDR_ANY, port);
	if (err != ERR_OK)
	{
		printf("Unable to bind to port %d: err = %d\r\n", port, err);
		ret = -2;
		goto __exit;
	}

	/* listen for connections */
	pcb = tcp_listen(pcb);
	if (!pcb)
	{
		printf("Out of memory while tcp_listen\r\n");
		ret = -3;
	}

	/* specify callback to use for incoming connections */
	tcp_accept(pcb, accept_callback);

	/* create success */
	printf("TCP echo server started @ port %d\r\n", port);
	return ret;

__exit:
	if (pcb)
		memp_free(MEMP_TCP_PCB, pcb);
	return ret;
}

/**
 * TCP发送数据
 * @param  buf 待发送的数据
 * @param  len 数据长度
 * @return     返回实际发送的字节数
 */
int user_senddata(const void* buf, uint32_t len)
{
	return tcp_server_send(tcppcb, buf, len);
}

/**
 * 轮询函数,放置于main函数的while死循环中
 * @return 无
 */
int transfer_data(void)
{
	uint32_t nsend = 0;
	if (tcppcb != NULL && tcppcb->state == ESTABLISHED) /* 连接有效 */
	{
		if (TCP_RX_STA & 0x8000)   /* 有数据收到  */
		{
			nsend = user_senddata(TCP_RX_BUF, TCP_RX_STA & 0x7FFF);    /* 将接收到的数据发回去  */
			TCP_RX_STA = 0;
			printf("send %d bytes success\r\n", nsend);
		}
	}
	return 0;
}

freertos.c的全部代码如下:

c 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"

#include <string.h>
#include <stdint.h>
#include "lwip/tcp.h"
#include "lwip/err.h"
#include "lwip/memp.h"
#include "lwip/inet.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
// tcp接收发送缓存
//

#define TCP_RX_LEN  8192
uint8_t TCP_RX_BUF[TCP_RX_LEN];
volatile uint16_t TCP_RX_STA = 0;  /* bit15:有无数据标志位        bit14-0:数据量计数 */
struct tcp_pcb* tcppcb = NULL;
/* USER CODE END Variables */
osThreadId defaultTaskHandle;
osThreadId LEDTaskHandle;
osThreadId UARTTask03Handle;
osSemaphoreId uartBinarySem01Handle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
static void tcp_server_disconnect(struct tcp_pcb *tpcb);
static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len);
int transfer_data(void);
int user_senddata(const void* buf, uint32_t len);
int tcp_server_start(uint16_t port);
static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len);
static void tcp_server_disconnect(struct tcp_pcb *tpcb);
static err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err);
static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);

/* USER CODE END FunctionPrototypes */

void StartDefaultTask(void const * argument);
void StartTask_LED(void const * argument);
void StartTask_UART(void const * argument);

extern void MX_LWIP_Init(void);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */
  /* definition and creation of uartBinarySem01 */
  osSemaphoreDef(uartBinarySem01);
  uartBinarySem01Handle = osSemaphoreCreate(osSemaphore(uartBinarySem01), 1);

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of defaultTask */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 2048);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* definition and creation of LEDTask */
  osThreadDef(LEDTask, StartTask_LED, osPriorityIdle, 0, 256);
  LEDTaskHandle = osThreadCreate(osThread(LEDTask), NULL);

  /* definition and creation of UARTTask03 */
  osThreadDef(UARTTask03, StartTask_UART, osPriorityIdle, 0, 512);
  UARTTask03Handle = osThreadCreate(osThread(UARTTask03), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
  /* init code for LWIP */
  MX_LWIP_Init();
  /* USER CODE BEGIN StartDefaultTask */
  /* Infinite loop */
  int ret =  tcp_server_start(6001);
  if(ret !=0) return;
  for(;;)
  {
	transfer_data();
    osDelay(100);
  }
  /* USER CODE END StartDefaultTask */
}

/* USER CODE BEGIN Header_StartTask_LED */
/**
* @brief Function implementing the LEDTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask_LED */
void StartTask_LED(void const * argument)
{
  /* USER CODE BEGIN StartTask_LED */
  /* Infinite loop */
  for(;;)
  {
	HAL_GPIO_TogglePin(GPIOE, LED0_Pin|LED1_Pin);
    osDelay(300);
  }
  /* USER CODE END StartTask_LED */
}

/* USER CODE BEGIN Header_StartTask_UART */
/**
* @brief Function implementing the UARTTask03 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask_UART */
void StartTask_UART(void const * argument)
{
  /* USER CODE BEGIN StartTask_UART */
  /* Infinite loop */
//printf("freertos start\r\n");
  for(;;)
  {

    osDelay(1000);
  }
  /* USER CODE END StartTask_UART */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
static err_t recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
	if (p == NULL) /* 接收到空包表示对方断开连接 */
	{
		tcp_server_disconnect(tpcb);
		err = ERR_CLSD;
	}
	else if (err != ERR_OK) /* 收到非空包但是出现错误 */
	{
		pbuf_free(p);
	}
	else /* 接收数据正常,遍历pbuf拷贝出接收到的数据 */
	{
		struct pbuf* it = p;

		if ((TCP_RX_STA & 0x8000) == 0) /* 当前缓存为空  */
		{
			for (it = p; it != NULL; it = it->next)
			{
				if (TCP_RX_STA + it->len > TCP_RX_LEN) /* 缓存满了 */
					break;
				memcpy(TCP_RX_BUF + TCP_RX_STA, it->payload, it->len);  /* 将接收到的数据拷贝到自己的缓存中  */
				TCP_RX_STA += it->len;
			}
			TCP_RX_STA |= 0x8000;    /* 标记有数据收到 */
		}
		tcp_recved(tpcb, p->tot_len); /* 滑动TCP窗口 */
		pbuf_free(p); /* 释放pbuf */
	}
	return err;
}


static err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
	if (tcppcb == NULL)
	{
		if (err == ERR_OK)
		{
			tcppcb = newpcb;
			tcp_arg(newpcb, NULL);
			tcp_recv(newpcb, recv_callback);

			printf("%s:%d connect.\r\n", inet_ntoa(newpcb->remote_ip), newpcb->remote_port);
		}
		else
		{
			tcp_server_disconnect(newpcb);
		}
	}
	else
	{
		tcp_abort(newpcb);
		printf("already connected.\r\n");
	}
	return err;
}

static void tcp_server_disconnect(struct tcp_pcb *tpcb)
{
	tcp_arg(tpcb, NULL);
	tcp_recv(tpcb, NULL);

	tcp_abort(tpcb); /* 关闭连接并释放tpcb控制块 */
	tcppcb = NULL;
	printf("disconnected.\r\n");
}

static uint32_t tcp_server_send(struct tcp_pcb *tpcb, const void* buf, uint32_t len)
{
	uint32_t nwrite = 0, total = 0;
	const uint8_t* p = (const uint8_t *) buf;
	err_t err = ERR_OK;
	if (!tpcb)
		return 0;
	while ((err == ERR_OK) && (len != 0) && (tcp_sndbuf(tpcb) > 0))
	{
		nwrite = tcp_sndbuf(tpcb) >= len ? len : tcp_sndbuf(tpcb);
		err = tcp_write(tpcb, p, nwrite, 1);
		if (err == ERR_OK)
		{
			len -= nwrite;
			total += nwrite;
			p += nwrite;
		}
		tcp_output(tpcb);
	}
	return total;
}

/**
 * 启动TCP服务器
 * @param  port 本地端口号
 * @return      成功返回0
 */
int tcp_server_start(uint16_t port)
{
	int ret = 0;
	struct tcp_pcb* pcb = NULL;
	err_t err = ERR_OK;

	/* create new TCP PCB structure */
	pcb = tcp_new();
	if (!pcb)
	{
		printf("Error creating PCB. Out of Memory\r\n");
		ret = -1;
		goto __exit;
	}

	/* bind to specified @port */
	err = tcp_bind(pcb, IP_ADDR_ANY, port);
	if (err != ERR_OK)
	{
		printf("Unable to bind to port %d: err = %d\r\n", port, err);
		ret = -2;
		goto __exit;
	}

	/* listen for connections */
	pcb = tcp_listen(pcb);
	if (!pcb)
	{
		printf("Out of memory while tcp_listen\r\n");
		ret = -3;
	}

	/* specify callback to use for incoming connections */
	tcp_accept(pcb, accept_callback);

	/* create success */
	printf("TCP echo server started @ port %d\r\n", port);
	return ret;

__exit:
	if (pcb)
		memp_free(MEMP_TCP_PCB, pcb);
	return ret;
}

/**
 * TCP发送数据
 * @param  buf 待发送的数据
 * @param  len 数据长度
 * @return     返回实际发送的字节数
 */
int user_senddata(const void* buf, uint32_t len)
{
	return tcp_server_send(tcppcb, buf, len);
}

/**
 * 轮询函数,放置于main函数的while死循环中
 * @return 无
 */
int transfer_data(void)
{
	uint32_t nsend = 0;
	if (tcppcb != NULL && tcppcb->state == ESTABLISHED) /* 连接有效 */
	{
		if (TCP_RX_STA & 0x8000)   /* 有数据收到  */
		{
			nsend = user_senddata(TCP_RX_BUF, TCP_RX_STA & 0x7FFF);    /* 将接收到的数据发回去  */
			TCP_RX_STA = 0;
			printf("send %d bytes success\r\n", nsend);
		}
	}
	return 0;
}
/* USER CODE END Application */

2.下载运行测试

下载完成后,串口输出:TCP echo server started @ port 6001

netif_up

点击网络调试助手的连接按钮,显示连接成功,串口输出:192.168.111.11:57991 connect.

点击网络调试助手的发送,网络调试助手的接收区显示收到相同的数据,同时串口打印接收字节数

点击网络调试助手的断开,串口输出:disconnected.

再次点击连接后可以继续通讯。

代码部分参考了 这篇文章
STM32F407 + LAN8720A + LWIP 实现TCP服务器

相关推荐
无名之逆6 小时前
Rust 开发提效神器:lombok-macros 宏库
服务器·开发语言·前端·数据库·后端·python·rust
rainFFrain7 小时前
单例模式与线程安全
linux·运维·服务器·vscode·单例模式
GalaxyPokemon7 小时前
Muduo网络库实现 [九] - EventLoopThread模块
linux·服务器·c++
weixin_508821657 小时前
1ms软延时,不用定时器,stm32
stm32·单片机·嵌入式硬件
xujiangyan_8 小时前
nginx的反向代理和负载均衡
服务器·网络·nginx
GalaxyPokemon8 小时前
Muduo网络库实现 [十] - EventLoopThreadPool模块
linux·服务器·网络·c++
自由鬼8 小时前
开源虚拟化管理平台Proxmox VE部署超融合
linux·运维·服务器·开源·虚拟化·pve
weixin_508821659 小时前
stm32 主频216MHz,写个ms延时函数,us延时函数
stm32·单片机·嵌入式硬件
华清远见IT开放实验室9 小时前
【新书上市】《STM32嵌入式系统开发—基于STM32CubeMX和HAL库》,高校教学推荐,STM32入门必读书籍!
stm32·单片机·嵌入式硬件·虚拟仿真·必读书籍·高校教学
忆源9 小时前
SOME/IP-SD -- 协议英文原文讲解9(ERROR处理)
网络·网络协议·tcp/ip