|
|
|
|
公众号矩阵

使用鸿蒙Hi3861完成连接WiFi热点并启动TCPSocketServ

这次使用Hi3861来完成Wifi热点的连接,并启动TCP SocketServer,接收消息并将消息反馈TcpCLient。

作者:LegendOfTiger来源:鸿蒙社区|2021-02-02 15:52

想了解更多内容,请访问:

51CTO和华为官方战略合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#zz

这次使用Hi3861来完成Wifi热点的连接,并启动TCP SocketServer,接收消息并将消息反馈TcpCLient。

一、连接Wifi热点。主要做法是启动开发板Wifi,然后设置热点和密码等配置信息,再连接热点。

1、先定义两个Wifi监听器,一个连接改变、一个状态改变,并注册监听器。其中重要的是OnWifiConnectionChanged连接状态事件处理函数。该函数会在连接成功后设置全局变量g_connected=1,代表已经连接成功。

  1. WifiEvent eventListener = { 
  2.   .OnWifiConnectionChanged = OnWifiConnectionChanged, 
  3.   .OnWifiScanStateChanged = OnWifiScanStateChanged 
  4. }; 
  5. WifiErrorCode errCode = RegisterWifiEvent(&eventListener); 
  6.  
  7. void OnWifiConnectionChanged(int state, WifiLinkedInfo* info) { 
  8.   if (!info) return
  9.  
  10.   if (state == WIFI_STATE_AVALIABLE) { 
  11.     g_connected = 1; 
  12.   } else { 
  13.     g_connected = 0; 
  14.   } 

2、启动Wifi

  1. EnableWifi(); 

3、设置Wifi热点信息,并返回NetworkId

  1. WifiDeviceConfig apConfig = {}; 
  2. strcpy(apConfig.ssid, "MyWifi"); 
  3. strcpy(apConfig.preSharedKey, "12345678"); 
  4. apConfig.securityType = WIFI_SEC_TYPE_PSK; 
  5.  
  6. int netId = -1; 
  7. AddDeviceConfig(&apConfig, &netId); 

4、连接热点,注意此时的g_connected变量,true代表已连接,false代表未连接。这个状态在事件处理函数中设置。未连接成功时,系统会循环等待,知道事件设置该值。

  1. ConnectTo(netId); 
  2. while (!g_connected) { 
  3.   osDelay(10); 

二、进行联网,找到wlan0的network interface,然后启动DHCP客户端,获取IP地址。

  1. struct netif* iface = netifapi_netif_find("wlan0"); 
  2. if (iface) { 
  3.   err_t ret = netifapi_dhcp_start(iface); 
  4.   osDelay(300); 

三、启动TcpSocketServer,并收发消息

1、创建SocketServer,设置服务端口,并启动监听

  1. int sockfd = socket(AF_INET, SOCK_STREAM, 0); 
  2.  
  3. struct sockaddr_in serverAddr = {0}; 
  4. serverAddr.sin_family = AF_INET; 
  5. serverAddr.sin_port = htons(port); 
  6. serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 
  7. bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); 
  8.  
  9. int backlog = 1; 
  10. listen(sockfd, backlog) 

2、客户端连接。接收客户端消息并发送回去。注意连接后,会创建一个新的Socket File Description。

  1. int connfd = -1; 
  2. connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen); 
  3.  
  4. recv(connfd, request, sizeof(request), 0); 
  5. send(connfd, request, strlen(request), 0); 

3、关闭TcpSocketServer

  1. lwip_close(connfd); 
  2. lwip_close(socketfd); 

四、联网结束,关闭DHCP客户端,断开Wifi,移除热点的配置信息,禁用Wifi。

  1. err_t ret = netifapi_dhcp_stop(iface); 
  2. Disconnect(); 
  3. RemoveDevice(netId); 
  4. DisableWifi(); 

五、测试情况如下:

1、启动开发板,连接Wifi热点,启动TcpServer

2、TcpClient(网络调试助手)连接开发板的TcpServer(HiBurn)。

3、TcpClient输入数据并发送,TcpServer接收后再发送回TcpClient。


六、全部源代码,我都注释了,希望大家能够有所参考。

  1. #include <errno.h> 
  2. #include <stdio.h> 
  3. #include <string.h> 
  4. #include <stddef.h> 
  5. #include <unistd.h> 
  6.  
  7. #include "ohos_init.h" 
  8. #include "cmsis_os2.h" 
  9. #include "wifi_device.h" 
  10.  
  11. #include "lwip/netifapi.h" 
  12. #include "lwip/api_shell.h" 
  13. #include "lwip/sockets.h" 
  14.  
  15. // 接收、发送的数据 
  16. static char request[128] = ""
  17. // 未连接热点=0,已连接热点=1 
  18. static int g_connected = 0; 
  19.  
  20. // 输出连接信息字符串 
  21. // 打印内容样例--> bssid: 38:47:BC:49:01:FA, rssi: 0, connState: 0, reason: 0, ssid: MyMobile 
  22. void PrintLinkedInfo(WifiLinkedInfo* info) { 
  23.   if (!info) return
  24.  
  25.   static char macAddress[32] = {0}; 
  26.   unsigned char* mac = info->bssid; 
  27.   snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 
  28.   printf("bssid: %s, rssi: %d, connState: %d, reason: %d, ssid: %srn", macAddress, info->rssi, info->connState, info->disconnectedReason, info->ssid); 
  29.  
  30. // 连接状态改变事件处理 
  31. void OnWifiConnectionChanged(int state, WifiLinkedInfo* info) { 
  32.   if (!info) return
  33.  
  34.   // 输出类似内容:OnWifiConnectionChanged 31, state = 1, info =  
  35.   printf("%s %d, state = %d, info = rn", __FUNCTION__, __LINE__, state); 
  36.   PrintLinkedInfo(info); 
  37.  
  38.   // 根据连接状态设置g_connected 
  39.   if (state == WIFI_STATE_AVALIABLE) { 
  40.     g_connected = 1; 
  41.   } else { 
  42.     g_connected = 0; 
  43.   } 
  44.  
  45. // 扫描状态改变事件处理 
  46. void OnWifiScanStateChanged(int state, int size) { 
  47.   printf("%s %d, state = %X, size = %drn", __FUNCTION__, __LINE__, state, size); 
  48.  
  49. void DisconnectTcpSocket(int connfd) { 
  50.   sleep(1); 
  51.   printf("do_disconnect...rn"); 
  52.   lwip_close(connfd); 
  53.   sleep(1); // for debug 
  54.  
  55. void CloseTcpSocket(int socketfd) { 
  56.   printf("do_cleanup...rn"); 
  57.  
  58.   lwip_close(socketfd); 
  59.  
  60. static void TcpServerHandler(void) { 
  61.   ssize_t retval = 0; 
  62.   unsigned short port = 9118; 
  63.  
  64.   // 创建一个通信的Socket,并返回一个Socket文件描述符。第一个参数IpV4,第二个参数SOCK_STREAM类型,第三个指用到的协议 
  65.   int sockfd = socket(AF_INET, SOCK_STREAM, 0); 
  66.  
  67.   // 客户端地址和地址长度 
  68.   struct sockaddr_in clientAddr = {0}; 
  69.   socklen_t clientAddrLen = sizeof(clientAddr); 
  70.  
  71.   // 服务端地址 
  72.   struct sockaddr_in serverAddr = {0}; 
  73.   serverAddr.sin_family = AF_INET; 
  74.   // htons是将整型变量从主机字节顺序转变成网络字节顺序,就是整数在地址空间存储方式变为高位字节存放在内存的低地址处 
  75.   serverAddr.sin_port = htons(port); 
  76.   // 监听本机的所有IP地址,INADDR_ANY=0x0 
  77.   // 将主机数转换成无符号长整型的网络字节顺序 
  78.   serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 
  79.  
  80.   // 服务端绑定端口 
  81.   retval = bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); 
  82.   if (retval < 0) { 
  83.     printf("bind failed, %ld!rn", retval); 
  84.     CloseTcpSocket(sockfd); 
  85.     return
  86.   } 
  87.   printf("bind to port %d success!rn", port); 
  88.  
  89.   // 开始监听,backlog指Pending连接队列增长到的最大长度。队列满了,再有新连接请求到达,则客户端ECONNREFUSED错误。如果支持重传,则请求忽略。 
  90.   int backlog = 1; 
  91.   retval = listen(sockfd, backlog); 
  92.   if (retval < 0) { 
  93.     printf("listen failed!rn"); 
  94.     CloseTcpSocket(sockfd); 
  95.     return
  96.   } 
  97.   printf("listen with %d backlog success!rn", backlog); 
  98.  
  99.   int outerFlag = 1; 
  100.   while (outerFlag) { 
  101.     // 接受客户端连接,成功会返回一个表示连接的 socket。clientAddr参数将会携带客户端主机和端口信息;失败返回 -1 
  102.     // 从Pending连接队列中获取第一个连接,根据sockfd的socket协议、地址族等内容创建一个新的socket文件描述,并返回。 
  103.     // 此后的 收、发 都在 表示连接的 socket 上进行;之后 sockfd 依然可以继续接受其他客户端的连接, 
  104.     // UNIX系统上经典的并发模型是“每个连接一个进程”——创建子进程处理连接,父进程继续接受其他客户端的连接 
  105.     // 鸿蒙liteos-a内核之上,可以使用UNIX的“每个连接一个进程”的并发模型liteos-m内核之上,可以使用“每个连接一个线程”的并发模型 
  106.     int connfd = -1; 
  107.     connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen); 
  108.     if (connfd < 0) { 
  109.       printf("accept failed, %d, %drn", connfd, errno); 
  110.       CloseTcpSocket(sockfd); 
  111.       outerFlag = 0; 
  112.     } 
  113.     printf("accept success, connfd = %d !rn", connfd); 
  114.     // inet_ntoa:网络地址转换成“.”点隔的字符串格式。ntohs:16位数由网络字节顺序转换为主机字节顺序。 
  115.     printf("client addr info: host = %s, port = %drn", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); 
  116.  
  117.     int innerFlag = 1; 
  118.     // 接收消息,然后发送回去 
  119.     while (innerFlag) { 
  120.       // 后续 收、发 都在 表示连接的 socket 上进行; 
  121.       // 在新的Socket文件描述上接收信息. 
  122.       retval = recv(connfd, request, sizeof(request), 0); 
  123.       if (retval < 0) { 
  124.         printf("recv request failed, %ld!rn", retval); 
  125.         innerFlag = 0; 
  126.       } else if (retval == 0) { 
  127.         // 对方主动断开连接 
  128.         printf("client disconnected!rn"); 
  129.         innerFlag = 0; 
  130.       } else { 
  131.         printf("recv request{%s} from client done!rn", request); 
  132.  
  133.         // 发送数据 
  134.         retval = send(connfd, request, strlen(request), 0); 
  135.         if (retval <= 0) { 
  136.           printf("send response failed, %ld!rn", retval); 
  137.           innerFlag = 0; 
  138.         } 
  139.         printf("send response{%s} to client done!rn", request); 
  140.  
  141.         // 清空缓冲区 
  142.         memset(&request, 0, sizeof(request)); 
  143.     } 
  144.  
  145.     DisconnectTcpSocket(connfd); 
  146.  
  147.     outerFlag = 0; 
  148.   } 
  149.  
  150.   CloseTcpSocket(sockfd); 
  151.  
  152. static void TcpServerTask(void *arg) { 
  153.   (void)arg; 
  154.    
  155.   // 先定义两个Wifi监听器,一个连接改变、一个状态改变 
  156.   WifiEvent eventListener = { 
  157.     .OnWifiConnectionChanged = OnWifiConnectionChanged, 
  158.     .OnWifiScanStateChanged = OnWifiScanStateChanged 
  159.   }; 
  160.  
  161.   // 等待10个系统Ticks。每个ticks多少个us,计算方式= 1000 * 1000 / osKernelGetTickFreq() 
  162.   osDelay(10); 
  163.  
  164.   // 注册监听器 
  165.   WifiErrorCode errCode = RegisterWifiEvent(&eventListener); 
  166.   printf("RegisterWifiEvent: %drn", errCode); 
  167.  
  168.   // 设置Wifi热点信息 
  169.   WifiDeviceConfig apConfig = {}; 
  170.   strcpy(apConfig.ssid, "MyMobile"); 
  171.   strcpy(apConfig.preSharedKey, "12345678"); 
  172.   apConfig.securityType = WIFI_SEC_TYPE_PSK; 
  173.  
  174.   int netId = -1; 
  175.  
  176.   // 启用Wifi 
  177.   errCode = EnableWifi(); 
  178.   printf("EnableWifi: %drn", errCode); 
  179.   osDelay(10); 
  180.  
  181.   // 设置Wifi热点配置信息,返回生成的网络Id-netId。 
  182.   errCode = AddDeviceConfig(&apConfig, &netId); 
  183.   printf("AddDeviceConfig: %drn", errCode); 
  184.  
  185.   // 根据网络Id连接到Wifi热点 
  186.   g_connected = 0; 
  187.   errCode = ConnectTo(netId); 
  188.   printf("ConnectTo(%d): %drn", netId, errCode); 
  189.   // 未连接完成,则一直等待。g_connected状态会在事件中设置。 
  190.   while (!g_connected) { 
  191.     osDelay(10); 
  192.   } 
  193.   printf("g_connected: %drn", g_connected); 
  194.   osDelay(50); 
  195.  
  196.   // 联网业务开始,找到netifname=wlan0的netif。 
  197.   struct netif* iface = netifapi_netif_find("wlan0"); 
  198.   if (iface) { 
  199.     // 启动DHCP客户端,获取IP地址 
  200.     err_t ret = netifapi_dhcp_start(iface); 
  201.     printf("netifapi_dhcp_start: %drn", ret); 
  202.     // 等待DHCP服务器反馈给予地址 
  203.     osDelay(300); 
  204.     // 执行线程安全的网络方法,第二个参数是voidFunc,第三个参数是errFunc。如果没有errFunc,那么就执行voidFunc。 
  205.     // netifapi_dhcp_start/netifapi_dhcp_stop等都是调用的netifapi_netif_common方法。 
  206.     // dhcp_clients_info_show显示信息 
  207.     /* 
  208.     server : 
  209.       server_id : 192.168.43.1 
  210.       mask : 255.255.255.0, 1 
  211.       gw : 192.168.43.1 
  212.       T0 : 3600 
  213.       T1 : 1800 
  214.       T2 : 3150 
  215.     clients <1> : 
  216.       mac_idx mac             addr            state   lease   tries   rto      
  217.       0       b4c9b9af69f8    192.168.43.56   10      0       1       2       
  218.     */ 
  219.     ret = netifapi_netif_common(iface, dhcp_clients_info_show, NULL); 
  220.     printf("netifapi_netif_common: %drn", ret); 
  221.   } 
  222.  
  223.   TcpServerHandler(); 
  224.  
  225.   // 联网业务结束,断开DHCP 
  226.   err_t ret = netifapi_dhcp_stop(iface); 
  227.   printf("netifapi_dhcp_stop: %drn", ret); 
  228.  
  229.   // 断开Wifi热点连接 
  230.   Disconnect(); 
  231.  
  232.   // 移除Wifi热点的配置 
  233.   RemoveDevice(netId); 
  234.  
  235.   // 关闭Wifi 
  236.   errCode = DisableWifi(); 
  237.   printf("DisableWifi: %drn", errCode); 
  238.   osDelay(200); 
  239.  
  240. static void TcpServerEntry(void) { 
  241.   osThreadAttr_t attr; 
  242.  
  243.   attr.name = "TcpServerTask"
  244.   attr.attr_bits = 0U; 
  245.   attr.cb_mem = NULL
  246.   attr.cb_size = 0U; 
  247.   attr.stack_mem = NULL
  248.   attr.stack_size = 10240; 
  249.   attr.priority = osPriorityNormal; 
  250.  
  251.   if (osThreadNew((osThreadFunc_t)TcpServerTask, NULL, &attr) == NULL) { 
  252.     printf("SunLaoTest-Fail Create"); 
  253.   } 
  254.  
  255. APP_FEATURE_INIT(TcpServerEntry); 

想了解更多内容,请访问:

51CTO和华为官方战略合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#zz

【编辑推荐】

  1. CDN:什么是边缘CDN和虚拟CDN(vCDN)?
  2. 终于有人将数据中台讲清楚了,原来根本不算啥
  3. 4种速度最慢的动态编程语言,你一定用过
  4. 勒索软件攻击将在2021年爆发
  5. 为什么有些高级开发人员不喜欢Python
【责任编辑:jianghua TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

数据湖与数据仓库的分析实践攻略

数据湖与数据仓库的分析实践攻略

助力现代化数据管理:数据湖与数据仓库的分析实践攻略
共3章 | 创世达人

5人订阅学习

云原生架构实践

云原生架构实践

新技术引领移动互联网进入急速赛道
共3章 | KaliArch

31人订阅学习

数据中心和VPDN网络建设案例

数据中心和VPDN网络建设案例

漫画+案例
共20章 | 捷哥CCIE

215人订阅学习

视频课程+更多

从新手小白进入WinForm编程 (VS2019)

从新手小白进入WinForm编程 (VS2019)

讲师:黄强141人学习过

Redis 核心原理与实战

Redis 核心原理与实战

讲师:白丁4119人学习过

Ceph分布式存储运维实战

Ceph分布式存储运维实战

讲师:张岩峰279人学习过

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微