[文章]【HarmonyOS HiSpark Wi-Fi IoT 套件试用连载】九:远程TCP通信

阅读量0
0
0
今天的帖子是关于如何让手机可以与wifiiot进行远程通信。通过远程的TCP中转服务端,让手机与板子进行可以进行远程数据传输。
一、数据传输过程
该过程与局域网内的TCP服务端与客户端数据传输过程很类似。局域网内是这样的,两个设备在同一个局域网内,一个作为TCP服务端,一个作为TCP客户端,这样这两个设备就是进行局域网内的数据通信。但是两个设备不在同一个网络内,数据传输在上面的方法是行不通的。这时可以借助一个在公网的TCP中转服务端,实现两个设备的远程数据传输。数据传输过程为两个设备都作为TCP客户端,一个设备把数据传输到服务端,然后服务端把数据传输到另一个设备。但是这也要求,这两个设备的网络是可以连接到公网的,否则是无法连接到公网的TCP服务端。
二、软件设计
首先我们需要一个在公网的TCP客户端。如果各位自己有云服务器的,可以自行搭建一个TCP中转服务端。我使用的客户端是别人用于测试的客户端。软件方面需要考虑几个问题:
  • 连接服务器的切换在我的上几个帖子上,需要连接其他服务器获取时间数据或者获取天气数据。这时,我们需要在进行连接服务器切换,确保获取的数据正常。
  • 连接状态检测我们还需要进行连接状态检查,如果与TCP服务端断开,是无法正常进行数据交互的。
  • 连接重连如果检查到连接断开,或者没有连接成功,需要重新尝试进行与服务端的连接。

1、TCP相关函数
新建tcp_connect.c tcp_connect.h文件,里面主要是与TCP连接、TCP连接断开、TCP数据发送、数据接收相关的函数。注意:TCPIPADDR、TCPPORT是公网TCP客户端IP地址和端口号。其实这几个函数是在之前的tcp例程中拆分出来的,把一个函数划分为四个函数而已。
  • 设置接收超时之前的例程中,没有加入接收超时机制,如果没有接收到数据,会一直呈现阻塞状态,其他任务可能会出现无法正常运行的状态,所以我们需要加入超时机制,超过一定时间没接收到数据,也会推出退出接收过程。timeval 在 <sys/time.h> 头文件中。//设置接收超时  struct timeval timeout={2,0};//1s  if (setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval)) == -1)  {      printf("setsockopt failed!rn");      //goto do_cleanup;  }
  • tcp_connect.c

  1. #include "tcp_connect.h"
  2. //#define     TCPIPADDR       "192.168.3.9"
  3. //#define     TCPPORT          5678
  4. #define     TCPIPADDR       "115.29.109.104"
  5. #define     TCPPORT          6545

  6. static int sockfd;
  7. static int netId;
  8. static struct sockaddr_in serverAddr = {0};

  9. TCP_STATIC connect_status = DISCONNECTED;
  10. //extern char sendData[30];

  11. bool TcpConnect(void)
  12. {
  13.     bool connectflag = false;
  14.     WifiDeviceConfig config = {0};

  15.     // 准备AP的配置参数
  16.     strcpy(config.ssid, PARAM_HOTSPOT_SSID);
  17.     strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);
  18.     config.securityType = PARAM_HOTSPOT_TYPE;
  19.     osDelay(10);
  20.     netId= ConnectToHotspot(&config);

  21.     sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP socket

  22.     serverAddr.sin_family = AF_INET;  // AF_INET表示IPv4协议
  23.     serverAddr.sin_port = htons(TCPPORT);  // 端口号,从主机字节序转为网络字节序
  24.     if (inet_pton(AF_INET, TCPIPADDR , &serverAddr.sin_addr) <= 0) {  // 将主机IP地址从“点分十进制”字符串 转化为 标准格式(32位整数)
  25.         printf("inet_pton failed!rn");
  26.         goto do_cleanup;
  27.     }

  28.     //设置接收超时
  29.     struct timeval timeout={2,0};//1s
  30.     if (setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval)) == -1)
  31.     {
  32.         printf("setsockopt failed!rn");
  33.         //goto do_cleanup;
  34.     }

  35.     // 尝试和目标主机建立连接,连接成功会返回0 ,失败返回 -1
  36.     if (connect(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
  37.         printf("connect failed!rn");
  38.         goto do_cleanup;
  39.     }
  40.     printf("connect to server %s success!rn",  TCPIPADDR);
  41.     connectflag = true;
  42.     connect_status = CONNECTED;
  43. do_cleanup:
  44.     return connectflag;
  45. }

  46. bool TcpDisconnect(void){

  47.     close(sockfd);
  48.     DisconnectWithHotspot(netId);
  49.     connect_status = DISCONNECTED;
  50.     return true;
  51. }

  52. bool TcpSend(char *data,int len){
  53.     // printf("send start!n");
  54.     int retval = send(sockfd, data , len, 0);
  55.     if (retval < 0) {
  56.       //  printf("send request failed!rn");
  57.         return false;
  58.     }
  59.     else{
  60.      //   printf("send OK!n");
  61.         return true;
  62.     }
  63. }

  64. extern char revData[30];
  65. bool TcpRev(void){
  66.     int retval =0;
  67.     retval = recv(sockfd, &revData, sizeof(revData), 0);
  68.     if (retval <= 0) {
  69.        // printf("rev from server failed or done, %ld!rn", retval);
  70.         return false;
  71.     }
  72.     revData[retval] = '\0';
  73.         return true;
  74. }
