這次使用Hi3861來完成Wifi熱點的連接,並啓動TCP SocketServer,接收消息並將消息反饋TcpCLient。 |
主要做法是啓動開發板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; } }
EnableWifi();
WifiDeviceConfig apConfig = {}; strcpy(apConfig.ssid, "MyWifi"); strcpy(apConfig.preSharedKey, "12345678"); apConfig.securityType = WIFI_SEC_TYPE_PSK; int netId = -1; AddDeviceConfig(&apConfig, &netId);
這個狀態在事件處理函數中設置。未連接成功時,系統會循環等待,知道事件設置該值。
ConnectTo(netId); while (!g_connected) { osDelay(10); }
struct netif* iface = netifapi_netif_find("wlan0"); if (iface) { err_t ret = netifapi_dhcp_start(iface); osDelay(300); }
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)
int connfd = -1; connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen); recv(connfd, request, sizeof(request), 0); send(connfd, request, strlen(request), 0);
lwip_close(connfd); lwip_close(socketfd);
err_t ret = netifapi_dhcp_stop(iface); Disconnect(); RemoveDevice(netId); DisableWifi();
#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);