阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

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

93次阅读
没有评论

共计 9149 个字符,预计需要花费 23 分钟才能阅读完成。

导读 这次使用 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);

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计9149字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中