立即注册 登录
即时通讯网 返回首页

wx_jQ6PE5fi的个人空间 http://www.52im.net/?24842 [收藏] [复制] [RSS]

日志

万行代码从零构建跨平台IM

热度 1已有 549 次阅读2021-09-05 12:33 | 跨平台IM, 暗黑模式, 水印, 桌面客户端

先看效果图:


日常办公使用的桌面IM是基于Electron进行开发的,在日常使用中存在卡顿,进程暂用多以及弱网情况下加载缓慢等问题。因此萌生了能不能自己从零使用Qt重新构建跨平台的桌面客户端替代日常的使用呢?

1. 可行性

毕竟是自己个人业余项目,首先考察肯定是可行性,前面提到的要改造的客户端是基于Electron自然有网页版本,利用网页版可以拿到https请求的request以及response。而https的验证是通过浏览器中的cookie等信息。还有一个IM软件都有的长链接,使用Qt可以直接使用内置的websocket即可,官方也有相关的参考demo。这样接收消息推送的长链接有了,同时也可以通过https请求会话等信息,因此是可行的。

2. 技术选型

由于需要支持跨平台的原因,而原有客户端基于Electron虽然可以跨平台但是性能以及后续优化较难进行。因此选用了Qt进行开发,Qt5.15.2已经默认支持了cmake,所以也选用cmake进行构建。

3. 长连接

使用Qt的QWebSocket即可,在完成客户端登录后直接去open即可,具体的逻辑如下,

QNetworkRequest request(m_url); request.setRawHeader("Pragma", "no-cache"); request.setRawHeader("Accept-Language", "zh,zh-CN;q=0.9,en;q=0.8,hu;q=0.7"); request.setRawHeader("Sec-WebSocket-Key", "t8ZjtY/6hV1FBto6iailxQ=="); request.setRawHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML," "like Gecko) Chrome/89.0.4389.128 Safari/537.36"); request.setRawHeader("Upgrade", "websocket"); request.setRawHeader("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits"); request.setRawHeader("Cache-Control", "no-cache"); request.setRawHeader("Connection", "Upgrade"); request.setRawHeader("Sec-WebSocket-Version", "13"); m_webSocket.open(request);

具体的请求头部可以参考websocket协议的文章。

连接上websocket之后还需要发送心跳包,以维持当前客户端的登录状态。如果断开的连接需要重新连接

connect(&m_webSocket, &QWebSocket::disconnected, this, [&]() { qDebug() << "disconnect"; qDebug() << "begin to reconnect"; QTimer::singleShot(ConstSpace::WebSocketsReconnectInterval, [&]() { App->getSessService()->checkLoginSync(App->m_loginBaseInfo); reconnect(); }); });

之后就是处理接收到不同的websocket包进行相应业务处理。当然其中还有一些websocket的握手认证则根据相应的接口实现来进行。

4. 最近会话列表的显示

会话列表的显示较为简单,使用QListView即可,当然也可以使用QListWidget如果数据量不多的话。由于获取最近会话接口是分页的,因此需要考虑滚动加载,而QListView是可以支持的,只需要实现下列逻辑即可

bool RecentListModel::canFetchMore(const QModelIndex& parent) const { // 200条最近会话 if (m_recentSessInfoList.size() == 0) return false; return rowCount() < ConstSpace::kRecentSessListMaxVal; } void RecentListModel::fetchMore(const QModelIndex& parent) { int remainder = ConstSpace::kRecentSessListMaxVal - rowCount(); int itemsToFetch = qMin(10, remainder); if (itemsToFetch <= 0) return; emit onUiFetchMoreRecentSessList(m_recentSessInfoList.nextSeq, m_recentSessInfoList.nextHasStickied); }

主要就是告诉listview在滚动在最底部的时候时候可以获取更多,以及在fetchMore获取相应的数据参入model即可。剩余的逻辑就是在会话更新是对列表进行重新排序,刷新,删除等操作。主要思路就是改变model中数据,并同时到listview即可。

5. 数据库存储

有了最近会话信息则需要处理最近会话信息的存储到数据库中,这里主要用到了sqlite。在程序启动时建立相应的表结构,在运行过程中对数据进行入口操作,在需要相应会话信息的时候从数据库中获取。访问数据库注意不能在多线程中访问,需要在单独的数据库线程进行访问。存储批量数据记得开启数据库事务。

6. 会话列表显示

采用自定义listwidget,中间每个消息是自定义的item。富文本的显示可以使用QTextEdit可以支持插入图片文字,html甚至markdown。还可以自定义textobject进行插入。功能还是很强大的。具体这里的逻辑较为复杂,也是一步一步完成,当前阶段我也是实现了纯文本以及图片的显示。还有较多类型的消息需要支持,如图文卡片消息,文件,语音,视频等。

7. 控制开发进度

虽然是自己一个人开发,但是开发流程上还是要规范化。git使用的是gitee的私有仓库,而项目管理自己使用的是jira进行项目管理,jira对于10人以下的团队是免费申请的。

以2周作为一个迭代,可以更好规划开发的进度以及优先级。

总结

兴趣是最好的驱动力。

1 推荐

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 立即注册

返回顶部