鸿蒙Hi3861来完成Wifi热点的连接并启动TCP SocketServer

导读这次使用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热点的连接,并启动TCP SocketServer?

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

如何使用鸿蒙Hi3861来完成Wifi热点的连接,并启动TCP SocketServer?如何使用鸿蒙Hi3861来完成Wifi热点的连接,并启动TCP SocketServer?

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

如何使用鸿蒙Hi3861来完成Wifi热点的连接,并启动TCP SocketServer?如何使用鸿蒙Hi3861来完成Wifi热点的连接,并启动TCP SocketServer?

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

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

以上是 鸿蒙Hi3861来完成Wifi热点的连接并启动TCP SocketServer 的全部内容, 来源链接: utcz.com/a/120419.html

回到顶部