用户数据报协议(UDP):UDP(用户数据报协议)是一个简单的面向数据报的传输层协议。提供的是非面向连接的、不可靠的数据流传输。UDP不提供可靠性,也不提供报文到达确认、排序以及流量控制等功能。
想了解更多关于开源的内容,请访问:
51CTO 开源基础软件社区
https://ost.51cto.com
一、前言
在net_demo.h文件里,testFun是什么?它是怎么选择客户端还是服务端再跳转到tcp_client_test.c文件执行TcpClientTest()函数的呢?
搞不懂别人高深的代码,于是后面就仿照官方代码,自己写了一份简单的代码,并且学习另一种传输协议UDP。
二、UDP简介
1、定义
用户数据报协议(UDP):UDP(用户数据报协议)是一个简单的面向数据报的传输层协议。提供的是非面向连接的、不可靠的数据流传输。UDP不提供可靠性,也不提供报文到达确认、排序以及流量控制等功能。它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。因此报文可能会丢失、重复以及乱序等。但由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。
2、复习一下TCP
“面向连接的TCP”就是在正式通信前必须要与对方建立起连接。TCP协议是一种可靠的、一对一的、面向有连接的通信协议。
3、UDP/TCP区别
TCP和UDP最大的区别就是:TCP是面向连接的,UDP是无连接的。TCP协议和UDP协议各有所长、各有所短,适用于不同要求的通信环境。TCP协议和UDP协议之间的差别如下表所示。
在实际的使用中,TCP主要应用于文件传输精确性相对要求较高且不是很紧急的情景,比如电子邮件、远程登录等。有时在这些应用场景下即使丢失一两个字节也会造成不可挽回的错误,所以这些场景中一般都使用TCP传输协议。由于UDP可以提高传输效率,所以UDP被广泛应用于数据量大且精确性要求不高的数据传输,比如我们平常在网站上观看视频或者听音乐的时候应用的基本上都是UDP传输协议。
开发流程图:
UDP:
TCP:
三、代码
1、把连接WiFi的代码搬过来
在net_params.h文件里配置WiFi:
#ifndef PARAM_HOTSPOT_SSID
#define PARAM_HOTSPOT_SSID "Fsr" // your AP SSID
#endif
#ifndef PARAM_HOTSPOT_PSK
#define PARAM_HOTSPOT_PSK "12345678" // your AP PSK
#endif
连接WiFi:
//连接wifi
WifiDeviceConfig config = {0};
// 准备AP的配置参数
strcpy(config.ssid, PARAM_HOTSPOT_SSID);
strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);
config.securityType = PARAM_HOTSPOT_TYPE;
osDelay(10);
//开始连接
int netId = ConnectToHotspot(&config);
记得在udp文件夹里的BUILD.gn编译WiFi的.c文件:
2、简单的UDP
// 1.创建udp
int sock_fd;
int ret;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0); //SOCK_DGRAM:UDP类型的socket
if (sock_fd < 0)
{
perror("sock_fd create error\r\n");
return;
}
// 2.配置地址
struct sockaddr_in send_addr;
socklen_t send_addr_len = sizeof(send_addr);
//内存初始化
memset((void *)&send_addr, 0, send_addr_len);
send_addr.sin_family = AF_INET;
send_addr.sin_addr.s_addr = inet_addr("192.168.11.41"); // 是将一个点分十进制的IP转换成一个长整数型数(u_long类型)
send_addr.sin_port = htons(1234); // 端口号,从主机字节序转为网络字节序
// 3.配置发送消息
char *msg = "hello world";
while (1)
{
//4.发送
// UDP socket 是 “无连接的” ,因此每次发送都必须先指定目标主机和端口,主机可以是多播地址
ret = sendto(sock_fd, msg, strlen(msg), 0, (struct sockaddr *)&send_addr, send_addr_len);
printf("send UDP message {%s}done!\r\n", msg);
usleep(1 * 1000 * 1000);
// 5.接收
struct sockaddr_in fromAddr = {0};
socklen_t fromLen = sizeof(fromAddr);
// UDP socket 是 “无连接的” ,因此每次接收时前并不知道消息来自何处,通过 fromAddr 参数可以得到发送方的信息(主机、端口号)
ret = recvfrom(sock_fd, &response, sizeof(response), 0, (struct sockaddr *)&fromAddr, &fromLen);
if (ret <= 0)
{
printf("recvfrom failed or abort, %ld!\r\n", ret);
}
response[ret] = '\0';
printf("recv UDP message {%s} %ld done!\r\n", response, ret);
printf("peer info: ipaddr = %s, port = %d\r\n", inet_ntoa(fromAddr.sin_addr), ntohs(fromAddr.sin_port)); //将网络地址转换成“.”点隔的字符串格式。将一个16位数由网络字节顺序转换为主机字节顺序
}
代码中主要的函数与TCP一样都是用socket,已在上文,【FFH】学习设备开发之Hi3861-TCPclient-开关灯解释。
两者开发过程的区别:
① 创建socket的类型type=SOCK_STREAM(TCP)/SOCK_DGRAM(UDP)。
int sockfd = socket(AF_INET, type, 0);
②TCP需要多一步与主机连接,而UDP不需要。
3、代码运行结果
netcat,-u代表主机使用UDP协议传输,-l 开启监听,-p指定端口。
四、UDP广播
因为UDP是无连接的,并且一对多发送消息,自然而然就具有广播消息的功能。
下面给出主要代码:
// 1.创建udp
int sock_fd;
int ret;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
{
perror("sock_fd create error\r\n");
return;
}
// 2.设置广播模式
int yes = 1;
ret = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&yes, sizeof(yes));
// SOL_SOCKET表示给当前的socketfd,赋予SO_BROADCAST广播权限
if (ret == -1)
{
perror("setsockopt error\r\n");
return;
}
// 3.配置广播地址
struct sockaddr_in broadcast_addr;
socklen_t broadcast_addr_len = sizeof(broadcast_addr);
//初始化地址内存
memset((void *)&broadcast_addr, 0, broadcast_addr_len);
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); //INADDR_BROADCAST:要发送给所有主机的地址
broadcast_addr.sin_port = htons(1234);
char *msg = "hello ";
while (1)
{
ret = sendto(sock_fd, msg, strlen(msg), 0, (struct sockaddr *)&broadcast_addr, broadcast_addr_len);
usleep(1 * 1000 * 1000);
}
设置广播模式的函数:
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&yes, sizeof(yes));
int setsockopt( int socket, int level, int option_name,const void *option_value, size_t ,ption_len);
第一个参数socket是套接字描述符。
第二个参数level是被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET。
第三个参数option_name指定准备设置的选项,option_name可以有哪些取值,这取决于level。当level取SOL_SOCKET时,option_name可取
SO_DEBUG,打开或关闭调试信息。
SO_REUSEADDR,打开或关闭地址复用功能。 当option_value不等于0时,打开,否则,关闭。
SO_DONTROUTE,打开或关闭路由查找功能。 当option_value不等于0时,打开,否则,关闭。
SO_BROADCAST,允许或禁止发送广播数据。 当option_value不等于0时,允许,否则,禁止。
不需要确定发给哪个特定的主机,所以要设置广播的地址为所有。
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); //INADDR_BROADCAST:要发送给所有主机的地址
想了解更多关于开源的内容,请访问:
51CTO 开源基础软件社区
https://ost.51cto.com。