RDA8910(4GCAT1)CSDK二次开发:9、趁热打铁干脆顺带把UDP通讯也给撸了吧

目录

点击这里查看所有博文

  本系列博客所述资料均来自合宙官方,并不是本人原创(只有博客是自己写的),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 网络字节顺序数据
**/

  • #define 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: 失败
**/

  • #define 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、编写主程序

  主程序负责注册网络回调函数,以及创建一个消息处理函数。

	//系统休眠
	iot_os_sleep(10000);
	//注册网络状态回调函数
	iot_network_set_cb(NetWorkCb);
	//创建一个任务
	//TestTask_HANDLE =
	TestTask_HANDLE = iot_os_create_task(TestTask, NULL, 2048, 10, OPENAT_OS_CREATE_DEFAULT, "TestTask");
	return 0;

2.3、编写网络回调函数

  消息回调函数负责通知系统消息,最好不要在其中处理复杂的动作。我这里将系统消息转存到全局变量中,然后再任务中定时查询并处理其他的逻辑。

static void NetWorkCb(E_OPENAT_NETWORK_STATE state)
{
	NetWorkCbMessage = state;
}

2.4、编写消息处理任务

  在消息处理函数中,定时查询全局变量转存的网络状态,进行相应的处理。网络正常后调用UdpInit建立套接字。执行完成后进行任务自毁。

	bool NetLink = FALSE;
	while (NetLink == FALSE)
	{
		T_OPENAT_NETWORK_CONNECT networkparam = {0};
		switch (NetWorkCbMessage)
		{
		case OPENAT_NETWORK_DISCONNECT: //网络断开 表示GPRS网络不可用澹,无法进行数据连接,有可能可以打电话
			iot_debug_print("[socket] OPENAT_NETWORK_DISCONNECT");
			iot_os_sleep(10000);
			break;
		case OPENAT_NETWORK_READY: //网络已连接 表示GPRS网络可用,可以进行链路激活
			iot_debug_print("[socket] OPENAT_NETWORK_READY");
			memcpy(networkparam.apn, "CMNET", strlen("CMNET"));
			//建立网络连接,实际为pdp激活流程
			iot_network_connect(&networkparam);
			iot_os_sleep(500);
			break;
		case OPENAT_NETWORK_LINKED: //链路已经激活 PDP已经激活,可以通过socket接口建立数据连接
			iot_debug_print("[socket] OPENAT_NETWORK_LINKED");
			NetLink = TRUE;
			break;
		}
	}
	if (NetLink == TRUE)
	{
		UdpInit();
	}
	iot_os_delete_task(TestTask_HANDLE);

2.5、编写UdpInit

  这是一个子程序,网络连接正常后在消息处理函数中被调用,只会执行一次。首先创建一个套接字,判断创建是否正常。若创建套接字正常,继续创建两个任务,一个负责接收数据,一个负责发送数据。

static void UdpInit()
{
	//创建套接字,Udp连接
	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、编写发送任务

  在发送任务中,定时对建立的套接字做循环发送字符串的动作,并进行相应的次数标记。要注意的是,发送的时候需要带上远端设备网络连接结构体。

	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)
			{
				// UDP 发送数据
				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函数会陷入阻塞状态,直到接收到数据。程序中提供的测试服务端自带回环功能,会将接收的的数据原封不动返回。所以我们接收到的数据就是自己发送的数据。

	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)
		{
			// UDP 接受数据
			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
			{
				//发现获取不到远端ip和端口
				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。我看这函数的说明用的应该是没错的。
在这里插入图片描述

三、编译并下载程序

  完整代码在这,自取。

/*
 * @Author: your name
 * @Date: 2020-05-19 14:05:32
 * @LastEditTime: 2020-05-26 21:50:08
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \RDA8910_CSDK\USER\user_main.c
 */

#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"

//Udp Demo
//UDP模式指定IP和端口就可以的
//UDP服务没有客户端和服务器的概念,UDP是对等网络。tcp服务才有服务器和客户端的概念。
//错的人多了就变成对的了

//远端ip和port
#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)
			{
				// UDP 发送数据
				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)
		{
			// UDP 接受数据
			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
			{
				//发现获取不到远端ip和端口
				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()
{
	//创建套接字,Udp连接
	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: //网络断开 表示GPRS网络不可用澹,无法进行数据连接,有可能可以打电话
			iot_debug_print("[socket] OPENAT_NETWORK_DISCONNECT");
			iot_os_sleep(10000);
			break;
		case OPENAT_NETWORK_READY: //网络已连接 表示GPRS网络可用,可以进行链路激活
			iot_debug_print("[socket] OPENAT_NETWORK_READY");
			memcpy(networkparam.apn, "CMNET", strlen("CMNET"));
			//建立网络连接,实际为pdp激活流程
			iot_network_connect(&networkparam);
			iot_os_sleep(500);
			break;
		case OPENAT_NETWORK_LINKED: //链路已经激活 PDP已经激活,可以通过socket接口建立数据连接
			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;
}
//main函数
int appimg_enter(void *param)
{
	//系统休眠
	iot_os_sleep(10000);
	//注册网络状态回调函数
	iot_network_set_cb(NetWorkCb);
	//创建一个任务
	//TestTask_HANDLE =
	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二次开发:环境搭建里面讲了怎么下载
这里只是我的学习笔记,拿出来给大家分享,欢迎大家批评指正,本篇教程到此结束

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

遇雪长安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值