这次使用Hi3861来完成Wifi热点的连接,并启动TCP SocketServer,接收消息并将消息反馈TcpCLient。
一、连接Wifi热点
主要做法是启动开发板Wifi,然后设置热点和密码等配置信息,再连接热点。
1、先定义两个Wifi监听器,一个连接改变、一个状态改变,并注册监听器。
其中重要的是OnWifiConnectionChanged连接状态事件处理函数。该函数会在连接成功后设置全局变量g_connected=1,代表已经连接成功。
WifiEvent eventListener={
.OnWifiConnectionChanged=OnWifiConnectionChanged,
.OnWifiScanStateChanged=OnWifiScanStateChanged
};
WifiErrorCode errCode=RegisterWifiEvent(&eventListener);
void OnWifiConnectionChanged(int state, WifiLinkedInfo* info) {
if (!info) return;
if (state==WIFI_STATE_AVALIABLE) {
g_connected=1;
} else {
g_connected=0;
}
}
2、启动Wifi
EnableWifi();
3、设置Wifi热点信息,并返回NetworkId
WifiDeviceConfig apConfig={};
strcpy(apConfig.ssid, "MyWifi");
strcpy(apConfig.preSharedKey, "12345678");
apConfig.securityType=WIFI_SEC_TYPE_PSK;
int netId=-1;
AddDeviceConfig(&apConfig, &netId);
4、连接热点,注意此时的g_connected变量,true代表已连接,false代表未连接。
这个状态在事件处理函数中设置。未连接成功时,系统会循环等待,知道事件设置该值。
ConnectTo(netId);
while (!g_connected) {
osDelay(10);
}
二、进行联网,找到wlan0的network interface,然后启动DHCP客户端,获取IP地址。
struct netif* iface=netifapi_netif_find("wlan0");
if (iface) {
err_t ret=netifapi_dhcp_start(iface);
osDelay(300);
}
三、启动TcpSocketServer,并收发消息
1、创建SocketServer,设置服务端口,并启动监听
int sockfd=socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serverAddr={0};
serverAddr.sin_family=AF_INET;
serverAddr.sin_port=htons(port);
serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);
bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
int backlog=1;
listen(sockfd, backlog)
2、客户端连接。接收客户端消息并发送回去。注意连接后,会创建一个新的Socket File Description。
int connfd=-1;
connfd=accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen);
recv(connfd, request, sizeof(request), 0);
send(connfd, request, strlen(request), 0);
3、关闭TcpSocketServer
lwip_close(connfd);
lwip_close(socketfd);
四、联网结束,关闭DHCP客户端,断开Wifi,移除热点的配置信息,禁用Wifi。
err_t ret=netifapi_dhcp_stop(iface);
Disconnect();
RemoveDevice(netId);
DisableWifi();
五、测试情况如下:
1、启动开发板,连接Wifi热点,启动TcpServer
2、TcpClient(网络调试助手)连接开发板的TcpServer(HiBurn)。
3、TcpClient输入数据并发送,TcpServer接收后再发送回TcpClient。
六、全部源代码,我都注释了,希望大家能够有所参考。
#include
#include
#include
#include
#include
#include "ohos_init.h"#include "cmsis_os2.h"#include "wifi_device.h" #include "lwip/netifapi.h"#include "lwip/api_shell.h"#include "lwip/sockets.h" // 接收、发送的数据static char request[128]="";// 未连接热点=0,已连接热点=1static int g_connected=0; // 输出连接信息字符串// 打印内容样例--> bssid: 38:47:BC:49:01:FA, rssi: 0, connState: 0, reason: 0, ssid: MyMobilevoid PrintLinkedInfo(WifiLinkedInfo* info) { if (!info) return; static char macAddress[32]={0}; unsigned char* mac=info->bssid; snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); printf("bssid: %s, rssi: %d, connState: %d, reason: %d, ssid: %srn", macAddress, info->rssi, info->connState, info->disconnectedReason, info->ssid);} // 连接状态改变事件处理void OnWifiConnectionChanged(int state, WifiLinkedInfo* info) { if (!info) return; // 输出类似内容:OnWifiConnectionChanged 31, state=1, info=printf("%s %d, state=%d, info=rn", __FUNCTION__, __LINE__, state); PrintLinkedInfo(info); // 根据连接状态设置g_connected if (state==WIFI_STATE_AVALIABLE) { g_connected=1; } else { g_connected=0; }} // 扫描状态改变事件处理void OnWifiScanStateChanged(int state, int size) { printf("%s %d, state=%X, size=%drn", __FUNCTION__, __LINE__, state, size);} void DisconnectTcpSocket(int connfd) { sleep(1); printf("do_disconnect...rn"); lwip_close(connfd); sleep(1); // for debug} void CloseTcpSocket(int socketfd) { printf("do_cleanup...rn"); lwip_close(socketfd);} static void TcpServerHandler(void) { ssize_t retval=0; unsigned short port=9118; // 创建一个通信的Socket,并返回一个Socket文件描述符。第一个参数IpV4,第二个参数SOCK_STREAM类型,第三个指用到的协议 int sockfd=socket(AF_INET, SOCK_STREAM, 0); // 客户端地址和地址长度 struct sockaddr_in clientAddr={0}; socklen_t clientAddrLen=sizeof(clientAddr); // 服务端地址 struct sockaddr_in serverAddr={0}; serverAddr.sin_family=AF_INET; // htons是将整型变量从主机字节顺序转变成网络字节顺序,就是整数在地址空间存储方式变为高位字节存放在内存的低地址处 serverAddr.sin_port=htons(port); // 监听本机的所有IP地址,INADDR_ANY=0x0 // 将主机数转换成无符号长整型的网络字节顺序 serverAddr.sin_addr.s_addr=htonl(INADDR_ANY); // 服务端绑定端口 retval=bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); if (retval < 0) { printf("bind failed, %ld!rn", retval); CloseTcpSocket(sockfd); return; } printf("bind to port %d success!rn", port); // 开始监听,backlog指Pending连接队列增长到的最大长度。队列满了,再有新连接请求到达,则客户端ECONNREFUSED错误。如果支持重传,则请求忽略。 int backlog=1; retval=listen(sockfd, backlog); if (retval < 0) { printf("listen failed!rn"); CloseTcpSocket(sockfd); return; } printf("listen with %d backlog success!rn", backlog); int outerFlag=1; while (outerFlag) { // 接受客户端连接,成功会返回一个表示连接的 socket。clientAddr参数将会携带客户端主机和端口信息;失败返回 -1 // 从Pending连接队列中获取第一个连接,根据sockfd的socket协议、地址族等内容创建一个新的socket文件描述,并返回。 // 此后的 收、发 都在 表示连接的 socket 上进行;之后 sockfd 依然可以继续接受其他客户端的连接, // UNIX系统上经典的并发模型是“每个连接一个进程”——创建子进程处理连接,父进程继续接受其他客户端的连接 // 鸿蒙liteos-a内核之上,可以使用UNIX的“每个连接一个进程”的并发模型liteos-m内核之上,可以使用“每个连接一个线程”的并发模型 int connfd=-1; connfd=accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen); if (connfd < 0) { printf("accept failed, %d, %drn", connfd, errno); CloseTcpSocket(sockfd); outerFlag=0; } printf("accept success, connfd=%d !rn", connfd); // inet_ntoa:网络地址转换成“.”点隔的字符串格式。ntohs:16位数由网络字节顺序转换为主机字节顺序。 printf("client addr info: host=%s, port=%drn", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); int innerFlag=1; // 接收消息,然后发送回去 while (innerFlag) { // 后续 收、发 都在 表示连接的 socket 上进行; // 在新的Socket文件描述上接收信息. retval=recv(connfd, request, sizeof(request), 0); if (retval < 0) { printf("recv request failed, %ld!rn", retval); innerFlag=0; } else if (retval==0) { // 对方主动断开连接 printf("client disconnected!rn"); innerFlag=0; } else { printf("recv request{%s} from client done!rn", request); // 发送数据 retval=send(connfd, request, strlen(request), 0); if (retval <=0) { printf("send response failed, %ld!rn", retval); innerFlag=0; } printf("send response{%s} to client done!rn", request); // 清空缓冲区 memset(&request, 0, sizeof(request)); } DisconnectTcpSocket(connfd); outerFlag=0; } CloseTcpSocket(sockfd);} static void TcpServerTask(void *arg) { (void)arg; // 先定义两个Wifi监听器,一个连接改变、一个状态改变 WifiEvent eventListener={ .OnWifiConnectionChanged=OnWifiConnectionChanged, .OnWifiScanStateChanged=OnWifiScanStateChanged }; // 等待10个系统Ticks。每个ticks多少个us,计算方式=1000 * 1000 / osKernelGetTickFreq() osDelay(10); // 注册监听器 WifiErrorCode errCode=RegisterWifiEvent(&eventListener); printf("RegisterWifiEvent: %drn", errCode); // 设置Wifi热点信息 WifiDeviceConfig apConfig={}; strcpy(apConfig.ssid, "MyMobile"); strcpy(apConfig.preSharedKey, "12345678"); apConfig.securityType=WIFI_SEC_TYPE_PSK; int netId=-1; // 启用Wifi errCode=EnableWifi(); printf("EnableWifi: %drn", errCode); osDelay(10); // 设置Wifi热点配置信息,返回生成的网络Id-netId。 errCode=AddDeviceConfig(&apConfig, &netId); printf("AddDeviceConfig: %drn", errCode); // 根据网络Id连接到Wifi热点 g_connected=0; errCode=ConnectTo(netId); printf("ConnectTo(%d): %drn", netId, errCode); // 未连接完成,则一直等待。g_connected状态会在事件中设置。 while (!g_connected) { osDelay(10); } printf("g_connected: %drn", g_connected); osDelay(50); // 联网业务开始,找到netifname=wlan0的netif。 struct netif* iface=netifapi_netif_find("wlan0"); if (iface) { // 启动DHCP客户端,获取IP地址 err_t ret=netifapi_dhcp_start(iface); printf("netifapi_dhcp_start: %drn", ret); // 等待DHCP服务器反馈给予地址 osDelay(300); // 执行线程安全的网络方法,第二个参数是voidFunc,第三个参数是errFunc。如果没有errFunc,那么就执行voidFunc。 // netifapi_dhcp_start/netifapi_dhcp_stop等都是调用的netifapi_netif_common方法。 // dhcp_clients_info_show显示信息 ret=netifapi_netif_common(iface, dhcp_clients_info_show, NULL); printf("netifapi_netif_common: %drn", ret); } TcpServerHandler(); // 联网业务结束,断开DHCP err_t ret=netifapi_dhcp_stop(iface); printf("netifapi_dhcp_stop: %drn", ret); // 断开Wifi热点连接 Disconnect(); // 移除Wifi热点的配置 RemoveDevice(netId); // 关闭Wifi errCode=DisableWifi(); printf("DisableWifi: %drn", errCode); osDelay(200);} static void TcpServerEntry(void) { osThreadAttr_t attr; attr.name="TcpServerTask"; attr.attr_bits=0U; attr.cb_mem=NULL; attr.cb_size=0U; attr.stack_mem=NULL; attr.stack_size=10240; attr.priority=osPriorityNormal; if (osThreadNew((osThreadFunc_t)TcpServerTask, NULL, &attr)==NULL) { printf("SunLaoTest-Fail Create"); }} APP_FEATURE_INIT(TcpServerEntry);
本文地址:https://www.linuxprobe.com/hi3861-wifi-socketserver.html