【Qt聊天室客户端】聊天界面功能

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 实现逻辑分析

  1. 客户端通过HTTP向服务器发送消息
  2. 服务器根据HTTP发送的消息,也就是根据两个用户对话(单聊)还是多个用户对话(群聊)判断,同时根据chatSessionId判断是哪个用户发起的对话
  3. 服务器构建好消息后,将这个消息通过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内容是否错误

 

 测试

猜你喜欢

转载自blog.csdn.net/gma999/article/details/143175308