基于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_LWIPrt_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;// 创建socketif ((_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1){log_w(_log_item, "socket error\n");// todoreturn;}// 初始化本地地址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;// 正文CRCuint16_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;// 正文CRCuint16_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;}}}}}
}

基于rt-thread的udp客户端相关推荐

  1. 基于rt thread smart构建EtherCAT主站

    我把源码开源到到了gitee,https://gitee.com/rathon/rt-thread-smart-soem 有兴趣的去可以下载下来跑一下 软件工程推荐用vscode 打开.rt thre ...

  2. LwIP应用开发笔记之三:LwIP无操作系统UDP客户端

    前一节我们实现了基于RAW API的UDP服务器,在接下来,我们进一步利用RAW API实现UDP客户端. 1.UDP协议简述 UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包 ...

  3. 基于UDP客户端服务器的编程模型-linux网络编程

    坚持在代码中注释,边读代码边学习Linux网络编程 使用到的发送函数原型: #include <sys/types.h>#include <sys/socket.h>ssize ...

  4. 基于GD32F103C8T6添加RT Thread nano设备框架并添加串口设备(以控制台console( uart0 )为例)

    最近没事琢磨了一下使用设备框架的问题.因为将串口注册到设备框架可以应用十分丰富的软件包. 于是就整理了一下手上的工程,重新将工程梳理了一遍. 像这样是十分清爽了,其中RTOS是操作系统源代码 并且学习 ...

  5. java: java mina ——基于TCP/IP、UDP/IP协议栈的通信框架

    Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务.虚拟机管道通信服务等),M ...

  6. Python基于socket实现的TCP客户端

    ''' 基于socket实现的TCP客户端 '''import socket# 建立socket对象 # 参数一表示IP地址类型(AF_INET为IPV4,AF_INET6为IPV6),参数二表示连接 ...

  7. Boost:基于Boost的阻塞udp echo的测试程序

    Boost:基于Boost的阻塞udp echo的测试程序 实现功能 C++实现代码 客户端源码 服务端源码 实现功能 基于Boost的阻塞udp echo的测试程序 C++实现代码 客户端源码 #i ...

  8. Boost:基于Boost的阻塞udp回显服务器

    Boost:基于Boost的阻塞udp回显服务器 实现功能 C++实现代码 客户端源码 服务端源码 实现功能 基于Boost的阻塞udp回显服务器 C++实现代码 客户端源码 #include < ...

  9. 从入门到入土:基于C语言采用UDP协议实现远程控制|详细说明|利用流套接字实现一个简单的远程控制系统|代码展示

    此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...

最新文章

  1. 并查集【CF731C】Socks
  2. 利用python将txt文件中的内容写入Excel文件中
  3. 文献学习(part32)--Density Peak Clustering Based on Relative Density Optimization
  4. 前端学习(3000):vue+element今日头条管理--封装请求模块
  5. BZOJ 4278: [ONTAK2015]Tasowanie 后缀数组 + 贪心 + 细节
  6. html引入vue不兼容ie11,关于vue.js:iview按需引入ie11不兼容报无效字符问题解决
  7. foreach的 多种用法
  8. “Abp.AbpException: Unknown filter name: MayHaveTenant. Be sure this filter is registered before.”的解决
  9. WORD批量更改所有图片大小
  10. Transformer入门Transformer和CNN之间的区别
  11. luogu 大陆争霸 WD yj
  12. vba随机抽取人名不重复_用vb编写个随机滚动抽取人名的抽奖系统,怎么样做到不重复并添加一个记录显示已抽到的人名...
  13. 火狐浏览器常用的几个插件
  14. 闲谈安全测试之IAST
  15. Honeyview 漫画阅读器 v5.30.zip 下载
  16. 初中数学课程与信息技术的整合
  17. linux安装启动openoffice和swftools
  18. FTP服务器是什么意思?FTP服务器怎么搭建?
  19. 【LOJ2863】【IOI2018】组合动作(交互)
  20. multisim怎么设置晶体管rbe_multisim 三极管放大倍数怎么设置

热门文章

  1. Word编辑公式 下划线取消自动套用格式 下划线不转义
  2. 万字Java技术类校招面试题汇总,月薪30K
  3. java计算机毕业设计家庭园艺服务平台源码+数据库+lw文档+系统
  4. widget中文技术文档
  5. 【1024社区大奖】助你狂揽大奖[保姆级教程①]
  6. iQQ 学习笔记1 :登录、验证码、收消息
  7. 测试工作3年还在基础岗?可能只是因为你的工作能力差
  8. 大三下的计划以及找工作的准备
  9. java课堂作业部分
  10. 教你用Python做小游戏