复制代码
  • tcp_connect.h

  1. #ifndef __TCP_CONNECT_H
  2. #define __TCP_CONNECT_H


  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <unistd.h>

  6. #include "net_demo.h"
  7. #include "net_common.h"
  8. #include "net_params.h"
  9. #include "wifi_connecter.h"
  10. #include "ohos_init.h"
  11. #include "cmsis_os2.h"

  12. typedef enum{
  13.     DISCONNECTED = 0,
  14.     CONNECTED,
  15. }TCP_STATIC;

  16. extern TCP_STATIC connect_status;

  17. bool TcpConnect(void);
  18. bool TcpDisconnect(void);
  19. bool TcpSend(char *data,int len);
  20. bool TcpRev(void);

  21. #endif  /*__TCP_CONNECT_H*/
复制代码
2、TCP数据接收和发送任务
新建tcptask.c 这里面这要是新建两个任务,一个是数据发送任务,一个是数据发送任务。
  • 数据发送任务在数据发送任务中,如果与服务端连接状态正常,会每隔两秒发送一次数据到服务端。这里有连接状态检查和连接重连机制,如果数据发送不成功,则把连接状态看为是未连接状态。在未连接状态,每隔两秒会尝试重新连接服务端,直到再一次成功连接上服务端。
  • 数据接收任务为了保证能够随时接收到服务端发送过来的数据,该任务会每10ms调度一次。在连接状态为已连接情况下,会执行TCP数据接收函数。


  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdbool.h>

  4. #include "tcp_connect.h"
  5. #include "ohos_init.h"
  6. #include "cmsis_os2.h"

  7. char sendData[]="hellow tcp!";
  8. char revData[30]="";
  9. typedef enum{
  10.     GET_NORMAL =0 ,
  11.     GET_PROPRESS,
  12.     GET_SUC,
  13.     GET_FAIL,
  14. }GET_STATUS;

  15. extern GET_STATUS Get_Status;

  16. static void TcpSendTask(void *arg)
  17. {
  18.     sleep(3);
  19.     uint8_t i = 0;
  20.     while(1){
  21.         if(TcpConnect())
  22.         {
  23.             printf("Tcp Connect Sucn");
  24.             break;
  25.         }
  26.         else{
  27.             i++;
  28.         }
  29.         if(i>10)
  30.         break;
  31.     }
  32.     if(i>10)
  33.         printf("Tcp Connect failn");

  34.    
  35.     (void)arg;
  36.     while(1)
  37.     {
  38.         
  39.         if(connect_status == CONNECTED)
  40.         {
  41.             if(!TcpSend(sendData,sizeof(sendData)-1)){
  42.                 connect_status = DISCONNECTED;
  43.                 TcpDisconnect();
  44.             }
  45.         }
  46.         else{
  47.                 if(Get_Status == GET_NORMAL){
  48.                     if(TcpConnect()){
  49.                         printf("Tcp Connect Sucn");
  50.                     }
  51.                 }
  52.         }
  53.         sleep(2);
  54.     }
  55. }

  56. static void TcpSendTaskHandle(void)
  57. {
  58.     osThreadAttr_t attr;
  59.     attr.name = "TcpSendTask";
  60.     attr.attr_bits = 0U;
  61.     attr.cb_mem = NULL;
  62.     attr.cb_size = 0U;
  63.     attr.stack_mem = NULL;
  64.     attr.stack_size = 4096;
  65.     attr.priority = osPriorityNormal;
  66.     if (osThreadNew(TcpSendTask, NULL, &attr) == NULL) {
  67.         printf("[TcpSendTaskHandle] Falied to create TcpSendTask!n");
  68.     }
  69. }
  70. APP_FEATURE_INIT(TcpSendTaskHandle);


  71. static void TcpRevTask(void *arg){
  72.     (void)arg;
  73.     while(1)
  74.     {
  75.         if(connect_status == CONNECTED)
  76.         {
  77.             if(TcpRev()){
  78.                 printf("%s",revData);
  79.             }
  80.         }
  81.         usleep(10000);
  82.     }
  83. }

  84. static void TcpRevTaskHandle(void)
  85. {
  86.     osThreadAttr_t attr;
  87.     attr.name = "TcpRevTask";
  88.     attr.attr_bits = 0U;
  89.     attr.cb_mem = NULL;
  90.     attr.cb_size = 0U;
  91.     attr.stack_mem = NULL;
  92.     attr.stack_size = 4096;
  93.     attr.priority = osPriorityNormal;
  94.     if (osThreadNew(TcpRevTask, NULL, &attr) == NULL) {
  95.         printf("[TcpRevTaskHandle] Falied to create TcpRevTask!n");
  96.     }
  97. }
  98. APP_FEATURE_INIT(TcpRevTaskHandle);
