PHP 接收 UDP包_WIFI模块开发教程之W600网络篇4:UDP通信

本文详细介绍了如何利用UDP进行数据通信,包括UDP的工作原理、与TCP的区别,以及关键函数sendto和recvfrom的使用。通过一个实际的程序示例展示了模块如何连接路由器并监听特定端口,接收到数据后原路回传。在实践中,还提到了遇到的hardfault问题及解决方法,即增加tcpip线程堆栈大小。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本文研究如何使用UDP进行数据通信,模块连上路由后,绑定2000端口,监听数据,收到数据后,原路发送回去。

一、 理论基础

1.理解UDP

UDP工作原理可以用信件来说明,寄信之前需要在信封上写上寄信人和收信人的地址,之后贴上邮票放进信箱即可,使用信件方式,我们无法确认对方是否收到,寄信过程中也可能发生丢失信件情况,总而言之,信件是一种不可靠的传输方式,UDP也是类似,提供不可靠的通信服务。

UDP和TCP的区别?

(1)TCP是基于连接的服务,UDP是基于无连接的服务

(2)UDP程序结构较简单

(3)TCP对系统资源要求较高,UDP较小

(4)TCP是基于流模式,UDP基于数据包模式,是两者间最大区别

(5)TCP保证数据正确性,UDP可能丢包

(6)TCP保证数据顺序,UDP不保证

2.要点说明

模块上电首先连接路由器,连接相关的操作和前文完全一致,需要做的就是在连接路由器之后处理UDP通信,UDP通信代码中常用的也是最核心的是sendto和recvfrom两个函数。

int sendto(int s,const void * dataptr,size_t size,int flags,const struct sockaddr * to,socklen_t tolen )
  • 参数
  • s套接字描述符
  • dataptr发送的数据指针
  • size发送的数据长度
  • flags标志,一般为0
  • to目标地址结构体指针
  • tolen目标地址结构体长度
  • 返回
  • 大于0 成功,返回发送的数据的长度;小于等于0 失败。
int recvfrom(int s,void * mem,size_t len,int flags,struct sockaddr * from,socklen_t * fromlen )
  • 参数
  • s套接字描述符mem接收的数据指针len接收的数据长度
  • flags标志,一般为0
  • from接收地址结构体指针
  • fromlen接收地址结构体长度
  • 返回
  • 大于0 成功,返回接收的数据的长度;等于0 接收地址已传输完并关闭连接;小于0 失败。

除了这两个函数还有一个bind函数也很重要,这里将重点说下bind函数,UDP程序中调用sendto函数传输数据前应完成对套接字的地址分配工作,而bind函数的作用就在此,bind在TCP程序中也出现过,但是bind函数不区分TCP和UDP,都可以使用。另外如果调用sendto函数时候发现尚未分配地址信息,则在首次调用sendto函数时候给响应套接字自动分配IP和端口,因此,UDP客户端程序中通常无需额外的地址分配过程。

二、使用实例

1.程序

