目录
点击这里查看所有博文
本系列博客所述资料均来自合宙官方
,并不是本人原创(只有博客是自己写的),csdk只是得到了口头的允许公开授权
。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法
。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。
本系列博客基于紫光展锐的RDA8910 LTE Cat 1
bis芯片平台开发。理论上适用于合宙的Air720U、Air724U、广和通L610以及安信可的cat-01模块。
各个厂家的部分配置文件可能不一样,也许会导致设备出现奇怪的问题,其他的模块我也不确定能不能用,自行测试。但是有一点编译下载和监视流程基本一样
,可供参考。
先不管支不支持,如果你用的模块是是紫光展锐的RDA8910,那都不妨一试,也许会有意外收获(也有可能变砖,慎重!!!
)。
我使用的是Air724UG
开发板,如果在其他模块上不能用,那也不要强怼,也许是开发包不兼容吧。这里的代码是没有问题的。例程仅供参考!
一、前言
上篇博客我们一起了解过tcp通讯之后,再来学习udp会轻松得多。程序基本是一样的,就改了几个传入的参数,上篇博客对应的实验要是做出来了,这篇udp的博客对于各位来讲想必不是什么问题。
udp和tcp之间最明显的区别就是udp是无连接
的通讯,UDP是点对点
的通讯,UDP通讯模式只要指定IP和端口就可以的,UDP服务没有客户端和服务器的概念
,UDP是对等网络。tcp服务才有服务器和客户端的概念。UDP服务既可以主动给任何地址发消息,也可以被动接收任何地址传入进来的消息。(注意:这里的服务端是指软件的状态,并不是指远程的服务器
)。
UDP服务没有客户端和服务器的概念并不是说没有UDP服务器(硬件)。将一个UDP的服务,运行在一台远端服务器上,那就是一个提供UDP服务的服务器。本系列教程的第7篇博客—RDA8910CSDK二次开发:通过DNS解析迈向互联网的第一步就是一个UDP服务
,通过向远端的UDP服务器请求解析数据获得网址对应的的ip。
二、编写测试程序
2.1、了解本例程所用到的函数
使用udp服务需要包含#include "iot_socket.h""
头文件,我们这里只用到了6个函数,分别是:
/*设置网络状态回调函数
*@param indCb: 回调函数
*@return TRUE: 成功
* FALSE: 失败
*/
BOOL iot_network_set_cb
(F_OPENAT_NETWORK_IND_CB indCb )
/**创建socket
@param domain: 仅支持AF_INET (IPV4 网络协议)
@param type: 支持SOCK_STREAM/SOCK_DGRAM,分别表示TCP、UDP连接
@param protocol: 仅支持0
@return >=0: socket描述符,用于后续操作
<0: 创建socket失败
@note 创建的socket不用后需要用close将其关闭
*/
int socket
(int domain, int type, int protocol)
/**本地字节顺序转化为网络字节顺序(16bits)
@param n: 本地字节书序数据
*@return 网络字节顺序数据
*/
htons
(n) ((n & 0xff) << 8) | ((n & 0xff00) >> 8)
/**将ip地址字符串转为数值,转化后的数值为网络字节顺序
@param cp: ip地址字符串,例如”192.168.1.1”
*@param addr: struct in_addr 返回的ip地址数值
*@return 1: 成功
* 0: 失败
*/
inet_aton
(cp, addr) ipaddr_aton(cp, (openat_ip_addr_t*)addr)
/*发送数据到指定ip地址,一般用于udp发送数据
*@param socketfd: 调用socket接口返回的socket描述符
@param buf: 数据内容
@param len: 数据长度
@param flags: 仅支持0
@param to_p: 指定ip地址和端口号
@param tolen: sizeof(struct openat_sockaddr)
*@return >=0: 实际发送的长度
* <0 发送错误
*/
int sendto
(int socketfd, const void *buf,size_t len,int flags, const struct openat_sockaddr *to_p, openat_socklen_t tolen)
/**接收指定ip地址发送来的数据,一般用于UDP收取数据
@param socketfd: 调用socket接口返回的socket描述符
@param buf: 用于存放数据的缓存
@param len: buf的长度
@param flags: 仅支持0
@param addr: 支持ip地址和端口
@param addrlen: sizeof(struct openat_sockaddr)
@return >0: 接收到的数据长度
=0: 对方已经断开连接
<0: 读取错误
@note 当flags没有设置MSG_DONTWAIT,该函数会阻塞,直到有数据或者读取超时
*/
int recvfrom
(int sockfd, void *buf, size_t len, int flags,struct openat_sockaddr *src_addr,openat_socklen_t *addrlen)
2.2、编写主程序
主程序负责注册网络回调函数,以及创建一个消息处理函数。
1 2 3 4 5 6 7 8
| iot_os_sleep(10000);
iot_network_set_cb(NetWorkCb);
TestTask_HANDLE = iot_os_create_task(TestTask, NULL, 2048, 10, OPENAT_OS_CREATE_DEFAULT, "TestTask"); return 0;
|
2.3、编写网络回调函数
消息回调函数负责通知系统消息,最好不要在其中处理复杂的动作。我这里将系统消息转存到全局变量中,然后再任务中定时查询并处理其他的逻辑。
1 2 3 4
| static void NetWorkCb(E_OPENAT_NETWORK_STATE state) { NetWorkCbMessage = state; }
|
2.4、编写消息处理任务
在消息处理函数中,定时查询全局变量转存的网络状态,进行相应的处理。网络正常后调用UdpInit
建立套接字。执行完成后进行任务自毁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| bool NetLink = FALSE; while (NetLink == FALSE) { T_OPENAT_NETWORK_CONNECT networkparam = {0}; switch (NetWorkCbMessage) { case OPENAT_NETWORK_DISCONNECT: iot_debug_print("[socket] OPENAT_NETWORK_DISCONNECT"); iot_os_sleep(10000); break; case OPENAT_NETWORK_READY: iot_debug_print("[socket] OPENAT_NETWORK_READY"); memcpy(networkparam.apn, "CMNET", strlen("CMNET")); iot_network_connect(&networkparam); iot_os_sleep(500); break; case OPENAT_NETWORK_LINKED: iot_debug_print("[socket] OPENAT_NETWORK_LINKED"); NetLink = TRUE; break; } } if (NetLink == TRUE) { UdpInit(); } iot_os_delete_task(TestTask_HANDLE);
|
2.5、编写UdpInit
这是一个子程序,网络连接正常后在消息处理函数中被调用,只会执行一次。首先创建一个套接字,判断创建是否正常。若创建套接字正常,继续创建两个任务,一个负责接收数据,一个负责发送数据。
1 2 3 4 5 6 7 8 9 10 11 12 13
| static void UdpInit() { socketfd = socket(OPENAT_AF_INET, OPENAT_SOCK_DGRAM, 0); while (socketfd < 0) { iot_debug_print("[socket] create udp socket error"); iot_os_sleep(3000); } iot_debug_print("[socket] udp connect success"); iot_os_create_task(SentTask, NULL, 2048, 10, OPENAT_OS_CREATE_DEFAULT, "SentTask"); iot_os_create_task(RecvTask, NULL, 2048, 10, OPENAT_OS_CREATE_DEFAULT, "RecvTask"); }
|
2.6、编写发送任务
在发送任务中,定时对建立的套接字做循环发送字符串的动作,并进行相应的次数标记。要注意的是,发送的时候需要带上远端设备网络连接结构体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| struct openat_sockaddr_in udp_remote_addr = {0}; udp_remote_addr.sin_family = OPENAT_AF_INET; udp_remote_addr.sin_port = htons((unsigned short)UDP_REMOTE_PORT); inet_aton(UDP_REMOTE_IP, &udp_remote_addr.sin_addr);
uint8 num = 0; int len = 0; char data[512] = {0}; while (1) { if (socketfd >= 0) { len = sprintf(data, "RDA8910 Sent:%d", num); data[len] = '\0'; iot_debug_print(data); if (len > 0) { len = sendto(socketfd, data, len + 1, 0, (const struct openat_sockaddr *)&udp_remote_addr, sizeof(struct openat_sockaddr)); if (len < 0) { iot_debug_print("[socket] udp send data False"); } else { iot_debug_print("[socket] udp send data Len = %d", len); num += 1; } } } iot_os_sleep(3000); }
|
2.7、编写接收任务
在接收任务中,将接收的数据打印在日志中显示,recvfrom函数会陷入阻塞状态,直到接收到数据。程序中提供的测试服务端自带回环功能,会将接收的的数据原封不动返回。所以我们接收到的数据就是自己发送的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| struct openat_sockaddr_in udp_remote_addr = {0}; openat_socklen_t udp_remote_len = 0; int len = 0; unsigned char data[512] = {0}; while (1) { if (socketfd >= 0) { len = recvfrom(socketfd, data, sizeof(data), 0, (struct openat_sockaddr *)&udp_remote_addr, &udp_remote_len); if (len < 0) { iot_debug_print("[socket] udp Recv data False"); } else { iot_debug_print("[socket] udp Recv from ip:%s,port:%d", inet_ntoa(udp_remote_addr.sin_addr),ntohs(udp_remote_addr.sin_port)); iot_debug_print("[socket] udp Recv data result = %s", data); } } }
|
recvfrom函数接收到数据的时候会将远端连接结构体数据,写在传入的空结构体内,但是我们这个好像没什么用,不知道是不是我用错了,打印出来的远端ip的端口都是0。我看这函数的说明用的应该是没错的。
三、编译并下载程序
完整代码在这,自取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
#include "string.h" #include "cs_types.h"
#include "osi_log.h" #include "osi_api.h"
#include "am_openat.h" #include "am_openat_vat.h" #include "am_openat_common.h"
#include "iot_debug.h" #include "iot_uart.h" #include "iot_os.h" #include "iot_gpio.h" #include "iot_pmd.h" #include "iot_adc.h" #include "iot_vat.h" #include "iot_network.h" #include "iot_socket.h"
#define UDP_REMOTE_IP "121.40.170.41" #define UDP_REMOTE_PORT 12414
HANDLE TestTask_HANDLE = NULL; uint8 NetWorkCbMessage = 0; int socketfd = -1;
static void SentTask(void *param) {
struct openat_sockaddr_in udp_remote_addr = {0}; udp_remote_addr.sin_family = OPENAT_AF_INET; udp_remote_addr.sin_port = htons((unsigned short)UDP_REMOTE_PORT); inet_aton(UDP_REMOTE_IP, &udp_remote_addr.sin_addr);
uint8 num = 0; int len = 0; char data[512] = {0}; while (1) { if (socketfd >= 0) { len = sprintf(data, "RDA8910 Sent:%d", num); data[len] = '\0'; iot_debug_print(data); if (len > 0) { len = sendto(socketfd, data, len + 1, 0, (const struct openat_sockaddr *)&udp_remote_addr, sizeof(struct openat_sockaddr)); if (len < 0) { iot_debug_print("[socket] udp send data False"); } else { iot_debug_print("[socket] udp send data Len = %d", len); num += 1; } } } iot_os_sleep(3000); } }
static void RecvTask(void *param) { struct openat_sockaddr_in udp_remote_addr = {0}; openat_socklen_t udp_remote_len = 0; int len = 0; unsigned char data[512] = {0}; while (1) { if (socketfd >= 0) { len = recvfrom(socketfd, data, sizeof(data), 0, (struct openat_sockaddr *)&udp_remote_addr, &udp_remote_len); if (len < 0) { iot_debug_print("[socket] udp Recv data False"); } else { iot_debug_print("[socket] udp Recv from ip:%s,port:%d", inet_ntoa(udp_remote_addr.sin_addr),ntohs(udp_remote_addr.sin_port)); iot_debug_print("[socket] udp Recv data result = %s", data); } } } } static void UdpInit() { socketfd = socket(OPENAT_AF_INET, OPENAT_SOCK_DGRAM, 0); while (socketfd < 0) { iot_debug_print("[socket] create udp socket error"); iot_os_sleep(3000); } iot_debug_print("[socket] udp connect success"); iot_os_create_task(SentTask, NULL, 2048, 10, OPENAT_OS_CREATE_DEFAULT, "SentTask"); iot_os_create_task(RecvTask, NULL, 2048, 10, OPENAT_OS_CREATE_DEFAULT, "RecvTask"); } static void TestTask(void *param) { bool NetLink = FALSE; while (NetLink == FALSE) { T_OPENAT_NETWORK_CONNECT networkparam = {0}; switch (NetWorkCbMessage) { case OPENAT_NETWORK_DISCONNECT: iot_debug_print("[socket] OPENAT_NETWORK_DISCONNECT"); iot_os_sleep(10000); break; case OPENAT_NETWORK_READY: iot_debug_print("[socket] OPENAT_NETWORK_READY"); memcpy(networkparam.apn, "CMNET", strlen("CMNET")); iot_network_connect(&networkparam); iot_os_sleep(500); break; case OPENAT_NETWORK_LINKED: iot_debug_print("[socket] OPENAT_NETWORK_LINKED"); NetLink = TRUE; break; } } if (NetLink == TRUE) { UdpInit(); } iot_os_delete_task(TestTask_HANDLE); } static void NetWorkCb(E_OPENAT_NETWORK_STATE state) { NetWorkCbMessage = state; }
int appimg_enter(void *param) { iot_os_sleep(10000); iot_network_set_cb(NetWorkCb); TestTask_HANDLE = iot_os_create_task(TestTask, NULL, 2048, 10, OPENAT_OS_CREATE_DEFAULT, "TestTask"); return 0; }
void appimg_exit(void) { OSI_LOGI(0, "application image exit"); }
|
查看输出,发现接收到的数据和发送的数据一致,唯一的问题就是recvfrom获取不到远端的地址。
不会下载的点击这里,进去查看我的RDA8910 CSDK二次开发入门教程
专题第一篇博文1、RDA8910CSDK二次开发:环境搭建
里面讲了怎么下载
这里只是我的学习笔记,拿出来给大家分享,欢迎大家批评指正,本篇教程到此结束