Directory:

Connect to a Wifi hotspot

Two, networking

3. Start the TcpSocketServer and send and receive messages

4. End of networking

V. Test conditions

Six, the full source code

This time Hi3861 is used to complete the connection of the Wifi hotspot, and the TCP SocketServer is started to receive the message and send the message back to the TcpCLient.

Connect to a Wifi hotspot. The main approach is to start the development board Wifi, and then set the hotspot and password and other configuration information, and then connect the hotspot.

1, first define two Wifi listeners, one connection change, one state change, and register the listener. Of importance is the OnWifiConnectionChanged connection status event handler. The function sets the global variable g_connected=1 after a successful connection, indicating a successful connection.

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;
  }
}
Copy the code

2. Enable Wifi

EnableWifi();
Copy the code

3. Set the Wifi hotspot information and return NetworkId

WifiDeviceConfig apConfig = {};
strcpy(apConfig.ssid, "MyWifi");
strcpy(apConfig.preSharedKey, "12345678");
apConfig.securityType = WIFI_SEC_TYPE_PSK;

int netId = -1;
AddDeviceConfig(&apConfig, &netId);
Copy the code

4. Connect the hotspot. Pay attention to the g_connected variable, where true means connected, false means disconnected. This state is set in the event handler. If the connection is not successful, the system waits until the event sets this value.

ConnectTo(netId); while (! g_connected) { osDelay(10); }Copy the code

2. Connect to the network, locate the network interface of WLAN0, and start the DHCP client to obtain the IP address.

struct netif* iface = netifapi_netif_find("wlan0");
if (iface) {
  err_t ret = netifapi_dhcp_start(iface);
  osDelay(300);
}
Copy the code

3. Start the TcpSocketServer and send and receive messages

1. Create a SocketServer, set the service port, and enable the listener

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)
Copy the code

2. Client connection. Receive the client message and send it back. Notice that a new Socket File Description is created after the connection.

int connfd = -1;
connfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientAddrLen);

recv(connfd, request, sizeof(request), 0);
send(connfd, request, strlen(request), 0);
Copy the code

3. Disable the TcpSocketServer

lwip_close(connfd);
lwip_close(socketfd);
Copy the code

4. After networking, close the DHCP client, disconnect Wifi, remove hotspot configuration information, and disable Wifi.

err_t ret = netifapi_dhcp_stop(iface);
Disconnect();
RemoveDevice(netId);
DisableWifi();
Copy the code

V. The test situation is as follows:

1. Start the development board, connect the Wifi hotspot, and start the TcpServer

TcpClient (Network debugging Assistant) Connects to the TcpServer (HiBurn) of the development board.

3. The TcpClient input data and sends it. The TcpServer receives the data and sends it back to the TcpClient.

Six, all source code, I have annotated, I hope you can have a reference.

#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);
Copy the code

Author: LegendOfTiger

Want to know more, please visit: 51 cto and huawei officials strategic cooperation HongMeng technology community at https://harmonyos.51cto.com