/* * Copyright (c) 2019 Winner Microelectronics Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date           Author       Notes * 2019-02-13     tyx          first implementation */#include #include #include  //使用BSD socket需要包含此头文件#define UDP_LOCAL_PORT 2000static rt_sem_t wait_sem = NULL;char *send_data = "hello UDP object!";static void wifi_connect_callback(int event, struct rt_wlan_buff *buff, void *parameter){    rt_kprintf("%s", __FUNCTION__);    rt_sem_release(wait_sem);}static void wifi_disconnect_callback(int event, struct rt_wlan_buff *buff, void *parameter){    rt_kprintf("%s", __FUNCTION__);    if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))    {        rt_kprintf("ssid : %s", ((struct rt_wlan_info *)buff->data)->ssid.val);    }}static void wifi_connect_fail_callback(int event, struct rt_wlan_buff *buff, void *parameter){    rt_kprintf("%s", __FUNCTION__);    if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))    {        rt_kprintf("ssid : %s", ((struct rt_wlan_info *)buff->data)->ssid.val);    }}static void udp_thread_entry(void *args){    int ret = 0;    int fd = -1;    struct sockaddr_in addr, remote_addr;    socklen_t addrLen = sizeof(addr);    struct  timeval t;    fd_set readfds;    char buf[512] = { 0x00 };    int len = 0;reconnect:    fd = socket(AF_INET, SOCK_DGRAM, 0);    if (-1 == fd)    {        rt_kprintf("create socket error!!!");        goto exit;    }    addr.sin_family = AF_INET;    addr.sin_port = htons(UDP_LOCAL_PORT);    addr.sin_addr.s_addr = INADDR_ANY;    rt_memset(&addr.sin_zero, 0x00, sizeof(addr.sin_zero));    ret = bind( fd, (struct sockaddr *)&addr, sizeof(addr));    if (0 != ret)    {        rt_kprintf("bind addr error!!!");    }    while (1)    {        len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, &addrLen);        if (len > 0)        {            buf[len] = 0x00;            rt_kprintf("receive data:%s, from %s:%d", buf, inet_ntoa(addr.sin_addr), addr.sin_port);            sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in) );        }else        {            rt_kprintf("receive data from tcp server error!");            goto label_try_reconnect;        }    }label_try_reconnect:    if (-1 != fd)    {        closesocket(fd);    }    rt_thread_mdelay(1000);    goto reconnect;exit:    if (-1 != fd)    {        closesocket(fd);    }    rt_kprintf("thread udp exit!");}int main(void){    rt_err_t ret = RT_EOK;    char str[] = "hello world!";    // 创建一个动态信号量,初始值为0    wait_sem = rt_sem_create("sem_conn", 0, RT_IPC_FLAG_FIFO);    /* Start automatic connection */    rt_wlan_config_autoreconnect(RT_TRUE);    // register event    ret = rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED, wifi_connect_callback, RT_NULL);    if (0 != ret)    {        rt_kprintf("register event handler error!");    }    ret = rt_wlan_register_event_handler(RT_WLAN_EVT_STA_DISCONNECTED, wifi_disconnect_callback, RT_NULL);    if (0 != ret)    {        rt_kprintf("register event handler error!");    }    ret = rt_wlan_register_event_handler(RT_WLAN_EVT_STA_CONNECTED_FAIL, wifi_connect_fail_callback, RT_NULL);    if (0 != ret)    {        rt_kprintf("register event handler error!");    }    // connect to router    rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);    rt_wlan_connect("HUAWEI-6ZCHWJ", "123456789a");    rt_kprintf("start to connect ap ...");    // wait until module connect to ap success    ret = rt_sem_take(wait_sem, RT_WAITING_FOREVER);    if (0 != ret)    {        rt_kprintf("wait_sem error!");    }    rt_kprintf("connect to AP success!");    //create udp thread    rt_thread_t client_thread = rt_thread_create("udp", udp_thread_entry, RT_NULL, 4*1024, 25, 10);    if (client_thread != NULL)    {        rt_thread_startup(client_thread);    }else    {        ret = RT_ERROR;        rt_kprintf("create tcp client error!!!");    }exit:    rt_sem_delete(wait_sem);    return ret;}

2.配置

在applications目录下新建一个文件夹:6-udp_unicast,然后同理需要修改aplications/SConscript脚本。

Import('RTT_ROOT')Import('rtconfig')from building import *cwd = GetCurrentDir()src  = Glob('6-udp_unicast/main.c')CPPPATH = [cwd]group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)Return('group')

三、下载运行

在ENV控制台,输入scons命令,在build/Bin目录下生成rtthread_1M.FLS,

烧录运行后,电脑连接模块起来的热点,然后打开电脑网络调试助手,开启一个UDP服务,IP地址是电脑的IP地址,端口为8089(可自行定义),模块启动UDP服务之后,通过网络助手发送hello world,模块收到数据后,返回给网络助手。

网络助手界面如下:

5ba8fbd7792780bc57f3b47b662842c8.png

块调试串口信息如下:

efac250ddc577619c4f623fb1ba25a37.png

四、结语

1.总结:

本节完,实际操作过程中需要注意的地方有如下几点:

(1) 解决hardfault问题

e1187a6021361d0e059772f5715c7856.png

程序运行过程中,偶先hardfault问题,从提示的任务状态信息可以看到tcpip thread使用了100%,发生溢出,由此可知,需要增大tcpip线程堆栈大小。

修改地方在bsp/w60x/rt_config.h文件中,具体位置如下图所示:

389812ddf3e05b53a8ff84a0b185d656.png

将1024增大为4096即可。

​本文由小驿原创,欢迎关注,带你一起长知识!

寄语:人的成熟是从认识到自己的不完美开始!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值