#include // 标准输入输出 #include // POSIX标准接口 #include // 错误码 #include // 字符串处理(操作字符数组) #include "lwip/sockets.h" // lwIP TCP/IP协议栈:Socket API #include "ohos_init.h" // 用于初始化服务(services)和功能(features) #include "cmsis_os2.h" // CMSIS-RTOS API V2 #include "oled_ssd1306.h" #include "json_parser.h" // JSON解析器 #include "wifi_connecter.h" // WiFi连接器 extern int control_flag ; // 要发送的数据 static char request[] = "connecting"; // 要接收的数据 char response[128] = ""; // 全局变量存储解析后的命令 JsonCommand g_current_command = {0}; // 发送IP地址到服务器 void SendIpToServer(const char *host, unsigned short port) { char ip_buffer[32] = {0}; char message_buffer[128] = {0}; // 获取本机IP地址 if (GetLocalIpAddress(ip_buffer, sizeof(ip_buffer)) != 0) { printf("Failed to get local IP address\r\n"); return; } // 创建IP消息 if (CreateIpMessage(message_buffer, sizeof(message_buffer), ip_buffer) != 0) { printf("Failed to create IP message\r\n"); return; } // 创建UDP socket int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { printf("Failed to create socket\r\n"); return; } struct sockaddr_in toAddr = {0}; toAddr.sin_family = AF_INET; toAddr.sin_port = htons(port); if (inet_pton(AF_INET, host, &toAddr.sin_addr) <= 0) { printf("inet_pton failed!\r\n"); lwip_close(sockfd); return; } // 发送IP地址消息 ssize_t retval = sendto(sockfd, message_buffer, strlen(message_buffer), 0, (struct sockaddr *)&toAddr, sizeof(toAddr)); if (retval < 0) { printf("Failed to send IP message!\r\n"); } else { printf("IP message sent: %s\r\n", message_buffer); } lwip_close(sockfd); } /// @brief UDP客户端测试函数 /// @param host UDP服务端IP地址 /// @param port UDP服务端端口 void UdpClientTest(const char *host, unsigned short port) { // 用于接收Socket API接口返回值 ssize_t retval = 0; // 创建一个UDP Socket,返回值为文件描述符 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { printf("创建socket失败!\r\n"); return; } // 用于设置服务端的地址信息 struct sockaddr_in toAddr = {0}; // 用于设置本地绑定的地址信息 struct sockaddr_in localAddr = {0}; // 使用IPv4协议 toAddr.sin_family = AF_INET; localAddr.sin_family = AF_INET; // 端口号,从主机字节序转为网络字节序 toAddr.sin_port = htons(port); localAddr.sin_port = htons(port); // 绑定到相同端口接收数据 localAddr.sin_addr.s_addr = INADDR_ANY; // 绑定到所有可用的网络接口 // 将服务端IP地址从"点分十进制"字符串,转化为标准格式(32位整数) if (inet_pton(AF_INET, host, &toAddr.sin_addr) <= 0) { // 转化失败 printf("inet_pton failed!\r\n"); // 输出日志 lwip_close(sockfd); return; } // 绑定本地端口,这样才能接收数据 printf("绑定本地端口 %d...\r\n", port); if (bind(sockfd, (struct sockaddr *)&localAddr, sizeof(localAddr)) < 0) { printf("绑定端口失败!错误: %d\r\n", errno); lwip_close(sockfd); return; } printf("端口绑定成功!\r\n"); // 发送设备IP地址到服务器 SendIpToServer(sockfd, &toAddr); // 发送数据 // UDP socket是 "无连接的",因此每次发送都必须先指定目标主机和端口,主机可以是多播地址 // 发送数据的时候,使用本地随机端口N // // 参数: // s:socket文件描述符 // dataptr:要发送的数据 // size:要发送的数据的长度,最大65332字节 // flags:消息传输标志位 // to:目标的地址信息 // tolen:目标的地址信息长度 // // 返回值: // 发送的字节数,如果出错,返回-1 printf("发送连接消息到服务器...\r\n"); retval = sendto(sockfd, request, sizeof(request), 0, (struct sockaddr *)&toAddr, sizeof(toAddr)); // 检查接口返回值,小于0表示发送失败 if (retval < 0) { // 发送失败 printf("发送消息失败!错误: %d\r\n", errno); // 输出日志 lwip_close(sockfd); return; } // 发送成功 // 输出日志 printf("发送UDP消息成功: {%s} %ld 字节\r\n", request, retval); // 显示等待接收状态 printf("开始监听UDP消息,等待来自 %s 的数据...\r\n", host); // 用于记录发送方的地址信息(IP地址和端口号) struct sockaddr_in fromAddr = {0}; // 用于记录发送方的地址信息长度 socklen_t fromLen = sizeof(fromAddr); // 在本地随机端口N上面接收数据 // UDP socket是 “无连接的”,因此每次接收时并不知道消息来自何处,通过fromAddr参数可以得到发送方的信息(主机、端口号) // device\hisilicon\hispark_pegasus\sdk_liteos\third_party\lwip_sack\include\lwip\sockets.h -> lwip_recvfrom // // 参数: // s:socket文件描述符 // buffer:接收数据的缓冲区的地址 // length:接收数据的缓冲区的长度 // flags:消息接收标志位 // address:发送方的地址信息 // address_len:发送方的地址信息长度 // // 返回值: // 接收的字节数,如果出错,返回-1 while (1) { // 清空接收缓冲区 memset(response, 0, sizeof(response)); retval = lwip_recvfrom(sockfd, response, sizeof(response) - 1, 0, (struct sockaddr *)&fromAddr, &fromLen); // 检查接口返回值,小于0表示接收失败 if (retval <= 0) { // 接收失败,或者收到0长度的数据(忽略掉) printf("recvfrom failed or abort, %ld, %d!\r\n", retval, errno); // 输出日志 continue; // 继续接收,不要关闭socket } // 接收成功 // 末尾添加字符串结束符'\0',以便后续的字符串操作 response[retval] = '\0'; // 输出日志 - 显示所有收到的消息 printf("=== 收到UDP消息 ===\r\n"); printf("消息内容: {%s}\r\n", response); printf("消息长度: %ld 字节\r\n", retval); printf("发送方IP: %s\r\n", inet_ntoa(fromAddr.sin_addr)); printf("发送方端口: %d\r\n", ntohs(fromAddr.sin_port)); printf("==================\r\n"); // 尝试解析JSON命令 JsonCommand temp_cmd = {0}; if (ParseJsonCommand(response, &temp_cmd) == 0) { printf("JSON解析成功: cmd=%d, text=%s\r\n", temp_cmd.cmd, temp_cmd.text); // 保存解析的命令 g_current_command = temp_cmd; // 根据命令类型设置控制标志 if (temp_cmd.cmd >= 1 && temp_cmd.cmd <= 4) { control_flag = temp_cmd.cmd; printf("设置控制标志为: %d\r\n", control_flag); } else { printf("无效的命令类型: %d\r\n", temp_cmd.cmd); } } else { printf("JSON解析失败,尝试旧格式命令\r\n"); // 兼容旧的字符串命令格式 if(strlen(response) > 10) { control_flag = 2; g_current_command.cmd = 0; // 标记为旧命令 printf("设置为复杂字符串命令,控制标志: %d\r\n", control_flag); } else if(strcmp(response,"T:on") == 0) { control_flag = 1; g_current_command.cmd = 0; // 标记为旧命令 printf("设置为开启命令,控制标志: %d\r\n", control_flag); } else if(strcmp(response,"T:off") == 0) { control_flag = 0; g_current_command.cmd = 0; // 标记为旧命令 printf("设置为关闭命令,控制标志: %d\r\n", control_flag); } else { printf("未识别的命令格式: %s\r\n", response); } } sleep(1); } // 关闭socket lwip_close(sockfd); }