复制代码
3、连接服务器切换
前面也说了,在获取时间或者天气数据时,需要进行连接服务端的切换,确保能接收到正确的数据。在keytask.c文件进行修改。在获取时间和天气函数前加上TCP服务断开函数,获取完之后,加上TCP连接函数。

  1. if((voltage>0.45 && voltage<0.65)&&(!keyflag))
  2.         {
  3.             keyflag = true;
  4.             if(connect_status == CONNECTED)
  5.              TcpDisconnect();
  6.             //OledShowString(16,7,"Sync time...",1);
  7.             //getNtpTime();
  8.             //OledFillScreen(0);
  9.             switch (Now_Screen){
  10.                 case TIMESCREEN:
  11.                 //OledShowString(16,7,"Sync time...",1);
  12.                 Get_Status = GET_PROPRESS;
  13.                 if(getNtpTime()){
  14.                     Get_Status = GET_SUC;
  15.                 }
  16.                 //OledFillScreen(0);
  17.                 else
  18.                     {
  19.                     //OledShowString(0,7,"Get fail...",1);
  20.                     Get_Status = GET_FAIL;
  21.                     }
  22.                 break;
  23.                 case NOWSCREEN:
  24.                     //OledShowString(0,7,"Get Weather...",1);
  25.                     Get_Status = GET_PROPRESS;
  26.                     if(getWeather())
  27.                     //OledFillScreen(0);
  28.                     Get_Status = GET_SUC;
  29.                     else
  30.                     {
  31.                     //OledShowString(0,7,"Get fail...",1);
  32.                     Get_Status = GET_FAIL;
  33.                     }
  34.                 break;
  35.                 case TOSCREEN:
  36.                     Get_Status = GET_PROPRESS;
  37.                     if(getWeather())
  38.                      Get_Status = GET_SUC;
  39.                     else
  40.                     {
  41.                     Get_Status = GET_FAIL;
  42.                     }
  43.                 break;
  44.                 case ATOSCREEN:
  45.                     Get_Status = GET_PROPRESS;
  46.                     if(getWeather())
  47.                     Get_Status = GET_SUC;
  48.                     else
  49.                     {
  50.                     Get_Status = GET_FAIL;
  51.                     }
  52.                 break;

  53.                 default:
  54.                 break;
  55.             }
  56.             TcpConnect();
  57.         }
复制代码
三、演示情况
在手机端需要安装网络调试助手,附件里有我在大学时自己做的一个APP,里面包含网络调试功能。感兴趣的可以自己下载安装,就是界面很丑,有时间再进行好好进行优化。
连接到服务端,该服务端与wifiiot连接的服务端IP和端口一致。
1.jpg
手机数据接收情况,每隔两秒会接收到wifiiot发送过来的“hello tcp!”信息。
2.jpg
手机端发送123456,wifiiot成功接收到数据,并通过串口打印出来。
接收手机.PNG
四、总结
通过公网的TCP中转服务端,实现两个不同网络的设备之间进行远程数据传输,这只是两个设备之间进行远程通信的一种方式。现在不能设置连接wifi和服务端的IP、端口,后面有时间再慢慢进行改进吧。
监控宝_V1.0.rar
(1.32 MB, 下载次数: 3)

回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友