天天看點

基于rt-thread的udp用戶端

基于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;
                    }
                }
            }
        }
    }
}
           

繼續閱讀