1. 发送消息
1.1 实现逻辑分析
- 发送消息的具体实现,通过输入框右下角的发送按钮
- 输入框中发送的内容,通过网络传输给服务器
- 客户端,发送消息成功后,消息展示区中要显示刚刚发送出去的消息
1.2 具体实现
消息输入区域设置信号槽处理函数,并在构造函数中进行调用
发送文本消息实现
- 考虑会话是否选中
- 考虑文本输入内容是否为空
- 测试
设计客户端发送消息(通过netClient),实现不同消息类型的发送
- 根据消息的不同类型,填充消息内容,比对proto文件使用
message StringMessageInfo {string content = 1;//文字聊天内容
}
message ImageMessageInfo {optional string file_id = 1;//图片文件id,客户端发送的时候不用设置,由transmit服务器进行设置后交给storage的时候设置optional bytes image_content = 2;//图片数据,在ES中存储消息的时候只要id不要文件数据, 服务端转发的时候需要原样转发
}
message FileMessageInfo {optional string file_id = 1;//文件id,客户端发送的时候不用设置int64 file_size = 2;//文件大小string file_name = 3;//文件名称optional bytes file_contents = 4;//文件数据,在ES中存储消息的时候只要id和元信息,不要文件数据, 服务端转发的时候也不需要填充
}
message SpeechMessageInfo {optional string file_id = 1;//语音文件id,客户端发送的时候不用设置optional bytes file_contents = 2;//文件数据,在ES中存储消息的时候只要id不要文件数据, 服务端转发的时候也不需要填充
}
message MessageContent {MessageType message_type = 1; //消息类型oneof msg_content {StringMessageInfo string_message = 2;//文字消息FileMessageInfo file_message = 3;//文件消息SpeechMessageInfo speech_message = 4;//语音消息ImageMessageInfo image_message = 5;//图片消息};
DataCenter中实现addMessage()方法
发送消息到显示界面逻辑
服务器逻辑实现
阶段性测试
- 细节问题修复
- 会话消息实时展示(使用信号槽的方法实现)
- 发送方头像边框去除
2. 接收消息
2.1 实现逻辑分析
- 客户端通过HTTP向服务器发送消息
- 服务器根据HTTP发送的消息,也就是根据两个用户对话(单聊)还是多个用户对话(群聊)判断,同时根据chatSessionId判断是哪个用户发起的对话
- 服务器构建好消息后,将这个消息通过websocket转发给该会话中的用户
消息加载与处理逻辑
- 需求分析
- 消息慢加载机制
- 减少初始加载时候的开销,只有当用户点击某个会话的时候,才会加载会话的消息列表
- 简单来说,用户如果没有点击某个会话,那么在本地hash数据结构中,就不会存在该会话的消息列表
- websocket实时消息接收
- 如果当客户端通过websocket收到某个会话的新消息时,如果该会话的消息列表没有提前加载到本地,直接在本地保存不太方便
- 新消息直接通过服务端返回完整的消息列表获取,而不是单独存储新的消息
- 消息慢加载机制
- 加载会话列表逻辑
- 检查该会话的
chatSessionId
是否已经在本地hash
数据结构中存在 - 如果不存在,则通过网络请求从服务器加载该会话的完整消息列表
- 将该消息列表存储在本地的
hash
中(QHash<QString, QList<Message>>
) - 然后,展示该会话的消息列表
- 检查该会话的
- websocket收到新消息逻辑
- WebSocket 返回的消息数据已经包含该会话的完整消息列表,因此不需要单独存储这条新消息
- 直接将收到的完整消息列表,替换或更新本地
hash
中对应会话的消息列表 - 更新后的消息列表就可以直接用于展示
- 新消息到达但是用户没有查看
- 将最新一条消息设置为该会话的“消息预览”
- 更新会话的“未读消息数”,在界面上显示未读消息提醒
信号槽更新消息列表逻辑
- NetClient收到消息:服务器通过websocket向客户端发送消息,NetClient收到消息后开始处理消息,并将其保存到本地的消息列表中
- 发送信号通知界面更新:NetClient解析完消息后,发送信号,通知前端界面更新显示区域
- 界面槽函数接收信号并更新显示:在MessageShowArea的槽函数中,收到信号后将新消息追加到显示区域中;对于自己发送的消息,在MessageEditArea中,同样会将消息添加到消息展示区中
客户端与服务连接断开时的逻辑分析
客户端与服务端连接断开出现的问题
- 客户端与服务端建立连接
- 客户端连接服务器的时候,服务器会收到一个Socket对象,并发出一个Connect信号槽
- 此时,服务器生成一个hash,并将信号槽与socket对象之间的关系进行记录,简单来说就是服务器在管理连接的过程中会将每个socket对象与其对应的处理逻辑(信号槽)关联起来
- 断开连接后
- 客户端与服务器断开连接后,服务器相应的socket对象失效,也就是这个socket对象不再代表一个有效连接
- 尽管该socket已经失效,但是服务器中仍然保留里信号槽与之前socket对象之间的关系
- 新客户端重新连接后出现问题
- 此时如果新的客户端连接到了服务器,触发之前信号槽的回调逻辑,但是由于旧的socket对象依然是与信号槽绑定,服务器就会尝试使用已经失效的socket进行通信
- 当使用已经失效的socket进行通信的时候,自然就会导致程序错误或者不可预测的行为
解决思路
- 连接断开后,需要确保相应信号槽与旧的socket的关联及时被清除,避免野指针的出现
- 检查socket是否有效
2.2 具体实现
Protobuf分析
- 服务器推送给客户端消息类型
- 添加好友申请
- 好友申请处理
- 好友删除(双方实现互相删除)
- 创建群聊会话
- 接收消息
信号槽更新消息列表逻辑实现
消息更新逻辑测试
显示未读消息数量(收到的消息不是指定会话)
- 显示未读消息逻辑
- 构建会话Item(SessionItem)的时候
- 更新最后一条消息(updateLastMessage)时需要显示未读消息逻辑
- 点击未读消息active的时候,需要将未读消息清空
测试服务端
- 通过点击按钮,控制websocket服务器,给客户端发送消息
补充:客户端与服务器之间的连接断开时的实现(逻辑分析参2.1)
- 启动的时候释放旧的socket
服务端Bug解决(测试客户端书写完成后无法实现向客户端发送消息)
- 问题复现
- 点击按钮后信号正常进行了发送
- 添加日志,判断连接该信号的信号槽是否出现错误
- 结果:添加日志后重新运行,添加的日志没有按照正常逻辑打印
- 继续检查逻辑,打开测试网络逻辑,打印具体日志
- 结论:该处无错误
- 检查websocket初始化时测试连接是否成功时的逻辑判断错误,从而导致下述日志不打印而直接返回的情况
- 解决文件加载失败与头像资源加载问题
- 修改后加载资源和头像资源获取成功
- 问题:客户端仍然是没有显示出未读消息,进一步考虑客户端处理响应出现错误
- 分析是否updateLastMessage逻辑出现错误
- 检查信号是否正确发送
- 结论:信号没有正常发送,分析信号没有正常发送的原因
- 进一步检查信号槽连接是否正确
- 再次梳理NetClient
- 修复未读消息显示错误
- 添加检查日志,强制刷新内容
- 结论:逻辑都可以正常运行,但是界面没有正常更新
- 继续检查更新后的文本text内容是否错误
测试