导读 | 这次使用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?<errno.h> #include?<stdio.h> #include?<string.h> #include?<stddef.h> #include?<unistd.h> ? #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,已连接热点=1 static?int?g_connected?=?0; ? //?输出连接信息字符串 //?打印内容样例-->?bssid:?38:47:BC:49:01:FA,?rssi:?0,?connState:?0,?reason:?0,?ssid:?MyMobile void?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显示信息 ????/* ????server?: ??????server_id?:?192.168.43.1 ??????mask?:?255.255.255.0,?1 ??????gw?:?192.168.43.1 ??????T0?:?3600 ??????T1?:?1800 ??????T2?:?3150 ????clients?<1>?: ??????mac_idx?mac?????????????addr????????????state???lease???tries???rto ??????0???????b4c9b9af69f8????192.168.43.56???10??????0???????1???????2 ????*/ ????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);