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