Files
License_plate_recognition/udp_client_test.c
2025-10-18 16:05:39 +08:00

243 lines
8.5 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdio.h> // 标准输入输出
#include <unistd.h> // POSIX标准接口
#include <errno.h> // 错误码
#include <string.h> // 字符串处理(操作字符数组)
#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
//
// 参数:
// ssocket文件描述符
// 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
//
// 参数:
// ssocket文件描述符
// 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);
}