描述
本文来源电子发烧友社区,作者:李元江, 帖子地址:
https://bbs.elecfans.com/jishu_2028163_1_1.html
今天的帖子是关于如何让
手机可以与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 在 头文件中。//设置接收超时 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
-
#include "tcp_connect.h"
-
//#define TCPIPADDR "192.168.3.9"
-
//#define TCPPORT 5678
-
#define TCPIPADDR "115.29.109.104"
-
#define TCPPORT 6545
-
-
static int sockfd;
-
static int netId;
-
static struct sockaddr_in serverAddr = {0};
-
-
TCP_STATIC connect_status = DISCONNECTED;
-
//extern char sendData[30];
-
-
bool TcpConnect(void)
-
{
-
bool connectflag = false;
-
WifiDeviceConfig config = {0};
-
-
// 准备AP的配置参数
-
strcpy(config.ssid, PARAM_HOTSPOT_SSID);
-
strcpy(config.preSharedKey, PARAM_HOTSPOT_PSK);
-
config.securityType = PARAM_HOTSPOT_TYPE;
-
osDelay(10);
-
netId= ConnectToHotspot(&config);
-
-
sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP socket
-
-
serverAddr.sin_family = AF_INET; // AF_INET表示IPv4协议
-
serverAddr.sin_port = htons(TCPPORT); // 端口号,从主机字节序转为网络字节序
-
if (inet_pton(AF_INET, TCPIPADDR , &serverAddr.sin_addr) <= 0) { // 将主机IP地址从“点分十进制”字符串 转化为 标准格式(32位整数)
-
printf("inet_pton failed!rn");
-
goto do_cleanup;
-
}
-
-
//设置接收超时
-
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;
-
}
-
-
// 尝试和目标主机建立连接,连接成功会返回0 ,失败返回 -1
-
if (connect(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
-
printf("connect failed!rn");
-
goto do_cleanup;
-
}
-
printf("connect to server %s success!rn", TCPIPADDR);
-
connectflag = true;
-
connect_status = CONNECTED;
-
do_cleanup:
-
return connectflag;
-
}
-
-
bool TcpDisconnect(void){
-
-
close(sockfd);
-
DisconnectWithHotspot(netId);
-
connect_status = DISCONNECTED;
-
return true;
-
}
-
-
bool TcpSend(char *data,int len){
-
// printf("send start!n");
-
int retval = send(sockfd, data , len, 0);
-
if (retval < 0) {
-
// printf("send request failed!rn");
-
return false;
-
}
-
else{
-
// printf("send OK!n");
-
return true;
-
}
-
}
-
-
extern char revData[30];
-
bool TcpRev(void){
-
int retval =0;
-
retval = recv(sockfd, &revData, sizeof(revData), 0);
-
if (retval <= 0) {
-
// printf("rev from server failed or done, %ld!rn", retval);
-
return false;
-
}
-
revData[retval] = '';
-
return true;
-
}
复制代码
-
#ifndef __TCP_CONNECT_H
-
#define __TCP_CONNECT_H
-
-
-
#include
-
#include
-
#include
-
-
#include "net_demo.h"
-
#include "net_common.h"
-
#include "net_params.h"
-
#include "wifi_connecter.h"
-
#include "ohos_init.h"
-
#include "cmsis_os2.h"
-
-
typedef enum{
-
DISCONNECTED = 0,
-
CONNECTED,
-
}TCP_STATIC;
-
-
extern TCP_STATIC connect_status;
-
-
bool TcpConnect(void);
-
bool TcpDisconnect(void);
-
bool TcpSend(char *data,int len);
-
bool TcpRev(void);
-
-
#endif /*__TCP_CONNECT_H*/
复制代码
2、TCP数据接收和发送任务
新建tcptask.c 这里面这要是新建两个任务,一个是数据发送任务,一个是数据发送任务。
-
#include
-
#include
-
#include
-
-
#include "tcp_connect.h"
-
#include "ohos_init.h"
-
#include "cmsis_os2.h"
-
-
char sendData[]="hellow tcp!";
-
char revData[30]="";
-
typedef enum{
-
GET_NORMAL =0 ,
-
GET_PROPRESS,
-
GET_SUC,
-
GET_FAIL,
-
}GET_STATUS;
-
-
extern GET_STATUS Get_Status;
-
-
static void TcpSendTask(void *arg)
-
{
-
sleep(3);
-
uint8_t i = 0;
-
while(1){
-
if(TcpConnect())
-
{
-
printf("Tcp Connect Sucn");
-
break;
-
}
-
else{
-
i++;
-
}
-
if(i>10)
-
break;
-
}
-
if(i>10)
-
printf("Tcp Connect failn");
-
-
-
(void)arg;
-
while(1)
-
{
-
-
if(connect_status == CONNECTED)
-
{
-
if(!TcpSend(sendData,sizeof(sendData)-1)){
-
connect_status = DISCONNECTED;
-
TcpDisconnect();
-
}
-
}
-
else{
-
if(Get_Status == GET_NORMAL){
-
if(TcpConnect()){
-
printf("Tcp Connect Sucn");
-
}
-
}
-
}
-
sleep(2);
-
}
-
}
-
-
static void TcpSendTaskHandle(void)
-
{
-
osThreadAttr_t attr;
-
attr.name = "TcpSendTask";
-
attr.attr_bits = 0U;
-
attr.cb_mem = NULL;
-
attr.cb_size = 0U;
-
attr.stack_mem = NULL;
-
attr.stack_size = 4096;
-
attr.priority = osPriorityNormal;
-
if (osThreadNew(TcpSendTask, NULL, &attr) == NULL) {
-
printf("[TcpSendTaskHandle] Falied to create TcpSendTask!n");
-
}
-
}
-
APP_FEATURE_INIT(TcpSendTaskHandle);
-
-
-
static void TcpRevTask(void *arg){
-
(void)arg;
-
while(1)
-
{
-
if(connect_status == CONNECTED)
-
{
-
if(TcpRev()){
-
printf("%s",revData);
-
}
-
}
-
usleep(10000);
-
}
-
}
-
-
static void TcpRevTaskHandle(void)
-
{
-
osThreadAttr_t attr;
-
attr.name = "TcpRevTask";
-
attr.attr_bits = 0U;
-
attr.cb_mem = NULL;
-
attr.cb_size = 0U;
-
attr.stack_mem = NULL;
-
attr.stack_size = 4096;
-
attr.priority = osPriorityNormal;
-
if (osThreadNew(TcpRevTask, NULL, &attr) == NULL) {
-
printf("[TcpRevTaskHandle] Falied to create TcpRevTask!n");
-
}
-
}
-
APP_FEATURE_INIT(TcpRevTaskHandle);
复制代码
3、连接服务器切换
前面也说了,在获取时间或者天气数据时,需要进行连接服务端的切换,确保能接收到正确的数据。在keytask.c文件进行修改。在获取时间和天气函数前加上TCP服务断开函数,获取完之后,加上TCP连接函数。
-
if((voltage>0.45 && voltage<0.65)&&(!keyflag))
-
{
-
keyflag = true;
-
if(connect_status == CONNECTED)
-
TcpDisconnect();
-
//OledShowString(16,7,"Sync time...",1);
-
//getNtpTime();
-
//OledFillScreen(0);
-
switch (Now_Screen){
-
case TIMESCREEN:
-
//OledShowString(16,7,"Sync time...",1);
-
Get_Status = GET_PROPRESS;
-
if(getNtpTime()){
-
Get_Status = GET_SUC;
-
}
-
//OledFillScreen(0);
-
else
-
{
-
//OledShowString(0,7,"Get fail...",1);
-
Get_Status = GET_FAIL;
-
}
-
break;
-
case NOWSCREEN:
-
//OledShowString(0,7,"Get Weather...",1);
-
Get_Status = GET_PROPRESS;
-
if(getWeather())
-
//OledFillScreen(0);
-
Get_Status = GET_SUC;
-
else
-
{
-
//OledShowString(0,7,"Get fail...",1);
-
Get_Status = GET_FAIL;
-
}
-
break;
-
case TOSCREEN:
-
Get_Status = GET_PROPRESS;
-
if(getWeather())
-
Get_Status = GET_SUC;
-
else
-
{
-
Get_Status = GET_FAIL;
-
}
-
break;
-
case ATOSCREEN:
-
Get_Status = GET_PROPRESS;
-
if(getWeather())
-
Get_Status = GET_SUC;
-
else
-
{
-
Get_Status = GET_FAIL;
-
}
-
break;
-
-
default:
-
break;
-
}
-
TcpConnect();
-
}
复制代码
三、演示情况
在手机端需要安装网络调试助手,附件里有我在大学时自己做的一个APP,里面包含网络调试功能。感兴趣的可以自己下载安装,就是界面很丑,有时间再进行好好进行优化。
连接到服务端,该服务端与wifiiot连接的服务端IP和端口一致。
手机数据接收情况,每隔两秒会接收到wifiiot发送过来的“hello tcp!”信息。
手机端发送123456,wifiiot成功接收到数据,并通过串口打印出来。
四、总结
通过公网的TCP中转服务端,实现两个不同网络的设备之间进行远程数据传输,这只是两个设备之间进行远程通信的一种方式。现在不能设置连接wifi和服务端的IP、端口,后面有时间再慢慢进行改进吧。
打开APP阅读更多精彩内容