鸿蒙上实现多人聊天功能
本样例是基于即时通讯(简称 IM)服务实现的 OpenHarmony 应用。
该应用允许两人或多人使用互联网即时地传递文字、图片、文件、语音、emoji 等讯息,可应用于各类聊天场景,为人们带来更加及时高效的通讯体验。
此外即时通讯平台具备较高的定制化特点,适用于多种行业,客户可以根据自己的需求来定制,实现即时通讯的内部私有化。
设备端: DAYU200(RK3568)开发板 OpenHarmony 3.1 release 系统
即时通讯实现原理
如下图:
想要实现多个设备之间的无障碍即时通讯,需要多台终端设备、终端应用和服务器配合一起使用。
首先应该将终端应用安装到终端设备上,用户通过应用向服务器申请注册账号。
随后,用户可以通过账号进行查找,添加其他好友,并向好友发送文字、图片、文件、语音、emoji 等讯息。
用户发送的讯息会先送达服务器,由服务器判断其好友的状态(离线/在线),然后选择发送或者暂时缓存消息等操作。
最后,好友的终端应用接收到消息。
实现即时通讯的设备需求: 安装应用的终端设备、网络环境和云端服务器。
前提条件: 用户将应用安装在终端设备上,并且拥有注册账号,且需要通讯的用户也成功注册了账号并且添加了好友。
通讯原理: 用户在安装了应用的终端设备上编辑信息(文字、图片、文件、语音、emoji 等),通过网络将消息发送至云端服务器。
当对方用户在线时,云端服务器将把消息推送给对方用户,对方用户安装了应用的终端设备也将接收到信息。当对方用户不在线时,信息将被暂时缓存在云端服务器。
4 步实现多人即时通讯
① 通讯功能是通过 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);//清空字符串②文件消息的发送与接收
②文件消息的发送与接收
文件转发是即时通讯办公场景下的重要功能。样例中的文件功能支持文件消息的发送、接收和下载。
用户通过点击聊天界面的"+"按钮,选择"文件"按钮,完成本地文件的浏览,随后可以选择是否将文件发送给好友。
这个功能的实现包括三个步骤: 文件的选择 文件上传到服务器 文件的接收
文件上传: //文件的选择 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, }); });
③语音消息的发送与接收
用户通过点击聊天界面的录制按钮,完成语音的录制,随后可以选择是否将语音发送给好友。
这个功能的实现包括三个步骤: 语音的录制 语音上传到服务器 语音的接收
图片消息的发送与语音消息的发送步骤相同,文章中不再赘述。 //语音录制 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) });
④emoji 消息的发送与接收
emoji 是即时通讯软件不可缺少的一部分,可以更加生动地表现用户的聊天情感。
在样例中,用户通过点击聊天界面的 emoji 按钮,即可找到目前应用内支持的所有样式的 emoji,随后可以选择具体样式并将其发送给好友。 //引入emoji第三方组件 //第三方组件的展示布局 所有表情
即时通讯功能总结
本样例是基于 OpenHarmony 实现的即时通讯应用,目前已经支持文字、图片、文件、语音、emoji 等讯息的快速发送与接收。
除此之外还实现了好友的添加与删除、黑名单、安全登录、私聊/群聊、个人信息设置(二维码/头像等)等功能的全方面支持。
代码地址:https://gitee.com/isrc_ohos/instant-message_ohos
南阳高山深处发现保存较好的楚国古城,或是楚国的发源地丹阳丹江发源于陕西省秦岭南麓,然后,一路向东南蜿蜒流去。经武关流入淅川县境内。武关是当年的天下名关,是进入秦国的咽喉要道。出了武关,向东走,就进入了楚国的疆域。据史记记载,楚国只是周武
高山精灵梅花鹿四方顶景区介绍四方顶景区位于吉林省辉南县境内,是吉林龙湾群国家森林公园主要景区之一,也是区域内最典型独特的玄武岩质火山台地。四方顶子最高处称为云台,海拔1233米,云台面积约30公
西安阿房宫景区,耗资2亿元建成,仅仅13年就被夷为平地西安是许多人向往的旅游目的地,这里有回民街的美食小吃,这里有秦始皇兵马俑的千年历史,这里有陕西历史博物馆的上百万件文物,这里有数不尽的古迹。自西周王朝在此建都,至今这里有3000多
努力工作的意义是什么?幸福是什么?人生的目的是什么?1。幸福就是人的生理心理思想需求的满足感。2。人生的目的就是幸福地活着。说明(1)人生一辈子,无论是追求自由爱情友情事业,还是追求金钱权势名誉地位等,
是权利还是义务?这是个问题在世锦赛人员特别是内线人员捉襟见肘的时候,杜峰又想起来临时增调人员,但是这次有点傻眼,因为吃了一通闭门羹。山东内线小鲨鱼陶汉林拒绝了杜峰的邀请,原因是年纪大了体力不支。辽宁后卫赵继
巨婴到底多可怕?辽宁女孩火出圈16岁吃饭靠人喂,每日消费过万前不久,在无锡街头,上演了一幕让无数家长心寒的场景。一名8岁的男孩,稳稳当当地坐在电动车后座打游戏,而他的母亲在前面辛苦地推车。原来是在回家途中,电动车意外没电了,妈妈无奈只能推车
停发工资,是多么可怕的信号?千万别允许自己成为下一个国美员工明目张胆违反劳动法和劳动合同法,人社劳动监管不管?!今天,国美停发工资冲上热搜,对于12月底前仅为雇员缴纳社保,同时停发工资此事,是否有违反劳动法的嫌疑?其雇员有何手段维护自身权益
手脚冰冷腰疼胃部凉痛这些小毛病该如何调理?最近又降温啦有的人会出现手脚冰冷肩关节发凉腰疼胃不舒服等问题这些该如何调理?快来一起看看吧1hr因受凉导致肩关节发凉疼痛该如何缓解?逯俭北京中医药大学东直门医院针灸二区主任医师如果
鏖战39分钟,压哨绝杀!格兰特尽显大心脏,西部第一轰然倒下开拓者客战太阳早在新赛季正式开打之前,几乎所有人都以为,类似于立志卫冕冠军的勇士,还有重新点燃斗志的洛城双雄,以及上赛季的黑马灰熊,会迅速展现各自无与伦比的即战力。但谁能想到,随着
扔掉方能一身轻,潇潇洒洒过一生那天遇见一邻居,我俩坐在楼前,闲聊。小伙子说,詹老,不知咋的,最近这几年,总觉得日子过得太沉重,每天活得特别累!我说是吗?你说这话,我相信。不过,要想活得不沉重,不累,除了客观因素
醉后不知天在水,满船清梦压星河你说那感觉美不美?往年,一到蟹肥叶黄的11月,便是端杯好时节。遇上三两个知己,或碰到四五个好友,便可以在街角处,找张小桌一围,然后喝点靠杯酒。要是聚会人多,那就要郑重一点,至少要有个僻静的房间,位置