基于rt-thread的udp用戶端
本文部落格連結:http://blog.csdn.net/jdh99,作者:jdh,轉載請注明.
環境:
開發環境:MDK5.23
rt-thread版本:2.1.0
lwip版本:1.4.1
單片機型号:stm32f407
phy晶片型号:
說明:
本程式是udp用戶端子產品。綁定固定端口進行收發。udp接收是一個線程,接收到資料利用郵箱機制推送到其他處理子產品。udp發送提供了多種發送接口。
源碼: udp_socket.h
/**
* Copyright (c), 2015-2025
* @file udp_socket.h
* @brief udp端口頭檔案
* @author jdh
* @verbatim
* Change Logs:
* Date Author Notes
* 2017-12-22 jdh 建立
* @endverbatim
*/
#ifndef _UDP_SOCKET_H_
#define _UDP_SOCKET_H_
#include "drivers.h"
/**
* @brief 最大注冊郵箱數
*/
#define MAX_NUM_UDP_SOCKET_MAILBOX 10
/**
* @brief 接收資料結構.郵箱會推送此結構指針
*/
struct UdpSocketRx
{
T_Buffer_Large buffer;
struct sockaddr_in sockaddr;
};
/**
* @brief 子產品載入
*/
void udp_socket_load(void);
/**
* @brief 更新伺服器資訊
*/
void udp_socket_update_server_info(void);
/**
* @brief socket是否工作
* @return true: 工作.false: 不工作
*/
bool udp_socket_is_work(void);
/**
* @brief 發送資料
* @param data:發送資料存放位址
* @param size:發送資料位元組數
* @param dst_ip: 目的ip
* @param dst_port: 目的端口
*/
void udp_socket_tx(uint8_t *data, uint16_t size, char *dst_ip, uint16_t dst_port);
/**
* @brief 發送資料
* @param data:發送資料存放位址
* @param size:發送資料位元組數
* @param sockaddr: 目的位址
*/
void udp_socket_tx_sockaddr(uint8_t *data, uint16_t size, struct sockaddr_in sockaddr);
/**
* @brief 發送資料給伺服器
* @param data:發送資料存放位址
* @param size:發送資料位元組數
*/
void udp_socket_tx_server(uint8_t *data, uint16_t size);
/**
* @brief 發送資料給配置伺服器
* @note 配置伺服器無效則發送給伺服器
* @param data:發送資料存放位址
* @param size:發送資料位元組數
*/
void udp_socket_tx_config_server(uint8_t *data, uint16_t size);
/**
* @brief 注冊郵箱
* @note 接收資料後會推送到此郵箱
* @param mailbox: 郵箱位址
*/
void udp_socket_register_mailbox(rt_mailbox_t mailbox);
/**
* @brief 增加傳輸層頭後發送資料
* @param dst_device: 目标裝置類型
* @param data: 發送資料存放位址
* @param size: 發送資料位元組數
* @param sockaddr: 目的位址
*/
void udp_socket_tx_sockaddr_add_trans_head(uint8_t dst_device, uint8_t *data, uint16_t size, struct sockaddr_in sockaddr);
/**
* @brief 發送确認幀
* @param dst_device: 目标裝置類型
* @param cmd: 确認幀指令字
* @param ack_cmd: 需要确認的指令
* @param sockaddr: 目的位址
*/
void udp_socket_tx_sockaddr_ack_frame(uint8_t dst_device, uint8_t cmd, uint8_t ack_cmd, struct sockaddr_in sockaddr);
/**
* @brief 增加傳輸層頭後發送資料
* @param dst_device: 目标裝置類型
* @param data: 發送資料存放位址
* @param size: 發送資料位元組數
* @param sockaddr: 目的位址
*/
void udp_socket_tx_server_add_trans_head(uint8_t dst_device, uint8_t *data, uint16_t size);
/**
* @brief 發送确認幀
* @param dst_device: 目标裝置類型
* @param cmd: 确認幀指令字
* @param ack_cmd: 需要确認的指令
* @param sockaddr: 目的位址
*/
void udp_socket_tx_server_ack_frame(uint8_t dst_device, uint8_t cmd, uint8_t ack_cmd);
#endif
udp_socket.c
/**
* Copyright (c), 2015-2025
* @file udp_socket.c
* @brief udp端口主檔案
* @author jdh
* @verbatim
* Change Logs:
* Date Author Notes
* 2017-12-22 jdh 建立
* 2017-12-26 jdh 增加網絡和lwip功能
* @endverbatim
*/
#include "framework.h"
#include "stm32f4xx_eth.h"
#include <netif/ethernetif.h>
#define TAG "LOG_UDP"
/**
* @brief 配置幀逾時時間.機關:ms.超過這個時間,射頻子產品的回複就不會再發向配置伺服器
*/
#define CONFIG_TIMEOUT 500
/**
* @brief 啟動穩定期.機關:ms
*/
#define STARTUP_WAIT_TIME 1000
/**
* @brief 日志項編号
*/
static uint8_t _log_item = 0;
static int _socket;
static bool _is_net_work = false;
static struct sockaddr_in _server_addr;
static struct UdpSocketRx _udp_socket_rx;
/**
* @brief 配置伺服器的位址
*/
static struct sockaddr_in _config_server_addr;
static T_Time _last_config_frame_time;
/**
* @brief 郵箱數組
*/
static struct rt_mailbox *_mailbox_array[MAX_NUM_UDP_SOCKET_MAILBOX];
static uint8_t _len_mailbox_array = 0;
/**
* @brief 發送資料
*/
static T_Buffer_Large Buffer_Tx;
static void thread_init(void* parameter);
static void init_lwip(void);
static void set_ip(void);
static void bind_socket(void);
static void socket_rx(void);
static bool is_frame_valid(void);
static inline void send_mailbox(void);
/**
* @brief 子產品載入
*/
void udp_socket_load(void)
{
_log_item = log_register(TAG);
#ifdef RT_USING_LWIP
rt_thread_t tid_init = rt_thread_create("init_net",
thread_init, (void*)0,
THREAD_STACK_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_SLICE_NORMAL);
rt_thread_startup(tid_init);
#endif
}
static void thread_init(void* parameter)
{
init_lwip();
udp_socket_update_server_info();
bind_socket();
socket_rx();
}
static void init_lwip(void)
{
/* LwIP Initialization */
{
extern void lwip_sys_init(void);
/* register ethernetif device */
eth_system_device_init();
// rt_hw_stm32_eth_init();
rt_hw_stm32_eth_init_my();
/* init lwip system */
lwip_sys_init();
rt_kprintf("TCP/IP initialized!\n");
}
set_ip();
}
static void set_ip(void)
{
set_if("e0", para_manage_read_ip(), para_manage_read_gateway(), para_manage_read_mask());
set_dns(para_manage_read_dns());
}
/**
* @brief 更新伺服器資訊
*/
void udp_socket_update_server_info(void)
{
_server_addr.sin_family = AF_INET;
_server_addr.sin_port = htons(para_manage_read_server_port());
struct hostent *host;
host = (struct hostent *)gethostbyname(para_manage_read_server_ip());
_server_addr.sin_addr = *((struct in_addr *)host->h_addr);
rt_memset(&(_server_addr.sin_zero), 0, sizeof(_server_addr.sin_zero));
}
static void bind_socket(void)
{
struct sockaddr_in local_addr;
// 建立socket
if ((_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
log_w(_log_item, "socket error\n");
// todo
return;
}
// 初始化本地位址
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(para_manage_read_port());
local_addr.sin_addr.s_addr = INADDR_ANY;
rt_memset(&(local_addr.sin_zero), 0, sizeof(local_addr.sin_zero));
// 綁定端口
if (bind(_socket, (struct sockaddr *)&local_addr, sizeof(struct sockaddr)) == -1)
{
log_w(_log_item, "Bind error\n");
// todo
return;
}
thread_delay(STARTUP_WAIT_TIME);
_is_net_work = true;
}
static void socket_rx(void)
{
rt_uint32_t addr_len;
addr_len = sizeof(struct sockaddr);
while (1)
{
_udp_socket_rx.buffer.len = recvfrom(_socket,
_udp_socket_rx.buffer.buf,
LEN_BUFFER_LARGE - 1,
0, (struct sockaddr *)&_udp_socket_rx.sockaddr, &addr_len);
if (is_frame_valid())
{
log_i(_log_item, "udp rx ip:%s port:%02d\n", inet_ntoa(_udp_socket_rx.sockaddr.sin_addr.s_addr),
ntohs(_udp_socket_rx.sockaddr.sin_port));
if (_udp_socket_rx.buffer.buf[PTH_DST_DEVICE_POS] == DEVICE_STATION)
{
// 發送給本機
send_mailbox();
}
else
{
// 轉發
uart_tx(_udp_socket_rx.buffer.buf[PTH_ADDITIONAL_POS], _udp_socket_rx.buffer.buf, _udp_socket_rx.buffer.len);
// 儲存配置伺服器資訊
if (_udp_socket_rx.buffer.buf[PTH_DST_DEVICE_POS] == DEVICE_TIME_MODULE ||
_udp_socket_rx.buffer.buf[PTH_DST_DEVICE_POS] == DEVICE_RADIO_MODULE)
{
_config_server_addr = _udp_socket_rx.sockaddr;
_last_config_frame_time = get_local_time();
}
}
led_blink(LED_RX_NET);
log_add_num_rx_udp_frame();
}
}
}
static bool is_frame_valid(void)
{
if (_udp_socket_rx.buffer.len < PTH_LEN_FRAME_HEAD)
{
return false;
}
uint16_t frame_head = (_udp_socket_rx.buffer.buf[PTH_LEN_FRAME_HEAD_POS] << 8) +
_udp_socket_rx.buffer.buf[PTH_LEN_FRAME_HEAD_POS + 1];
if (frame_head != PTH_HEAD)
{
return false;
}
uint16_t body_len = (_udp_socket_rx.buffer.buf[PTH_BODY_LEN_POS] << 8) + _udp_socket_rx.buffer.buf[PTH_BODY_LEN_POS + 1];
if (_udp_socket_rx.buffer.len != body_len + PTH_LEN_FRAME_HEAD)
{
return false;
}
if (_udp_socket_rx.buffer.buf[PTH_ADDITIONAL_POS] > MODULE_NUM)
{
return false;
}
uint16_t crc_get = (_udp_socket_rx.buffer.buf[PTH_BODY_CRC_POS] << 8) + _udp_socket_rx.buffer.buf[PTH_BODY_CRC_POS + 1];
uint16_t crc_calc = crc_code(_udp_socket_rx.buffer.buf + PTH_LEN_FRAME_HEAD, body_len);
if (crc_get != crc_calc)
{
return false;
}
return true;
}
static inline void send_mailbox(void)
{
for (uint8_t i = 0; i < _len_mailbox_array; i++)
{
rt_mb_send(_mailbox_array[i], (rt_uint32_t)&_udp_socket_rx);
}
}
/**
* @brief socket是否工作
* @return true: 工作.false: 不工作
*/
bool udp_socket_is_work(void)
{
return _is_net_work;
}
/**
* @brief 發送資料
* @param data:發送資料存放位址
* @param size:發送資料位元組數
* @param dst_ip: 目的ip
* @param dst_port: 目的端口
*/
void udp_socket_tx(uint8_t *data, uint16_t size, char *dst_ip, uint16_t dst_port)
{
if (!_is_net_work)
{
return;
}
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(dst_port);
struct hostent *host;
host = (struct hostent *)gethostbyname(dst_ip);
remote_addr.sin_addr = *((struct in_addr *)host->h_addr);
rt_memset(&(remote_addr.sin_zero), 0, sizeof(remote_addr.sin_zero));
sendto(_socket, data, size, 0, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr));
led_blink(LED_TX_NET);
log_add_num_tx_udp_frame();
}
/**
* @brief 發送資料
* @param data:發送資料存放位址
* @param size:發送資料位元組數
* @param sockaddr: 目的位址
*/
void udp_socket_tx_sockaddr(uint8_t *data, uint16_t size, struct sockaddr_in sockaddr)
{
if (!_is_net_work)
{
return;
}
sendto(_socket, data, size, 0, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr));
led_blink(LED_TX_NET);
log_add_num_tx_udp_frame();
}
/**
* @brief 發送資料給伺服器
* @param data:發送資料存放位址
* @param size:發送資料位元組數
*/
void udp_socket_tx_server(uint8_t *data, uint16_t size)
{
if (!_is_net_work)
{
return;
}
sendto(_socket, data, size, 0, (struct sockaddr *)&_server_addr, sizeof(struct sockaddr));
led_blink(LED_TX_NET);
log_add_num_tx_udp_frame();
}
/**
* @brief 發送資料給配置伺服器
* @note 配置伺服器無效則發送給伺服器
* @param data:發送資料存放位址
* @param size:發送資料位元組數
*/
void udp_socket_tx_config_server(uint8_t *data, uint16_t size)
{
if (!_is_net_work)
{
return;
}
bool is_valid = false;
T_Time time = get_local_time();
if (time.s - _last_config_frame_time.s < 2)
{
if (sub_time(get_local_time(), _last_config_frame_time) < CONFIG_TIMEOUT * 1000)
{
is_valid = true;
}
}
if (is_valid)
{
sendto(_socket, data, size, 0, (struct sockaddr *)&_config_server_addr, sizeof(struct sockaddr));
}
else
{
sendto(_socket, data, size, 0, (struct sockaddr *)&_server_addr, sizeof(struct sockaddr));
}
led_blink(LED_TX_NET);
log_add_num_tx_udp_frame();
}
/**
* @brief 注冊郵箱
* @note 接收資料後會推送到此郵箱
* @param mailbox: 郵箱位址
*/
void udp_socket_register_mailbox(rt_mailbox_t mailbox)
{
_mailbox_array[_len_mailbox_array++] = mailbox;
}
/**
* @brief 增加傳輸層頭後發送資料
* @param dst_device: 目标裝置類型
* @param data: 發送資料存放位址
* @param size: 發送資料位元組數
* @param sockaddr: 目的位址
*/
void udp_socket_tx_sockaddr_add_trans_head(uint8_t dst_device, uint8_t *data, uint16_t size, struct sockaddr_in sockaddr)
{
if (size > PTH_MAX_BODY_LEN)
{
return;
}
// 幀頭
Buffer_Tx.buf[PTH_LEN_FRAME_HEAD_POS] = PTH_HEAD >> 8;
Buffer_Tx.buf[PTH_LEN_FRAME_HEAD_POS + 1] = PTH_HEAD & 0xff;
// 源裝置類型
Buffer_Tx.buf[PTH_SRC_DEVICE_POS] = DEVICE_MY;
// 目的裝置類型
Buffer_Tx.buf[PTH_DST_DEVICE_POS] = dst_device;
// 附加資訊
Buffer_Tx.buf[PTH_ADDITIONAL_POS] = 0;
// 正文長度
Buffer_Tx.buf[PTH_BODY_LEN_POS] = size >> 8;
Buffer_Tx.buf[PTH_BODY_LEN_POS + 1] = size;
// 正文CRC
uint16_t crc_calc = crc_code(data, size);
Buffer_Tx.buf[PTH_BODY_CRC_POS] = crc_calc >> 8;
Buffer_Tx.buf[PTH_BODY_CRC_POS + 1] = crc_calc;
// 正文
memcpy(Buffer_Tx.buf + PTH_LEN_FRAME_HEAD, data, size);
Buffer_Tx.len = size + PTH_LEN_FRAME_HEAD;
udp_socket_tx_sockaddr(Buffer_Tx.buf, Buffer_Tx.len, sockaddr);
}
/**
* @brief 發送确認幀
* @param dst_device: 目标裝置類型
* @param cmd: 确認幀指令字
* @param ack_cmd: 需要确認的指令
* @param sockaddr: 目的位址
*/
void udp_socket_tx_sockaddr_ack_frame(uint8_t dst_device, uint8_t cmd, uint8_t ack_cmd, struct sockaddr_in sockaddr)
{
T_Buffer buffer;
buffer.len = 0;
buffer.buf[buffer.len++] = cmd;
buffer.buf[buffer.len++] = ack_cmd;
udp_socket_tx_sockaddr_add_trans_head(dst_device, buffer.buf, buffer.len, sockaddr);
}
/**
* @brief 增加傳輸層頭後發送資料
* @param dst_device: 目标裝置類型
* @param data: 發送資料存放位址
* @param size: 發送資料位元組數
* @param sockaddr: 目的位址
*/
void udp_socket_tx_server_add_trans_head(uint8_t dst_device, uint8_t *data, uint16_t size)
{
if (size > PTH_MAX_BODY_LEN)
{
return;
}
// 幀頭
Buffer_Tx.buf[PTH_LEN_FRAME_HEAD_POS] = PTH_HEAD >> 8;
Buffer_Tx.buf[PTH_LEN_FRAME_HEAD_POS + 1] = PTH_HEAD & 0xff;
// 源裝置類型
Buffer_Tx.buf[PTH_SRC_DEVICE_POS] = DEVICE_MY;
// 目的裝置類型
Buffer_Tx.buf[PTH_DST_DEVICE_POS] = dst_device;
// 附加資訊
Buffer_Tx.buf[PTH_ADDITIONAL_POS] = 0;
// 正文長度
Buffer_Tx.buf[PTH_BODY_LEN_POS] = size >> 8;
Buffer_Tx.buf[PTH_BODY_LEN_POS + 1] = size;
// 正文CRC
uint16_t crc_calc = crc_code(data, size);
Buffer_Tx.buf[PTH_BODY_CRC_POS] = crc_calc >> 8;
Buffer_Tx.buf[PTH_BODY_CRC_POS + 1] = crc_calc;
// 正文
memcpy(Buffer_Tx.buf + PTH_LEN_FRAME_HEAD, data, size);
Buffer_Tx.len = size + PTH_LEN_FRAME_HEAD;
udp_socket_tx_server(Buffer_Tx.buf, Buffer_Tx.len);
}
/**
* @brief 發送确認幀
* @param dst_device: 目标裝置類型
* @param cmd: 确認幀指令字
* @param ack_cmd: 需要确認的指令
* @param sockaddr: 目的位址
*/
void udp_socket_tx_server_ack_frame(uint8_t dst_device, uint8_t cmd, uint8_t ack_cmd)
{
T_Buffer buffer;
buffer.len = 0;
buffer.buf[buffer.len++] = cmd;
buffer.buf[buffer.len++] = ack_cmd;
udp_socket_tx_server(Buffer_Tx.buf, Buffer_Tx.len);
}
接收的應用子產品示例:
/**
* Copyright (c), 2015-2025
* @file remote_reset.c
* @brief 遠端複位功能子產品主檔案
* @author jdh
* @verbatim
* Change Logs:
* Date Author Notes
* 2018-01-08 jdh 建立
* @endverbatim
*/
#include "remote_reset.h"
#include "protocol.h"
#define TAG "LOG:REMOTE_RESET"
/**
* @brief 日志項編号
*/
static uint8_t _log_item = 0;
static void thread_udp_rx(void* parameter);
/**
* @brief 子產品載入
*/
void remote_reset_load(void)
{
_log_item = log_register(TAG);
rt_thread_t tid_udp_rx = rt_thread_create("rr_udp_rx",
thread_udp_rx, (void*)0,
THREAD_STACK_BIG, THREAD_PRIORITY_NORMAL, THREAD_SLICE_NORMAL);
rt_thread_startup(tid_udp_rx);
}
static void thread_udp_rx(void* parameter)
{
rt_mailbox_t mb = rt_mb_create("mb_udp_rx", 32, RT_IPC_FLAG_FIFO);
udp_socket_register_mailbox(mb);
struct UdpSocketRx *udp_socket_rx;
while (1)
{
if (rt_mb_recv(mb, (rt_uint32_t *)&udp_socket_rx, RT_WAITING_FOREVER) == RT_EOK)
{
if (udp_socket_rx->buffer.buf[PTH_SRC_DEVICE_POS] == DEVICE_SERVER)
{
uint8_t cmd = udp_socket_rx->buffer.buf[PTH_LEN_FRAME_HEAD];
switch (cmd)
{
case PSRAS_RESET:
{
log_w(_log_item, "udp rx remote reset cmd\n");
// 應答
udp_socket_tx_sockaddr_ack_frame(DEVICE_SERVER, PSRAS_ACK, PSRAS_RESET, udp_socket_rx->sockaddr);
// 複位生效
reset_manage_delay_reset();
break;
}
}
}
}
}
}