HarmonyOS 鸿蒙Next:使用arkTS API9编写单页面程序与C++ Qt服务端通信
HarmonyOS 鸿蒙Next:使用arkTS API9编写单页面程序与C++ Qt服务端通信
制作这个应用的一个简单的想法就是公司的电脑和手机无法直接通信。虽然现在有各种第三方软件,但功能均不是我满意的。有时候我希望用手机发起指令,操作公司的电脑完成一些自动化的操作。
我年初就关注鸿蒙的开发了,当时NEXT正准备起飞,我也买了一些新的华为手机。现在有一台mate 50自用。最近NEXT还没有正式使用,能测试的机型均为mate 60,这意味着我只能用API 9在我的mate 50上开发,有些遗憾。
经过这几天鸿蒙的arkTS开发,我了解了一些手机客户端开发的方法,虽然还有一些不是很明白,比如说如何用API 9的Navigation搭建一个具有导航功能的页面。我之前有一些Qt的编程经验,就用Qt的网络模块做了一个服务端。可以看到,客户端和服务端成功地进行了通信。
我的想法不止这些。下一步,我希望做成手机操作客户端的工具集程序,可以自动执行编译、单元测试等。也可以将服务端的测试报告等文件发送到手机客户端进行分析。
希望能将这些程序分享出来,抛砖引玉,也希望得到高手的指导。
下面是鸿蒙arkTS端的代码,代码还处理了竖屏和横屏的适配。
import mediaquery from ‘@ohos.mediaquery’;
import wifiManager from ‘@ohos.wifiManager’;
import socket from ‘@ohos.net.socket’;
let storageRecord: Record<string, string> = { ‘textToSend’: ‘欢迎光临,服务器’ };
let storage: LocalStorage = new LocalStorage(storageRecord);
@Entry(storage)
@Component
struct Index
{
@LocalStorageLink(‘countStorage’) textToSend: string = ‘欢迎光临,服务器’
@State localIPText: string = ‘’
@State ipToConnectText: string = ‘192.168.1.8’
@State connectStatus: string = ‘准备连接’
@State tcp: socket.TCPSocket = socket.constructTCPSocketInstance()
@State iconWidthStr: string = ‘auto’
@State iconHeightStr: string = ‘50%’
@State tcpConnected: boolean = false
listener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync(’(orientation: landscape)’)
aboutToAppear()
{
// 绑定当前应用实例
// 绑定回调函数
this.listener.on(‘change’, (mediaQueryResult:mediaquery.MediaQueryResult) => { this.onPortrait(mediaQueryResult) });
}
// 当满足媒体查询条件时,触发回调
onPortrait(mediaQueryResult: mediaquery.MediaQueryResult)
{
if (mediaQueryResult.matches as boolean)
{
// 若设备为横屏状态,更改相应的页面布局
this.iconWidthStr = ‘auto’;
this.iconHeightStr = ‘50%’;
}
else
{
this.iconWidthStr = ‘50%’;
this.iconHeightStr = ‘auto’;
}
}
build()
{
Row()
{
Column()
{
Image($r(‘app.media.icon’)).width(this.iconWidthStr).height(this.iconHeightStr).autoResize(true)
Column() {
Row() {
Text(‘您的IP地址是’)
TextInput({ text: this.localIPText, placeholder: ‘请输入你的IP地址’ }).onAppear(() => {
let ipInfo = wifiManager.getIpInfo();
let ipAddressNumber = ipInfo.ipAddress;
this.localIPText = (ipAddressNumber >> 24 & 0xFF) +
“.” + (ipAddressNumber >> 16 & 0xFF) +
“.” + (ipAddressNumber >> 8 & 0xFF) +
“.” + (ipAddressNumber & 0xFF)
}).onChange((value: string) => {
this.localIPText = value;
})
}
}
Column() {
Row() {
Text(‘即将连接的IP地址,端口:23756’)
TextInput({ text: this.ipToConnectText, placeholder: ‘请输入即将连接的IP地址’ })
.onChange((value: string) => {
this.ipToConnectText = value;
})
}
}
Text(this.connectStatus)
Row({ space: 6 })
{
Button(‘连接’, {stateEffect: true}).enabled(!this.tcpConnected).onClick(() =>
{
let bindAddress = {
address: this.localIPText,
port: 23756, // 连接端口,如23756
family: 1// IPv4
};
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcp.bind(bindAddress, err =>{
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (err) {
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'绑定失败,原因是'</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(err);
<span class="hljs-keyword"><span class="hljs-keyword">return</span></span>;
}
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> connectAddress = {
address: <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.ipToConnectText,
port: <span class="hljs-number"><span class="hljs-number">23756</span></span>, <span class="hljs-comment"><span class="hljs-comment">// 连接端口,如23756</span></span>
family: <span class="hljs-number"><span class="hljs-number">1</span></span><span class="hljs-comment"><span class="hljs-comment">// IPv4</span></span>
};
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcp.connect({
address: connectAddress, timeout: <span class="hljs-number"><span class="hljs-number">6000</span></span>
}, err => {
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (err) {
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'连接失败,原因是'</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(err);
<span class="hljs-keyword"><span class="hljs-keyword">return</span></span>;
}
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcpConnected = <span class="hljs-literal"><span class="hljs-literal">true</span></span>;
})
})
})
Button(<span class="hljs-string"><span class="hljs-string">'发送'</span></span>, {stateEffect: <span class="hljs-literal"><span class="hljs-literal">true</span></span>}).enabled(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcpConnected).onClick(() =>
{
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (!<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcpConnected)
{
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'无法发送,因为您尚未连接。'</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">return</span></span>;
}
<span class="hljs-comment"><span class="hljs-comment">// 发送数据</span></span>
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcp.send({
data: <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.textToSend
}, err => {
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (err) {
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'发送失败,原因是'</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(err);
<span class="hljs-keyword"><span class="hljs-keyword">return</span></span>;
}
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'发送成功'</span></span>;
})
})
Button(<span class="hljs-string"><span class="hljs-string">'接收'</span></span>, {stateEffect: <span class="hljs-literal"><span class="hljs-literal">true</span></span>}).enabled(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcpConnected)
Button(<span class="hljs-string"><span class="hljs-string">'断开'</span></span>, {stateEffect: <span class="hljs-literal"><span class="hljs-literal">true</span></span>}).enabled(<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcpConnected).onClick(() =>
{
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcp.close(err => {
<span class="hljs-keyword"><span class="hljs-keyword">if</span></span> (err)
{
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'关闭套接字失败,原因是'</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(err);
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcpConnected = <span class="hljs-literal"><span class="hljs-literal">true</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">return</span></span>;
}
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'关闭套接字成功。'</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcpConnected = <span class="hljs-literal"><span class="hljs-literal">false</span></span>;
});
})
}
TextInput({text: <span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.textToSend}).onChange(textToFinish =>
{
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.textToSend = textToFinish;
})
}.justifyContent(FlexAlign.Start)
}.height(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>).onAppear(() =>
{
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcp.on(<span class="hljs-string"><span class="hljs-string">'connect'</span></span>, () =>
{
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'连接成功'</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcpConnected = <span class="hljs-literal"><span class="hljs-literal">true</span></span>;
});
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcp.on(<span class="hljs-string"><span class="hljs-string">'close'</span></span>, () =>
{
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'连接断开'</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcpConnected = <span class="hljs-literal"><span class="hljs-literal">false</span></span>;
})
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.tcp.on(<span class="hljs-string"><span class="hljs-string">'message'</span></span>, value =>
{
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> messageView = <span class="hljs-string"><span class="hljs-string">''</span></span>;
<span class="hljs-keyword"><span class="hljs-keyword">for</span></span> (<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> i: number = <span class="hljs-number"><span class="hljs-number">0</span></span>; i < value.message.byteLength; i++)
{
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> uint8Array = <span class="hljs-keyword"><span class="hljs-keyword">new</span></span> <span class="hljs-built_in"><span class="hljs-built_in">Uint8Array</span></span>(value.message)
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> messages = uint8Array[i]
<span class="hljs-keyword"><span class="hljs-keyword">let</span></span> message = <span class="hljs-built_in"><span class="hljs-built_in">String</span></span>.fromCharCode(messages);
messageView += message;
}
<span class="hljs-keyword"><span class="hljs-keyword">this</span></span>.connectStatus = <span class="hljs-string"><span class="hljs-string">'收到消息:'</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(messageView) + <span class="hljs-string"><span class="hljs-string">',远端信息:'</span></span> + <span class="hljs-built_in"><span class="hljs-built_in">JSON</span></span>.stringify(value.remoteInfo);
});
})
}
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
下面是Qt服务端的程序示例(并不能直接运行哦,仅供参考):
#ifndef SERVERPROCEDURE_H
#define SERVERPROCEDURE_H
#include <QSqlDatabase>
#include <QTcpServer>
#include “…/procedure.h”
#include “…/procedurewidget.h”
class ProcedureController;
class ServerProcedure : public Procedure
{
Q_OBJECT
public:
explicit ServerProcedure( QObject* parent = Q_NULLPTR );
<span class="hljs-keyword"><span class="hljs-keyword">void</span></span> exec( <span class="hljs-keyword"><span class="hljs-keyword">void</span></span> ) Q_DECL_OVERRIDE;
signals:
void yieldCritical( const QString& title, const QString& message ) const;
void yieldWarning( const QString& title, const QString& message ) const;
void listenResult( bool listenResult );
private slots:
void onNewConnection( void );
void onReadyRead( void );
private:
ProcedureController* m_procedureController;
QSqlDatabase m_db;
QTcpServer m_tcpServer;
};
#endif // SERVERPROCEDURE_H
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
#include <QTcpSocket>
#include <QCommandLineParser>
#include <TaskModel>
#include <codechecks.h>
#include “…/procedurecontroller.h”
#include “serverprocedure.h”
ServerProcedure::ServerProcedure( QObject* parent )
: Procedure( parent ), m_procedureController( qobject_cast<ProcedureController*>( parent ) )
{
}
void ServerProcedure::exec( void )
{
QStringList arguments = m_arguments;
arguments.prepend( “ServerProcedure” );
// 执行脚本
QCommandLineParser parser;
QCommandLineOption listenOption( QStringList( ) << “l” << “listen”,
tr( “Listen to port.” ), “listenPort” );
parser.addOption( listenOption );
parser.parse( arguments );
bool listenSuccessful = false;
if ( parser.isSet( listenOption ) )
{
const QString& portStr = parser.value( listenOption );
bool ok = false;
quint16 port = portStr.toUShort( &ok );
if ( ok )
{
connect( &m_tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()) );
ok = m_tcpServer.listen( QHostAddress::Any, port );
CONDITION_CHECK_RETURN_VOID( ok, QStringLiteral( “监听任意地址的端口%d失败” ).arg( port ),
QStringLiteral( “系统” ), “2024-03-24” );
listenSuccessful = true;
}
}
emit listenResult( listenSuccessful );
}
void ServerProcedure::onNewConnection( void )
{
QTcpSocket* clientSocket = m_tcpServer.nextPendingConnection( );
if ( clientSocket == Q_NULLPTR ) return;
connect( clientSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()) );
}
void ServerProcedure::onReadyRead( void )
{
QTcpSocket* clientSocket = qobject_cast<QTcpSocket*>( sender( ) );
if ( clientSocket == Q_NULLPTR ) return;
QByteArray content = clientSocket->readAll( );
qDebug( ) << QString::fromUtf8( content );
QString receivedData = QStringLiteral( “服务端收到啦。” );
clientSocket->write( receivedData.toUtf8( ) );
}
<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>
在使用HarmonyOS鸿蒙Next通过ArkTS API 9编写单页面程序与C++ Qt服务端进行通信时,你可以通过以下步骤实现:
-
确定通信协议:首先定义好客户端(ArkTS)与服务器(C++ Qt)之间的通信协议,如HTTP、WebSocket或自定义TCP/IP协议。
-
ArkTS端实现:在ArkTS中使用网络请求API(如Fetch API)或WebSocket API来发送请求到C++ Qt服务端。处理返回的数据并更新UI。
-
C++ Qt服务端实现:在Qt中设置相应的网络服务器(如QHttpServer、QTcpServer),根据协议解析ArkTS发送的请求,并返回相应数据。
-
测试与调试:确保网络通畅,并在两个环境中进行充分的测试以验证通信的正确性。
如果问题依旧没法解决请加我微信,我的微信是itying888。