Udp client based on rt-thread

Udp client based on rt-thread


This article blog link:http://blog.csdn.net/jdh99 , author: jdh, reprint please specify.

 

surroundings:

Development environment: MDK5.23

rt-thread version: 2.1.0

lwip version: 1.4.1

Single chip microcomputer model: stm32f407

phy chip model:


Description:
This program is a udp client module. Bind fixed ports for sending and receiving. The udp receiving is a thread, and the received data is pushed to other processing modules using the mailbox mechanism. udp sending provides a variety of sending interfaces.

Source code:
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);
}
Examples of received application modules:
/**
* 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;
                    }
                }
            }
        }
    }
}

Guess you like

Origin blog.csdn.net/jdh99/article/details/79212806