一、简介
本样例是基于即时通讯(Instant messaging,简称IM)服务实现的OpenAtom OpenHarmony(简称“OpenHarmony”)应用,允许两人或多人使用互联网即时地传递文字、图片、文件、语音、emoji等讯息,可应用于各类聊天场景,为人们带来更加及时高效的通讯体验。
此外即时通讯平台具备较高的定制化特点,适用于多种行业,客户可以根据自己的需求来定制,实现即时通讯的内部私有化。
设备端:DAYU200(RK3568)开发板,OpenHarmony 3.1 release系统。
二、即时通讯实现原理
想要实现多个设备之间的无障碍即时通讯,需要多台终端设备、终端应用和服务器配合一起使用。首先应该将终端应用安装到终端设备上,用户通过应用向服务器申请注册账号。随后,用户可以通过账号进行查找,添加其他好友,并向好友发送文字、图片、文件、语音、emoji等讯息。用户发送的讯息会先送达服务器,由服务器判断其好友的状态(离线/在线),然后选择发送或者暂时缓存消息等操作。最后,好友的终端应用接收到消息。
实现即时通讯的设备需求:安装应用的终端设备、网络环境和云端服务器。
前提条件:用户将应用安装在终端设备上,并且拥有注册账号,且需要通讯的用户也成功注册了账号并且添加了好友。
通讯原理:用户在安装了应用的终端设备上编辑信息(文字、图片、文件、语音、emoji等),通过网络将消息发送至云端服务器。当对方用户在线时,云端服务器将把消息推送给对方用户,对方用户安装了应用的终端设备也将接收到信息。当对方用户不在线时,信息将被暂时缓存在云端服务器。
三、4步实现多人即时通讯
(1)
通讯功能通讯功能是通过TCP协议实现的,我们将通讯接口connect()、send()、receive()的实现放置在CPP文件中,通过NAPI的方式对JS层暴露接口。
•
connect():客户端和服务器建立连接;
•
send():消息发送功能
•
receive():消息接收功能;
建立
TCP
连接
if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
napi_create_int32(env, 0, &result);
} else {
napi_create_int32(env, 1, &result);
OH_LOG_INFO(LOG_APP,"C++
接收线程启动
");
startRec();
}
发送消息
if(send(sock_cli, data, strlen(data),0) == -1) {
OH_LOG_INFO(LOG_APP,"zjf == send() : -1");
napi_create_int32(env, 0, &result);
} else {
OH_LOG_INFO(LOG_APP,"zjf == send() : !-1");
napi_create_int32(env, 1, &result);
}
接收消息
getStep(queue0,sharedMessage);
取出一条消息
const char *c_s=sharedMessage.c_str();
换为
char*
形式处理
napi_value result;
napi_create_string_utf8(env, c_s, sharedMessage.length(), &result);
std::string().swap(sharedMessage);
清空字符串
(2)
文件消息的发送与接收
文件转发是即时通讯办公场景下的重要功能。样例中的文件功能支持文件消息的发送、接收和下载。用户通过点击聊天界面的“+”按钮,选择“文件”按钮,完成本地文件的浏览,随后可以选择是否将文件发送给好友。这个功能的实现包括三个步骤:(1)文件的选择;(2)文件上传到服务器;(3)文件的接收。
• 文件上传
//
文件的选择
let file1 = {filename: this.$app.$def.uid +'-'+ FILE_URL, name: 'file', uri: FILE_URL, type:fileType }
let fileId = this.guid();
let data = {};
let header = { "filename": this.selectedFileName.toString()};
//
文件上传到服务器
request.upload({ url: "http://" + this.$app.$def.ip +"/file/fileUpload?fileSignature=" + fileId + "&uid=" +this.$app.$def.uid + "&fileType=" +this.$app.$def.chatData[this.idx].unRead, header: header, method: "POST", files: [file1], data: [data] }).then((data) => {
uploadTask = data;
uploadTask.on('headerReceive', function callback(headers){
_this.socketSendFile(fileId, timestamp);
});
}).catch((err) => {
console.error('fileSelect=====Failed to request the upload. Cause: ' + JSON.stringify(err));
})
//
文件的接收
let downloadConfig = { //
下载参数
url: fileUrl,
header: {},
enableMetered: true,
enableRoaming: true,
filePath: '/data/storage/el2/base/haps/entry/files/' + downloadFileName,
networkType: request.NETWORK_WIFI
}
request.download(downloadConfig, (err, data) => {
if (err) {
return;
}
downloadTask = data;
//
下载完成
downloadTask.on('complete', function callback() {
prompt.showToast({
message: '
下载文件成功!
',
duration: 1000,
});
});
(3)
语音消息的发送与接收
用户通过点击聊天界面的录制按钮,完成语音的录制,随后可以选择是否将语音发送给好友。这个功能的实现包括三个步骤:(1)语音的录制;(2)语音上传到服务器;(3)语音的接收。
图片消息的发送与语音消息的发送步骤相同,文章中不再赘述。
语音录制
startRecorder
(
config
,
callback
)
{
if
(
typeof
(
this
.
audioRecorder
)
!==
'undefined'
)
{
this
.
audioRecorder
.
on
(
'prepare'
,
()
=>
{
this
.
audioRecorder
.
start
()
})
this
.
audioRecorder
.
on
(
'start'
,
()
=>
{
callback
()
})
this
.
audioRecorder
.
prepare
(
config
)
}
else
{
logger
.
info
(
`${TAG} case failed, audiorecorder is null`
)
}
}
录制好的语音文件的位置
let
srcPath
=
'internal://cache/'
+
this
.
mainData
.
file
+
'.wav'
let
file1
=
{
filename
:
this
.
$app
.
$def
.
uid
+
'-'
+
this
.
mainData
.
path
,
name
:
'audio'
,
uri
:
srcPath
,
type
:
"wav"
};
语音消息发送到服务器
request
.
upload
({
url
:
myurl
,
header
:
header
,
method
:
"POST"
,
files
:
[
file1
],
data
:
[
data
]
}).
then
((
data
)
=>
{
uploadTask
=
data
;
uploadTask
.
on
(
'headerReceive'
,
function
callback
(
headers
){
_this
.
sendAudio
(
audioId
)
});
uploadTask
.
on
(
'progress'
,
function
callback
(
uploadedSize
,
totalSize
)
{
console
.
info
(
"dialogPages=====upload totalSize:"
+
totalSize
+
" uploadedSize:"
+
uploadedSize
);
});
}).
catch
((
err
)
=>
{
console
.
error
(
'dialogPages=====Failed to request the upload. Cause: '
+
JSON
.
stringify
(
err
));
})
语音的接收
let
downloadConfig
=
{
下载参数
url
:
item
.
content
.
path
,
header
:
{},
enableMetered
:
true
,
enableRoaming
:
true
,
filePath
:
filePath
,
networkType
:
request
.
NETWORK_WIFI
}
let
downloadTask
;
let
_this
=
this
request
.
download
(
downloadConfig
,
(
err
,
data
)
=>
{
if
(
err
)
{
return
;
}
downloadTask
=
data
;
下载完成
downloadTask
.
on
(
'complete'
,
function
callback
()
{
let
audio
=
{
content
:
{
path
:
filePath
}
}
_this
.
playAudio
(
audio
)
});
(4) emoji消息的发送与接收
emoji是即时通讯软件不可缺少的一部分,可以更加生动地表现用户的聊天情感。在样例中,用户通过点击聊天界面的emoji按钮,即可找到目前应用内支持的所有样式的emoji,随后可以选择具体样式并将其发送给好友。
引入
emoji
第三方组件
<
element name
=
"emojiExpression"
src
=
"../../common/components/emojiExpression/emojiExpression.hml"
></
element
>
第三方组件的展示布局
<
div id
=
"moreContainer"
if
=
"{{showFace}}"
>
<
div id
=
"moreOneLine"
>
<
div
class
=
"moreFillGap"
style
=
"flex: 1 1;"
></
div
>
<
text style
=
"left: 25fp; top: 10fp;"
>
所有表情
</
text
>
<
emojiExpression
></
emojiExpression
>
<
div
class
=
"moreFillGap"
style
=
"flex: 1 1;"
></
div
>
</
div
>
</
div
>
四、即时通讯功能总结
本样例是基于OpenHarmony实现的即时通讯应用,目前已经支持文字、图片、文件、语音、emoji等讯息的快速发送与接收。除此之外还实现了好友的添加与删除、黑名单、安全登录、私聊/群聊、个人信息设置(二维码/头像等)等功能的全方面支持。
代码地址https://gitee.com/isrc_ohos/instant-message_ohos
五、相关参考链接
样例源码https://gitee.com/isrc_ohos/instant-message_ohos
OpenHarmony知识体系工作组https://gitee.com/openharmony-sig/knowledge