【work记录:c++web聊天服务器】实现了基本聊天功能|修复muduo服务器的部分bug|前端页面设计

日期:2025.4.21(凌晨)

学习内容:

  • 修复了muduo服务端重复利用CHAT_ONE_MSG的bug
  • 实现了基本的聊天功能

个人总结:

自上一篇之后,又摸鱼吭哧吭哧了半天。

这次实现了基本的聊天功能,先贴一张图,然后聊一下大致的改进如下:

修复muduo服务器的Msgtype

之前为了偷工减料,没有设置发送消息MSG的ACK,导致发送的消息无法判断是自己发送的还是对方发送的(其实也是可以的,只需要判断一下to_id是不是自己的id就好了),但是还是觉得要加上才算是比较完善一点的,起码看起来比较好一点(bushi

enum Msgtype
{
	LOGIN_MSG = 1,		  // 1 登录的msg
	LOGIN_MSG_ACK,		  // 2 响应登录的msg ack
	REG_MSG,			  // 3 注册的msg
	RES_MSG_ACK,		  // 4 响应注册的msg ack
	CHAT_ONE_MSG,		  // 5 跟一个人发消息的msg
	ADD_FRIEND_MSG,		  // 6 向一个人添加好友的msg
	CREAT_GROUP_MSG,	  // 7 创建一个群聊的msg
	ADD_GROUP_MSG,		  // 8 添加一个群聊的msg
	CHAT_GROUP_MSG,		  // 9 在群聊里发消息的msg
	LOGIN_NAME_MSG,		  // 10 按照用户名登录的msg
	ADD_FRIEND_MSG_ACK,	  // 11 向一个人添加好友的msg的ack
	ADD_GROUP_MSG_ACK,	  // 12 添加一个群聊的msg的ack
	CREATE_GROUP_MSG_ACK, // 13 创建一个群聊的msg的ack
	SHOW_STATUS_MSG,	  // 14 请求自己状态的msg
	SHOW_STATUS_MSG_ACK,  // 15请求自己状态的msg的ack
	CHAT_ONE_MSG_ACK,	  // 16跟一个人发送消息的msg的ack(收到消息的人会得到的ack)
};

这里贴一下Msgtype,估计后续还是会有更新。

修复了muduo服务器中ChatToOne函数:

我竟然发现之前实现的时候偷懒,直接利用了发送过来的json再发送回去。顺便把msgid也改成了新添加的CHAT_ONE_MSG_ACK。


void ChatService::ChatToOne(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
	int to_id = js["to_id"].get<int>();
	User to_user;
	to_user = _user_modle.Query(to_id);

	json response;
	response["from_id"] = js["from_id"].get<int>();
	response["from_name"] = js["from_name"].get<std::string>();
	response["to_id"] = to_id;
	response["msgid"] = CHAT_ONE_MSG_ACK;
	response["text"] = js["text"];
	response["time"] = js["time"];

	// 对方不在线
	if (to_user.GetState() == "offline")
	{
		_offline_msg_model.Insert(to_id, response.dump());
		return;

这里关于response的内容是我今天刚补上的。

另外还有上一篇中短暂提到的ShowStatus函数的修改,也在本篇中展示一下:

void ChatService::ShowStatus(const TcpConnectionPtr &conn, json &js, Timestamp time)
{
	int user_id = js["user_id"].get<int>();
	User user;
	user.SetID(user_id);

	json response;
	std::string uid = js["uid"].get<std::string>();
	Session session(conn, uid);
	ConnInsert(user_id, session);

然后后续的改动其实更多的地方在客户端多一些了。

算是大概得了解了一下客户端的写法,虽然自己还是写不出来,但是多多少少能看懂一点点了。

关于发送消息的代码:

 // 发送消息功能
      const messageInput = document.getElementById("message-input");
      const sendButton = document.getElementById("send-button");
      sendButton.addEventListener("click", sendMessage);
      messageInput.addEventListener("keydown", (e) => {
        if (e.key === "Enter" && !e.shiftKey) {
          e.preventDefault();
          sendMessage();
        }
      });

      function sendMessage() {
        const content = messageInput.value.trim();
        // console.log("111");
        if (!content || !currentChat) {
          // if (!currentChat) console.log("333");
          // if (!content) console.log("444");

          return;
        }
        // console.log("222");

        // 构造消息对象
        const message = {
          msgid: 5,
          from_name: storedUser.name,
          from_id: parseInt(storedUser.id),
          to_id: parseInt(currentChat.id),
          text: content,
          time: new Date().toLocaleString(),
        };
        const message_to_self = {
          msgid: 5,
          from_name: storedUser.name,
          from_id: parseInt(storedUser.id),
          to_id: parseInt(storedUser.id),
          text: content,
          time: new Date().toLocaleString(),
        };

        // 立即显示本地消息
        messageHandlers[16](message_to_self);

        // 发送到服务器
        ws.send(JSON.stringify(message));

        // 清空输入框
        messageInput.value = "";
      }

还有其实对我来说感觉比较新颖的是这一段(处理好友列表的内容)

// 处理好友列表
        15: (data) => {
          // console.log("收到好友列表数据:", data);
          const friendsList = document.getElementById("friends-list");
          const parsedFriends = data.friends.map((friendStr) =>
            JSON.parse(friendStr)
          );

          friendsList.innerHTML = parsedFriends
            .map(
              (friend) => `
                    <div class="list-item" data-id="${friend.id}" data-name="${
                friend.name
              }">
                        <div>
                            <div >${friend.name}</div>
                            <div style="color: #666; font-size: 0.9em">${
                              friend.state === "online" ? "在线" : "离线"
                            }</div>
                        </div>
                        <div class="status-indicator ${
                          friend.state === "online" ? "online" : ""
                        }"></div>
                    </div>
                `
            )
            .join("");

          // 添加好友点击事件
          friendsList.querySelectorAll(".list-item").forEach((item) => {
            item.addEventListener("click", () => {
              // 移除其他项的激活状态
              document
                .querySelectorAll(".list-item")
                .forEach((i) => i.classList.remove("active"));
              item.classList.add("active");

              // 更新当前聊天对象
              currentChat = {
                type: "friend",
                id: item.dataset.id,
                name: item.dataset.name,
              };

              // 更新聊天窗口标题
              document.querySelector(
                ".chat-header"
              ).textContent = `与 ${currentChat.name} 的对话`;

              // TODO: 加载历史消息
            });
          });
        },

这里对于前端的代码就不细说了,我还是小白,目前只是勉强能用的状态。

后续问题:

来说说后续:

现在首先有一个问题就是之前客户端做到的,目前还没有处理的内容:

  • 离线消息
  • 群聊
  • 申请添加好友
  • 状态刷新

现在我觉得都好麻烦啊,一想到还有前端要整就感觉好ex。自己会有时间搞定吗?还有八股要背,我目前是一点没看。

目前有一些暂时不太能想明白的就是关于这个状态刷新,虽然说websocket协议现在是长连接了,但是我的状态刷新还是得从服务端发送过来才可以呢。目前暂时考虑的一个方案就是每当自己有一个好友上线了,就会向他的好友们去发送一个状态刷新,这样就能即时知道了好友的在线情况。但是这样做我觉得又会比较的浪费性能资源,如果节约一点,可能就要考虑上用户的常聊角色,或者是最近聊天的角色,先只刷新这些好友的状态。又或者是直接从列表上删除好友的在线状态(不是,然后点开一个好友的窗口,再发送一个是否在线的请求。这样做性能上讲应该浪费的就不多了,不过代码量倒是又多了不少。

另外还有历史消息的存储,传输文件等内容,感觉还有好长的一段路要走,而且这样做的话,感觉这个项目很大程度上已经变成了一个java项目233,貌似感觉作用不是太大的样子。w先睡了吧还是,明天还有课要上,好像过两天就是小组作业了,我还啥都不知道阿巴阿巴

posted @ 2025-04-21 03:49  AdviseDY  阅读(28)  评论(0)    收藏  举报