zoukankan      html  css  js  c++  java
  • 聊天室(C++客户端+Pyhton服务器)3.群功能添加

    创建群

    数据库 group_table(user, name)

    grpuser_table(grpname,user)

    按下添加群按钮

    // 创建群组
    void CUserDialog::OnBnClickedCrtgrp()
    {
    // 1. 获取群的名字,和自己的名字
    UpdateData(TRUE);
    CString MyName = MainDialog->m_UserName;

    // 2. 组合结构体
    SEND_INFO SendInfo = { TYPE::CRTGRP, GetSafeHwnd() };
    memcpy(SendInfo.CrtGrpInfo.Creator, MyName.GetBuffer(), MyName.GetLength() * 2);
    memcpy(SendInfo.CrtGrpInfo.GrpName, m_AddEdit.GetBuffer(), m_AddEdit.GetLength() * 2);

    // 3. 发送消息到服务器
    ::SendMessage(MainDialog->GetSafeHwnd(), UM_SEND_MSG, (WPARAM)& SendInfo, sizeof(SendInfo));
    }

    所需结构体

    // 5. 建群消息
    typedef struct _CRTGRP_INFO
    {
    WCHAR Creator[32];
    WCHAR GrpName[32];
    } CRTGRP_INFO, * PCRTGRP_INFO;

     

    服务器接收处理并返回

    # 创建群组的消息
       def on_crtgrp(self, client, message):
           # 解包获取发送来的数据
           hwnd, creator, grpname = struct.unpack("i64s64s", message[:132])
           # 获取群主和群名
           creator = creator.decode("UTF16").strip("x00")
           grpname = grpname.decode("UTF16").strip("x00")
           # 查看当前的群是否已经存在
           if self.mysql.select("SELECT * FROM group_table WHERE name='%s';" % grpname):
               client.send(struct.pack("iii40s", Type.CRTGRP.value, hwnd, 0, "群组已存在".encode("UTF16")))
           else:
               # 如果不存在,向数据库中添加一组数据
               self.mysql.exec("INSERT INTO group_table(name,user) VALUE('%s','%s')" % (grpname, creator))
               # 直接将群主添加到群内
               self.mysql.exec("INSERT INTO grpuser_table VALUE('%s', '%s');" % (grpname, creator))
               # 将新建结果进行返回,返回额群名,方便进行添加
               client.send(struct.pack("iii40s", Type.CRTGRP.value, hwnd, 1, grpname.encode("UTF16")))

     

    客户端主窗口接收到并转发

    // 响应建群
    case TYPE::CRTGRP:
    {
    // 对于注册是否成功的消息,wParam 没有用,lParam 指向 RECV_STATE
    ::SendMessage(RecvInfo->hWnd, UM_RECV_CRTGRP, NULL, (LPARAM)& RecvInfo->RecvState);
    break;
    }

    // 建群
    afx_msg LRESULT CUserDialog::OnUmRecvCrtgrp(WPARAM wParam, LPARAM lParam)
    {
    // 将 lParam 转换成对应的的结构
    PRECV_STATE RecvState = (PRECV_STATE)lParam;

    // 判断是否添加成功
    if (RecvState->IsSuccess)
    {
    // 如果成功执行直接添加到列表
    m_TreeCtrl.InsertItem(&RecvState->MsgInfo[0], m_GrpNode);
    return 0;
    }

    // 不成功执行这里
    MessageBox(RecvState->MsgInfo);
    return 0;
    }

     

    添加群组

     

    按下添加按钮

    // 添加群组
    void CUserDialog::OnBnClickedAddgrp()
    {
    // 1. 获取群的名字,和自己的名字
    UpdateData(TRUE);
    CString MyName = MainDialog->m_UserName;

    // 2. 组合结构体
    SEND_INFO SendInfo = { TYPE::ADDGRP, GetSafeHwnd() };
    memcpy(SendInfo.AddGrpInfo.User, MyName.GetBuffer(), MyName.GetLength() * 2);
    memcpy(SendInfo.AddGrpInfo.Group, m_AddEdit.GetBuffer(), m_AddEdit.GetLength() * 2);

    // 3. 发送消息到服务器
    ::SendMessage(MainDialog->GetSafeHwnd(), UM_SEND_MSG, (WPARAM)& SendInfo, sizeof(SendInfo));
    }

    所需结构体

    // 6. 添加群组的消息
    typedef struct _ADDGRP_INFO
    {
    WCHAR User[32];
    WCHAR Group[32];
    } ADDGRP_INFO, * PADDGRP_INFO;

    服务器

    # 发送添加群组的消息
       def on_addgrp(self, client, message):
           # 解包获取发送来的数据
           hwnd, user, grpname = struct.unpack("i64s64s", message[:132])
           # 获取目标用户和群组的名字
           user = user.decode("UTF16").strip("x00")
           grpname = grpname.decode("UTF16").strip("x00")
           # 是否有这样一个群
           if not self.mysql.select("SELECT * FROM group_table WHERE name='%s';" % grpname):
               client.send(struct.pack("iii40s", Type.ADDGRP.value, hwnd, 0, "群组不存在".encode("UTF16")))
           # 是否已经在群内了
           elif self.mysql.select("SELECT * FROM grpuser_table WHERE grpname='%s' AND user='%s';" % (grpname, user)):
               client.send(struct.pack("iii40s", Type.ADDGRP.value, hwnd, 0, "不能重复添加群组".encode("UTF16")))
           # 添加成功的情况
           else:
               # 向数据库中添加一组信息 (群名,用户名)
               self.mysql.exec("INSERT INTO grpuser_table VALUE('%s', '%s');" % (grpname, user))
               # 在返回的消息处填写添加的好友的名称
               client.send(struct.pack("iii40s", Type.ADDGRP.value, hwnd, 1, grpname.encode("UTF16")))

       # 发送群聊消息
     

    客户端

        // 响应加群
    case TYPE::ADDGRP:
    {
    // 对于注册是否成功的消息,wParam 没有用,lParam 指向 RECV_STATE
    ::SendMessage(RecvInfo->hWnd, UM_RECV_ADDGRP, NULL, (LPARAM)& RecvInfo->RecvState);
    break;
    }

    // 加群
    afx_msg LRESULT CUserDialog::OnUmRecvAddgrp(WPARAM wParam, LPARAM lParam)
    {
    // 将 lParam 转换成对应的的结构
    PRECV_STATE RecvState = (PRECV_STATE)lParam;

    // 判断是否添加成功
    if (RecvState->IsSuccess)
    {
    // 如果成功执行直接添加到列表
    m_TreeCtrl.InsertItem(&RecvState->MsgInfo[1], m_GrpNode);
    return 0;
    }

    // 不成功执行这里
    MessageBox(RecvState->MsgInfo);
    return 0;
    }


     

    更新群组列表

     

     

    初始化的时候发送请求


    // 发消息给服务器更新好友列表
    SEND_INFO SendInfo1 = { TYPE::UPDATEGRP, GetSafeHwnd() };
    memcpy(SendInfo1.UpdateGrpInfo.Name, MainDialog->m_UserName.GetBuffer(),
    MainDialog->m_UserName.GetLength() * 2);

    // 通过主窗口向服务器发送消息
    ::SendMessage(MainDialog->GetSafeHwnd(), UM_SEND_MSG, (WPARAM)& SendInfo1, sizeof(SendInfo));

    相关的结构体

    // 7. 更新群组列表
    typedef struct _UPDATEGRP_INFO
    {
    WCHAR Name[32];
    } UPDATEGRP_INFO, * PUPDATEGRP_INFO;

     

    服务器接收到处理之后发回来

     # 发送群聊消息
       def on_updategrp(self, client, message):
           # 解包获取发送来的数据
           hwnd, user = struct.unpack("i64s", message[:68])
           # 获取目标用户名
           user = user.decode("UTF16").strip("x00")
           # 查询目标用户的所有群组名称
           friends = self.mysql.select("SELECT grpname FROM grpuser_table WHERE user='%s';" % user)
           # 通过一个循环,将每一个群族名发送到客户端
           for name in friends:  # (("1"),("2"),("3"))
               client.send(struct.pack("ii64s", Type.UPDATEGRP.value, hwnd, name[0].encode("UTF16")))
               time.sleep(0.1)

         

     

     

    客户端接受到消息之后处理

    主窗口响应,转发

    // 响应加群
    case TYPE::UPDATEGRP:
    {
    // 对于注册是否成功的消息,wParam 没有用,lParam 指向 RECV_STATE
    ::SendMessage(RecvInfo->hWnd, UM_RECV_UPDATEGRP, NULL, (LPARAM)& RecvInfo->UpdateGrpInfo);
    break;
    }

    转发到的地方

    // 更新群组
    afx_msg LRESULT CUserDialog::OnUmRecvUpdategrp(WPARAM wParam, LPARAM lParam)
    {
    // 转换成对应的结构体
    PUPDATEGRP_INFO GrpInfo = (PUPDATEGRP_INFO)lParam;

    // 【添加到树控件的好友节点中,收到的消息前两个字节会是 ff fe】
    m_TreeCtrl.InsertItem(&GrpInfo->Name[1], m_GrpNode);

    return 0;
    }

     

     

     

     

    群聊

    双击树控件的时候跳出群聊框 参考好友

    点击发送发送信息

    void CGrpDialog::OnBnClickedSendmsg()
    {
    // 1. 获取编辑框(聊天记录 + 输入框)的内容
    UpdateData(TRUE);

    // 2. 将用户的输入拼接到聊天框的结尾
    SYSTEMTIME SystemTime = { 0 };
    GetSystemTime(&SystemTime);
    CString TimeString;
    TimeString.Format(L"%d:%d:%d %d:%d:%d", SystemTime.wYear,
    SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour,
    SystemTime.wMinute, SystemTime.wSecond);
    m_ShowEdit += L"你 说(" + TimeString + L"):   "
    + m_InputEdit + L" ";

    // 3. 将输入的数据进行组合,句柄是无效的
    CString MyName = MainDialog->m_UserName;
    SEND_INFO SendInfo = { TYPE::GRPMSG, NULL };
    memcpy(SendInfo.GrpMsg.From, MyName.GetBuffer(), MyName.GetLength() * 2);
    memcpy(SendInfo.GrpMsg.To, m_TargetName.GetBuffer(), m_TargetName.GetLength() * 2);
    memcpy(SendInfo.GrpMsg.Msg, m_InputEdit.GetBuffer(), m_InputEdit.GetLength() * 2);

    // 4. 通过主窗口发送到服务器
    ::SendMessage(MainDialog->GetSafeHwnd(), UM_SEND_MSG, (WPARAM)& SendInfo, sizeof(SEND_INFO));

    // 5. 将组合的数据显示到输出框
    m_InputEdit = "";
    UpdateData(FALSE);
    }

     

    所需结构体

    // 8. 群组消息
    using GRPMSG_INFO = FRDMSG_INFO;
    using PGRPMSG_INFO = PFRDMSG_INFO;

     

    服务器接收到信息处理并发送回来

        def on_grpmsg(self, client, message):
           # 解包获取发送来的数据
           hwnd, fromname, to, msg = struct.unpack("i64s64s200s", message[:332])
           # 获取群组的名字
           fromname = fromname.decode("UTF16").strip("x00")
           to = to.decode("UTF16").strip("x00")
           msg = to.msg("UTF16").strip("x00")

           # 查看当前群组的所有用户
           users = self.mysql.select("SELECT user FROM grpuser_table WHERE grpname='%s'" % to)
           # 遍历查到的所有用户,
           for user in users:
               # 判断是否在线,并且排除自己
               if user[0] in self.dict_client and self.dict_client[user[0]] != client:
                   self.dict_client[user[0]].send(b"x08x00x00x00" + message)

     

     

    客户端

    case TYPE::GRPMSG:
    {
    // 1. 获取群组的名字,To 保存的是发送给具体的群
    CString Name = RecvInfo->FrdMsg.To;

    // 2. 判断有没有窗口标题为这个名字的窗口
    if (WindowsMap.find(Name) == WindowsMap.end())
    {
    // 3. 如果没有这个窗口就进行创建
    CGrpDialog* dialog = new CGrpDialog(Name);
    dialog->Create(IDD_DIALOG3, this);
    // 3.2.2 设置群聊天窗口的窗口名为群名
    dialog->SetWindowText(Name);
    dialog->ShowWindow(SW_SHOWNORMAL);
    // 3.2.3 将新创建的窗口添加到字典中
    WindowsMap[Name] = dialog;
    }

    // 4. 发送给对应的窗口
    ::SendMessage(WindowsMap[Name]->GetSafeHwnd(), UM_RECV_GRPMSG, NULL, (LPARAM)& RecvInfo->FrdMsg);
    break;
    }
    afx_msg LRESULT CGrpDialog::OnUmRecvGrpmsg(WPARAM wParam, LPARAM lParam)
    {
    // 0. 转换成对应的结构体
    PGRPMSG_INFO Msg = (PGRPMSG_INFO)lParam;

    // 1. 获取编辑框(聊天记录)的内容
    UpdateData(TRUE);

    // 2. 将用户的输入拼接到聊天框的结尾
    SYSTEMTIME SystemTime = { 0 };
    GetSystemTime(&SystemTime);
    CString TimeString;
    TimeString.Format(L"%d:%d:%d %d:%d:%d", SystemTime.wYear,
    SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour,
    SystemTime.wMinute, SystemTime.wSecond);
    m_ShowEdit += CString(Msg->From) + L" 说(" + TimeString + L"):   "
    + Msg->Msg + L" ";

    // 3. 将组合的数据显示到输出框
    UpdateData(FALSE);
    return 0;

     

  • 相关阅读:
    XX宝面试题——css部分
    XX宝面试题——JS部分
    Struts、JSTL标签库的基本使用方法
    JavaScript:学习笔记(10)——XMLHttpRequest对象
    SpringBoot学习笔记:单元测试
    SpringMVC:学习笔记(11)——依赖注入与@Autowired
    SpringBoot学习笔记:动态数据源切换
    Django:学习笔记(9)——视图
    Django RF:学习笔记(8)——快速开始
    CNN学习笔记:批标准化
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11031519.html
Copyright © 2011-2022 走看看