路由器tcp和udp设置(WIFI模块开发教程之W600网络篇4)
路由器tcp和udp设置(WIFI模块开发教程之W600网络篇4)(3)TCP对系统资源要求较高,UDP较小(2)UDP程序结构较简单UDP工作原理可以用信件来说明,寄信之前需要在信封上写上寄信人和收信人的地址,之后贴上邮票放进信箱即可,使用信件方式,我们无法确认对方是否收到,寄信过程中也可能发生丢失信件情况,总而言之,信件是一种不可靠的传输方式,UDP也是类似,提供不可靠的通信服务。UDP和TCP的区别?(1)TCP是基于连接的服务,UDP是基于无连接的服务
前言
本文研究如何使用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(ints const void *dataptr size_tsize intflags const structsockaddr*to socklen_ttolen)
- 参数
- s套接字描述符
- dataptr发送的数据指针
- size发送的数据长度
- flags标志,一般为0
- to目标地址结构体指针
- tolen目标地址结构体长度
- 返回
- 大于0 成功,返回发送的数据的长度;小于等于0 失败。
int recvfrom(ints void *mem size_tlen intflags structsockaddr*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 <rtthread.h>
#include <rtdevice.h>
#include <sys/socket.h> //使用BSD socket需要包含此头文件
#define UDP_LOCAL_PORT 2000
static rt_sem_t wait_sem = NULL;
char *send_data = "hello UDP object!\r\n";
static void wifi_connect_callback(int event struct rt_wlan_buff *buff void *parameter)
{
rt_kprintf("%s\n" __FUNCTION__);
rt_sem_release(wait_sem);
}
static void wifi_disconnect_callback(int event struct rt_wlan_buff *buff void *parameter)
{
rt_kprintf("%s\n" __FUNCTION__);
if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
{
rt_kprintf("ssid : %s\r\n" ((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\n" __FUNCTION__);
if ((buff != RT_NULL) && (buff->len == sizeof(struct rt_wlan_info)))
{
rt_kprintf("ssid : %s\r\n" ((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!!!\r\n");
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!!!\r\n");
}
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\r\n" 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!\r\n");
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!\r\n");
}
int main(void)
{
rt_err_t ret = RT_EOK;
char str[] = "hello world!\r\n";
// 创建一个动态信号量,初始值为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!\r\n");
}
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!\r\n");
}
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!\r\n");
}
// 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 ...\n");
// wait until module connect to ap success
ret = rt_sem_take(wait_sem RT_WAITING_FOREVER);
if (0 != ret)
{
rt_kprintf("wait_sem error!\r\n");
}
rt_kprintf("connect to AP success!\r\n");
//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 模块收到数据后,返回给网络助手。
网络助手界面如下:
块调试串口信息如下:
四、结语
1.总结:
本节完,实际操作过程中需要注意的地方有如下几点:
(1) 解决hardfault问题
程序运行过程中,偶先hardfault问题,从提示的任务状态信息可以看到tcpip thread使用了100% 发生溢出,由此可知,需要增大tcpip线程堆栈大小。
修改地方在bsp/w60x/rt_config.h文件中 具体位置如下图所示:
将1024增大为4096即可。
本文由小驿原创,欢迎关注,带你一起长知识!
寄语:人的成熟是从认识到自己的不完美开始!