zoukankan      html  css  js  c++  java
  • 使用Hi3861完成连接wifi热点并启动TCPSocketServer

    目录:

    一、连接Wifi热点

    二、进行联网

    三、启动TcpSocketServer,并收发消息

    四、联网结束

    五、测试情况

    六、全部源代码

    这次使用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

    使用Hi3861完成连接wifi热点并启动TCPSocketServer2、TcpClient(网络调试助手)连接开发板的TcpServer(HiBurn)。

    使用Hi3861完成连接wifi热点并启动TCPSocketServer使用Hi3861完成连接wifi热点并启动TCPSocketServer3、TcpClient输入数据并发送,TcpServer接收后再发送回TcpClient。

    使用Hi3861完成连接wifi热点并启动TCPSocketServer使用Hi3861完成连接wifi热点并启动TCPSocketServer

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

    #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);

    作者:LegendOfTiger

    想了解更多内容,请访问: 51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com

  • 相关阅读:
    第03组 Alpha冲刺(3/6)
    第03组 Alpha冲刺(2/6)
    第03组 Alpha冲刺(1/6)
    团队项目-选题报告
    第3组 团队展示
    福大软工 · BETA 版冲刺前准备(团队)
    Alpha 事后诸葛亮
    Alpha冲刺
    Alpha冲刺-(9/10)
    Alpha冲刺
  • 原文地址:https://www.cnblogs.com/HarmonyOS/p/14355807.html
Copyright © 2011-2022 走看看