本案例程序将演示怎么在拓维Niobe WiFi IoT Core开发板上编写一个连接udp服务器的业务程序,实现开发板联网上报数据到服务器。
UDP是用户数据包协议(UDP,User Datagram Protocol), 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。RFC 768 描述了 UDP。
Internet 的传输层有两个主要协议,互为补充。无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的事情。面向连接的是 TCP,该协议几乎做了所有的事情
1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt();* 可选 3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 4、设置对方的IP地址和端口等属性; 5、循环接收/发送数据,用函数recvfrom()/sendto(); 6、关闭网络连接;
1、UDP是无连接的,即发送数据之前不需要建立连接; 2、UDP使用尽最大努力交付,即不保证可靠交付; 3、UDP是面向报文的; 4、UDP支持一对一、一对多、多对一和多对多的交互通信等。
struct sockaddr_in {
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
uint8_t sin_zero[8];
};
描述:
地址和端口信息
参数:
名字 | 描述 |
---|---|
sin_family | 指代协议族,在socket编程中只能是AF_INET |
sin_port | 存储端口号(使用网络字节顺序) |
sin_addr | 存储IP地址,使用in_addr这个数据结构 |
sin_zero | 为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节 |
示例代码如下:
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(_PROT_);
//server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
inet_pton(AF_INET, _SERVER_IP_, &server_addr.sin_addr);
int sin_size = sizeof(struct sockaddr_in);
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
描述:
通用的套接字地址,与sockaddr_in类似
参数
名字 | 描述 |
---|---|
sa_family | 地址族 |
sa_data | 14字节,包含套接字中的目标地址和端口信息 |
int WifiConnect(const char *ssid, const char *psk)
描述: 初始化网络,并连接指定wifi
参数:
名字 | 描述 |
---|---|
ssid | 指定wifi的账户名 |
psk | 指定wifi的密码 |
int socket(int domain, int type, int protocol)
描述:
创建套接字
参数:
名字 | 描述 |
---|---|
domain | 地址族,也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6 |
type | 数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)和SOCK_RAW(原始套接字) |
protocol | 传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议 |
ssize_t recvfrom(int fd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict alen)
描述:
接收数据
参数:
名字 | 描述 |
---|---|
fd | 客户端套接字 |
buf | UDP数据报缓存区(包含所接收的数据) |
len | 缓冲区长度 |
flags | 调用操作方式(一般设置为0) |
addr | 指向发送数据的客户端地址信息的结构体(sockaddr_in需类型转换) |
alen | 指针,指向addr结构体长度值 |
ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t alen)
描述:
发送数据
参数:
名字 | 描述 |
---|---|
fd | 客户端套接字 |
buf | UDP数据报缓存区(包含待发送数据) |
len | UDP数据报的长度 |
flags | 调用操作方式(一般设置为0) |
addr | 指向接收数据的主机地址信息的结构体(sockaddr_in需类型转换) |
alen | addr所指结构体的长度 |
主要代码分析
static void UDPClientTask(void)
{
//连接Wifi
WifiConnect(WIFI_SSID,WIFI_PASSWORD);
//创建socket
int sock_fd;
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
printf("socket is error\r\n");
exit(1);
}
//printf("socket sock_fd=%d \r\n",sock_fd);
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(_PROT_);
//server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
inet_pton(AF_INET, _SERVER_IP_, &server_addr.sin_addr);
int sin_size = sizeof(struct sockaddr_in);
char str[IP_LEN];
inet_ntop(AF_INET, &server_addr.sin_addr, str, sizeof(str));
printf("udp server IP_addr: %s at PORT %d\n",str,ntohs(server_addr.sin_port));
const char sendbufs[3][10]={{"0000"},{"1111"},{"2222"}};
struct sockaddr_in repaddr;
for(int i=0; i<3; i++)
{
if(sendto(sock_fd, sendbufs[i], strlen(sendbufs[i])+1, 0,(struct sockaddr *)&server_addr, (socklen_t )sin_size)==-1)
{
printf("send error\n");
close(sock_fd);
exit(-1);
}
printf("send smg: %s size: %d\n",sendbufs[i],strlen(sendbufs[i]));
//
memset(&repaddr, 0, sizeof(struct sockaddr));
int size = recvfrom(sock_fd, recvbuf, sizeof(recvbuf)-1, 0, (struct sockaddr *)&repaddr, (socklen_t *)&sin_size);
if(size>0)
{
printf("recv msg: %s \n",recvbuf);
}
usleep(500*1000);
}
char send_buf[100]="exit";
printf("send exit\n");
int res = sendto(sock_fd, send_buf, strlen(send_buf)+1, 0,(struct sockaddr *)&server_addr, (socklen_t )sin_size); // 向服务器发送信息
if(res==-1)
{
perror("sendto error\n");
exit(-1);
}
usleep(500*1000);
close(sock_fd);
}
修改udp_client.c
第32行和33行的WiFi热点SSID和密码,改成自己环境中的WiFi热点。
// 默认WiFi名和密码
#define WIFI_SSID "aaa"
#define WIFI_PASSWORD "talkweb1996"
修改udp_client.c
第27行和38行的端口号和ip地址,改成自己创建的服务端ip地址。
// 默认服务端的ip地址和端口号
#define _PROT_ 8800
#define _SERVER_IP_ "192.168.1.114"
修改 applications/app/BUILD.gn
路径中的 BUILD.gn 文件,指定 network_udpclient_demo
参与编译。
# "TW303_Network_mqttclient:network_mqttclient_example",
# "TW402_APP_oled_u8g2:app_oled_u8g2_example",
# "TW304_Network_tcpserver:network_tcpserver_demo",
# "TW304_Network_tcpclient:network_tcpclient_demo",
"TW305_Network_udpclient:network_udpclient_demo",
示例代码编译烧录代码后,按下开发板的RESET按键,通过串口助手查看日志,会打印连接到的Wifi热点信息,以及开启tcp服务端,等待客户端连接
链接wifi成功,并打印IP信息
callback function for wifi connect
WaitConnectResult:wait success[1]s
WiFi connect succeed!
begain to dhcp<-- DHCP state:Inprogress -->
<-- DHCP state:Inprogress -->
<-- DHCP state:Inprogress -->
<-- DHCP state:OK -->
server :
server_id : 192.168.1.1
mask : 255.255.255.0, 1
gw : 192.168.1.1
T0 : 7200
T1 : 3600
T2 : 6300
clients <1> :
mac_idx mac addr state lease tries rto
0 b4c9b9af6afe 192.168.1.118 10 0 1 3
链接tcp服务端并进行信息通信
udp server IP_addr: 192.168.1.112 at PORT 8800
send smg: 0000 size: 4
recv msg: Hello! I'm Talkweb UDP Server!
send smg: 1111 size: 4
recv msg: Hello! I'm Talkweb UDP Server!
send smg: 2222 size: 4
recv msg: Hello! I'm Talkweb UDP Server!
send exit
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。