4996b451d9
集成CH390驱动、LwIP协议栈和FreeRTOS多任务透传框架,确保TCP Server/Client与UART链路按配置稳定联动。
406 lines
9.8 KiB
C
406 lines
9.8 KiB
C
/**
|
|
* @file tcp_server.c
|
|
* @brief TCP Server module implementation for transparent transmission with UART2
|
|
*/
|
|
|
|
#include "tcp_server.h"
|
|
#include "config.h"
|
|
#include "lwip/opt.h"
|
|
#include "lwip/tcp.h"
|
|
#include "lwip/sys.h"
|
|
#include "lwip/sockets.h"
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "stream_buffer.h"
|
|
|
|
#include <string.h>
|
|
|
|
/*---------------------------------------------------------------------------
|
|
* Private Variables
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
/* Server configuration */
|
|
static tcp_server_config_t server_config = {
|
|
.port = TCP_SERVER_DEFAULT_PORT,
|
|
.auto_reconnect = true
|
|
};
|
|
|
|
/* Server status */
|
|
static tcp_server_status_t server_status = {
|
|
.state = TCP_SERVER_STATE_IDLE,
|
|
.rx_bytes = 0,
|
|
.tx_bytes = 0,
|
|
.connections = 0,
|
|
.errors = 0
|
|
};
|
|
|
|
/* Socket descriptors */
|
|
static int listen_socket = -1;
|
|
static int client_socket = -1;
|
|
|
|
/* Stream buffers for UART integration */
|
|
static StreamBufferHandle_t rx_stream = NULL; /* TCP RX -> UART TX */
|
|
static StreamBufferHandle_t tx_stream = NULL; /* UART RX -> TCP TX */
|
|
|
|
/*---------------------------------------------------------------------------
|
|
* Public Functions
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
static int tcp_server_send_all(int sock, const uint8_t *data, uint16_t len)
|
|
{
|
|
uint16_t total = 0;
|
|
|
|
while (total < len)
|
|
{
|
|
int sent = send(sock, data + total, len - total, 0);
|
|
if (sent > 0)
|
|
{
|
|
total += (uint16_t)sent;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return (int)total;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize TCP Server module
|
|
*/
|
|
int tcp_server_init(const tcp_server_config_t *config)
|
|
{
|
|
if (config != NULL)
|
|
{
|
|
memcpy(&server_config, config, sizeof(tcp_server_config_t));
|
|
}
|
|
|
|
/* Create stream buffers */
|
|
if (rx_stream == NULL)
|
|
{
|
|
rx_stream = xStreamBufferCreate(TCP_SERVER_RX_BUFFER_SIZE, 1);
|
|
if (rx_stream == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (tx_stream == NULL)
|
|
{
|
|
tx_stream = xStreamBufferCreate(TCP_SERVER_TX_BUFFER_SIZE, 1);
|
|
if (tx_stream == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
server_status.state = TCP_SERVER_STATE_IDLE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Start TCP Server
|
|
*/
|
|
int tcp_server_start(void)
|
|
{
|
|
struct sockaddr_in server_addr;
|
|
int opt = 1;
|
|
|
|
if (listen_socket >= 0)
|
|
{
|
|
/* Already started */
|
|
return 0;
|
|
}
|
|
|
|
/* Create socket */
|
|
listen_socket = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (listen_socket < 0)
|
|
{
|
|
server_status.state = TCP_SERVER_STATE_ERROR;
|
|
server_status.errors++;
|
|
return -1;
|
|
}
|
|
|
|
/* Set socket options */
|
|
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
|
|
|
/* Bind to port */
|
|
memset(&server_addr, 0, sizeof(server_addr));
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_addr.s_addr = INADDR_ANY;
|
|
server_addr.sin_port = htons(server_config.port);
|
|
|
|
if (bind(listen_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
|
|
{
|
|
close(listen_socket);
|
|
listen_socket = -1;
|
|
server_status.state = TCP_SERVER_STATE_ERROR;
|
|
server_status.errors++;
|
|
return -1;
|
|
}
|
|
|
|
/* Start listening */
|
|
if (listen(listen_socket, TCP_SERVER_MAX_CONNECTIONS) < 0)
|
|
{
|
|
close(listen_socket);
|
|
listen_socket = -1;
|
|
server_status.state = TCP_SERVER_STATE_ERROR;
|
|
server_status.errors++;
|
|
return -1;
|
|
}
|
|
|
|
server_status.state = TCP_SERVER_STATE_LISTENING;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Stop TCP Server
|
|
*/
|
|
int tcp_server_stop(void)
|
|
{
|
|
if (client_socket >= 0)
|
|
{
|
|
close(client_socket);
|
|
client_socket = -1;
|
|
}
|
|
|
|
if (listen_socket >= 0)
|
|
{
|
|
close(listen_socket);
|
|
listen_socket = -1;
|
|
}
|
|
|
|
server_status.state = TCP_SERVER_STATE_IDLE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Send data to connected client
|
|
*/
|
|
int tcp_server_send(const uint8_t *data, uint16_t len)
|
|
{
|
|
int sent;
|
|
|
|
if (client_socket < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
sent = tcp_server_send_all(client_socket, data, len);
|
|
if (sent > 0)
|
|
{
|
|
server_status.tx_bytes += sent;
|
|
}
|
|
else if (sent < 0)
|
|
{
|
|
/* Connection error */
|
|
close(client_socket);
|
|
client_socket = -1;
|
|
server_status.state = TCP_SERVER_STATE_LISTENING;
|
|
server_status.errors++;
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
/**
|
|
* @brief Receive data from connected client
|
|
*/
|
|
int tcp_server_recv(uint8_t *data, uint16_t max_len, uint32_t timeout_ms)
|
|
{
|
|
int received;
|
|
struct timeval tv;
|
|
|
|
if (client_socket < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/* Set receive timeout */
|
|
tv.tv_sec = timeout_ms / 1000;
|
|
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
|
setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
|
|
received = recv(client_socket, data, max_len, 0);
|
|
if (received > 0)
|
|
{
|
|
server_status.rx_bytes += received;
|
|
}
|
|
else if (received == 0)
|
|
{
|
|
/* Connection closed by client */
|
|
close(client_socket);
|
|
client_socket = -1;
|
|
server_status.state = TCP_SERVER_STATE_LISTENING;
|
|
}
|
|
else if (received < 0)
|
|
{
|
|
/* Timeout or error - check errno */
|
|
/* For timeout, just return 0 */
|
|
}
|
|
|
|
return received;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if client is connected
|
|
*/
|
|
bool tcp_server_is_connected(void)
|
|
{
|
|
return (client_socket >= 0);
|
|
}
|
|
|
|
/**
|
|
* @brief Get TCP Server status
|
|
*/
|
|
void tcp_server_get_status(tcp_server_status_t *status)
|
|
{
|
|
if (status != NULL)
|
|
{
|
|
memcpy(status, &server_status, sizeof(tcp_server_status_t));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get RX StreamBuffer handle
|
|
*/
|
|
void *tcp_server_get_rx_stream(void)
|
|
{
|
|
return rx_stream;
|
|
}
|
|
|
|
/**
|
|
* @brief Get TX StreamBuffer handle
|
|
*/
|
|
void *tcp_server_get_tx_stream(void)
|
|
{
|
|
return tx_stream;
|
|
}
|
|
|
|
/**
|
|
* @brief TCP Server task
|
|
*/
|
|
void tcp_server_task(void *argument)
|
|
{
|
|
const device_config_t *cfg;
|
|
tcp_server_config_t task_cfg;
|
|
struct sockaddr_in client_addr;
|
|
socklen_t addr_len;
|
|
uint8_t rx_buffer[256];
|
|
uint8_t tx_buffer[256];
|
|
int received;
|
|
size_t tx_len;
|
|
fd_set read_fds;
|
|
struct timeval tv;
|
|
int max_fd;
|
|
|
|
(void)argument;
|
|
|
|
/* Initialize server */
|
|
task_cfg.port = TCP_SERVER_DEFAULT_PORT;
|
|
task_cfg.auto_reconnect = true;
|
|
cfg = config_get();
|
|
if (cfg != NULL && cfg->server_port > 0)
|
|
{
|
|
task_cfg.port = cfg->server_port;
|
|
}
|
|
tcp_server_init(&task_cfg);
|
|
|
|
/* Start server */
|
|
while (tcp_server_start() != 0)
|
|
{
|
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
/* Check if we need to accept a new connection */
|
|
if (client_socket < 0 && listen_socket >= 0)
|
|
{
|
|
/* Use select with timeout to check for incoming connections */
|
|
FD_ZERO(&read_fds);
|
|
FD_SET(listen_socket, &read_fds);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 100000; /* 100ms timeout */
|
|
|
|
if (select(listen_socket + 1, &read_fds, NULL, NULL, &tv) > 0)
|
|
{
|
|
addr_len = sizeof(client_addr);
|
|
client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, &addr_len);
|
|
if (client_socket >= 0)
|
|
{
|
|
server_status.state = TCP_SERVER_STATE_CONNECTED;
|
|
server_status.connections++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Handle data transfer if connected */
|
|
if (client_socket >= 0)
|
|
{
|
|
/* Check for data from TCP client */
|
|
FD_ZERO(&read_fds);
|
|
FD_SET(client_socket, &read_fds);
|
|
max_fd = client_socket;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 10000; /* 10ms timeout */
|
|
|
|
if (select(max_fd + 1, &read_fds, NULL, NULL, &tv) > 0)
|
|
{
|
|
if (FD_ISSET(client_socket, &read_fds))
|
|
{
|
|
received = recv(client_socket, rx_buffer, sizeof(rx_buffer), 0);
|
|
if (received > 0)
|
|
{
|
|
/* Forward to UART via stream buffer */
|
|
xStreamBufferSend(rx_stream, rx_buffer, received, pdMS_TO_TICKS(10));
|
|
server_status.rx_bytes += received;
|
|
}
|
|
else if (received == 0)
|
|
{
|
|
/* Connection closed */
|
|
close(client_socket);
|
|
client_socket = -1;
|
|
server_status.state = TCP_SERVER_STATE_LISTENING;
|
|
}
|
|
else
|
|
{
|
|
/* Error */
|
|
close(client_socket);
|
|
client_socket = -1;
|
|
server_status.state = TCP_SERVER_STATE_LISTENING;
|
|
server_status.errors++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check for data from UART to send to TCP */
|
|
tx_len = xStreamBufferReceive(tx_stream, tx_buffer, sizeof(tx_buffer), 0);
|
|
if (tx_len > 0)
|
|
{
|
|
int sent = tcp_server_send_all(client_socket, tx_buffer, (uint16_t)tx_len);
|
|
if (sent > 0)
|
|
{
|
|
server_status.tx_bytes += sent;
|
|
}
|
|
else if (sent < 0)
|
|
{
|
|
/* Send error */
|
|
close(client_socket);
|
|
client_socket = -1;
|
|
server_status.state = TCP_SERVER_STATE_LISTENING;
|
|
server_status.errors++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Small delay to prevent tight loop */
|
|
vTaskDelay(pdMS_TO_TICKS(1));
|
|
}
|
|
}
|