本文博客链接: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;
}
}
}
}
}
}