| 导读 | 这次使用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);
那晚越女说我?