使用鴻蒙Hi3861完成連接WiFi熱點并啟動TCPSocketServ
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz
這次使用Hi3861來完成Wifi熱點的連接,并啟動TCP SocketServer,接收消息并將消息反饋TcpCLient。
一、連接Wifi熱點。主要做法是啟動開發(fā)板Wifi,然后設(shè)置熱點和密碼等配置信息,再連接熱點。
1、先定義兩個Wifi監(jiān)聽器,一個連接改變、一個狀態(tài)改變,并注冊監(jiān)聽器。其中重要的是OnWifiConnectionChanged連接狀態(tài)事件處理函數(shù)。該函數(shù)會在連接成功后設(shè)置全局變量g_connected=1,代表已經(jīng)連接成功。
- 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、設(shè)置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代表未連接。這個狀態(tài)在事件處理函數(shù)中設(shè)置。未連接成功時,系統(tǒng)會循環(huán)等待,知道事件設(shè)置該值。
- ConnectTo(netId);
- while (!g_connected) {
- osDelay(10);
- }
二、進(jìn)行聯(lián)網(wǎng),找到wlan0的network interface,然后啟動DHCP客戶端,獲取IP地址。
- struct netif* iface = netifapi_netif_find("wlan0");
- if (iface) {
- err_t ret = netifapi_dhcp_start(iface);
- osDelay(300);
- }
三、啟動TcpSocketServer,并收發(fā)消息
1、創(chuàng)建SocketServer,設(shè)置服務(wù)端口,并啟動監(jiān)聽
- 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、客戶端連接。接收客戶端消息并發(fā)送回去。注意連接后,會創(chuàng)建一個新的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、關(guān)閉TcpSocketServer
- lwip_close(connfd);
- lwip_close(socketfd);
四、聯(lián)網(wǎng)結(jié)束,關(guān)閉DHCP客戶端,斷開Wifi,移除熱點的配置信息,禁用Wifi。
- err_t ret = netifapi_dhcp_stop(iface);
- Disconnect();
- RemoveDevice(netId);
- DisableWifi();
五、測試情況如下:
1、啟動開發(fā)板,連接Wifi熱點,啟動TcpServer
2、TcpClient(網(wǎng)絡(luò)調(diào)試助手)連接開發(fā)板的TcpServer(HiBurn)。
3、TcpClient輸入數(shù)據(jù)并發(fā)送,TcpServer接收后再發(fā)送回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"
- // 接收、發(fā)送的數(shù)據(jù)
- static char request[128] = "";
- // 未連接熱點=0,已連接熱點=1
- static int g_connected = 0;
- // 輸出連接信息字符串
- // 打印內(nèi)容樣例--> 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);
- }
- // 連接狀態(tài)改變事件處理
- void OnWifiConnectionChanged(int state, WifiLinkedInfo* info) {
- if (!info) return;
- // 輸出類似內(nèi)容:OnWifiConnectionChanged 31, state = 1, info =
- printf("%s %d, state = %d, info = rn", __FUNCTION__, __LINE__, state);
- PrintLinkedInfo(info);
- // 根據(jù)連接狀態(tài)設(shè)置g_connected
- if (state == WIFI_STATE_AVALIABLE) {
- g_connected = 1;
- } else {
- g_connected = 0;
- }
- }
- // 掃描狀態(tài)改變事件處理
- 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;
- // 創(chuàng)建一個通信的Socket,并返回一個Socket文件描述符。第一個參數(shù)IpV4,第二個參數(shù)SOCK_STREAM類型,第三個指用到的協(xié)議
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
- // 客戶端地址和地址長度
- struct sockaddr_in clientAddr = {0};
- socklen_t clientAddrLen = sizeof(clientAddr);
- // 服務(wù)端地址
- struct sockaddr_in serverAddr = {0};
- serverAddr.sin_family = AF_INET;
- // htons是將整型變量從主機字節(jié)順序轉(zhuǎn)變成網(wǎng)絡(luò)字節(jié)順序,就是整數(shù)在地址空間存儲方式變?yōu)楦呶蛔止?jié)存放在內(nèi)存的低地址處
- serverAddr.sin_port = htons(port);
- // 監(jiān)聽本機的所有IP地址,INADDR_ANY=0x0
- // 將主機數(shù)轉(zhuǎn)換成無符號長整型的網(wǎng)絡(luò)字節(jié)順序
- serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
- // 服務(wù)端綁定端口
- 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);
- // 開始監(jiān)聽,backlog指Pending連接隊列增長到的最大長度。隊列滿了,再有新連接請求到達(dá),則客戶端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參數(shù)將會攜帶客戶端主機和端口信息;失敗返回 -1
- // 從Pending連接隊列中獲取第一個連接,根據(jù)sockfd的socket協(xié)議、地址族等內(nèi)容創(chuàng)建一個新的socket文件描述,并返回。
- // 此后的 收、發(fā) 都在 表示連接的 socket 上進(jìn)行;之后 sockfd 依然可以繼續(xù)接受其他客戶端的連接,
- // UNIX系統(tǒng)上經(jīng)典的并發(fā)模型是“每個連接一個進(jìn)程”——創(chuàng)建子進(jìn)程處理連接,父進(jìn)程繼續(xù)接受其他客戶端的連接
- // 鴻蒙liteos-a內(nèi)核之上,可以使用UNIX的“每個連接一個進(jìn)程”的并發(fā)模型liteos-m內(nèi)核之上,可以使用“每個連接一個線程”的并發(fā)模型
- 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:網(wǎng)絡(luò)地址轉(zhuǎn)換成“.”點隔的字符串格式。ntohs:16位數(shù)由網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為主機字節(jié)順序。
- printf("client addr info: host = %s, port = %drn", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
- int innerFlag = 1;
- // 接收消息,然后發(fā)送回去
- while (innerFlag) {
- // 后續(xù) 收、發(fā) 都在 表示連接的 socket 上進(jìn)行;
- // 在新的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);
- // 發(fā)送數(shù)據(jù)
- 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);
- // 清空緩沖區(qū)
- memset(&request, 0, sizeof(request));
- }
- DisconnectTcpSocket(connfd);
- outerFlag = 0;
- }
- CloseTcpSocket(sockfd);
- }
- static void TcpServerTask(void *arg) {
- (void)arg;
- // 先定義兩個Wifi監(jiān)聽器,一個連接改變、一個狀態(tài)改變
- WifiEvent eventListener = {
- .OnWifiConnectionChanged = OnWifiConnectionChanged,
- .OnWifiScanStateChanged = OnWifiScanStateChanged
- };
- // 等待10個系統(tǒng)Ticks。每個ticks多少個us,計算方式= 1000 * 1000 / osKernelGetTickFreq()
- osDelay(10);
- // 注冊監(jiān)聽器
- WifiErrorCode errCode = RegisterWifiEvent(&eventListener);
- printf("RegisterWifiEvent: %drn", errCode);
- // 設(shè)置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);
- // 設(shè)置Wifi熱點配置信息,返回生成的網(wǎng)絡(luò)Id-netId。
- errCode = AddDeviceConfig(&apConfig, &netId);
- printf("AddDeviceConfig: %drn", errCode);
- // 根據(jù)網(wǎng)絡(luò)Id連接到Wifi熱點
- g_connected = 0;
- errCode = ConnectTo(netId);
- printf("ConnectTo(%d): %drn", netId, errCode);
- // 未連接完成,則一直等待。g_connected狀態(tài)會在事件中設(shè)置。
- while (!g_connected) {
- osDelay(10);
- }
- printf("g_connected: %drn", g_connected);
- osDelay(50);
- // 聯(lián)網(wǎng)業(yè)務(wù)開始,找到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服務(wù)器反饋給予地址
- osDelay(300);
- // 執(zhí)行線程安全的網(wǎng)絡(luò)方法,第二個參數(shù)是voidFunc,第三個參數(shù)是errFunc。如果沒有errFunc,那么就執(zhí)行voidFunc。
- // netifapi_dhcp_start/netifapi_dhcp_stop等都是調(diào)用的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();
- // 聯(lián)網(wǎng)業(yè)務(wù)結(jié)束,斷開DHCP
- err_t ret = netifapi_dhcp_stop(iface);
- printf("netifapi_dhcp_stop: %drn", ret);
- // 斷開Wifi熱點連接
- Disconnect();
- // 移除Wifi熱點的配置
- RemoveDevice(netId);
- // 關(guān)閉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);
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz