客户端登录逻辑

news2024/9/20 20:34:26

将网关服务器发送的数据通过信号传递给 TcpMgr 中定义的槽函数

void LoginDialog::initHttpHandlers()
{
    // 注册获取登录回包逻辑
    m_handlers.insert(ReqId::ID_LOGIN_USER, [this](QJsonObject jsonObj){
        int error = jsonObj["error"].toInt();
        if(error != ErrorCodes::SUCCESS){
            showTip(tr("参数错误"),false);
            enableBtn(true);
            return;
        }
        auto email = jsonObj["email"].toString();

        // 发送信号通知tcpMgr发送长链接
        ServerInfo si;
        si.Uid = jsonObj["uid"].toInt();
        si.Host = jsonObj["host"].toString();
        si.Port = jsonObj["port"].toString();
        si.Token = jsonObj["token"].toString();

        m_uid = si.Uid;
        m_token = si.Token;
        qDebug()<< "email is " << email << " uid is " << si.Uid <<" host is "
                << si.Host << " Port is " << si.Port << " Token is " << si.Token;
        emit sig_connect_tcp(si); // 发送登录成功信号,开始连接tcp服务器
    });
}

这是 sig_connect_tcp 对应的槽函数,调用套接字中的 connectToHost 传入获取的 hostport 连接 tcp 聊天服务器。

// 接收到登录回包后就会调用该函数
void TcpMgr::slot_tcp_connect(ServerInfo si)
{
    qDebug() << "receive tcp connect signal";
    // 尝试连接到服务器
    qDebug() << "Connecting to server...";
    m_host = si.Host;
    m_port = static_cast<uint16_t>(si.Port.toUInt());
    m_socket.connectToHost(si.Host, m_port);
}

TcpMgr 中的构造函数中连接,当成功与聊天服务器建立连接后发出 sig_con_success(true) 信号,意味着可以发送消息。

QObject::connect(&m_socket, &QTcpSocket::connected, [&]() {
           qDebug() << "Connected to server!";
           // 连接建立后发送消息
           emit sig_con_success(true);
});

将网关服务器传来的用户 uidtoken 写入 json 对象中,发送 sig_send_data(ReqId::ID_CHAT_LOGIN, jsonString) 信号发送 tcp 请求给聊天服务器。使用槽函数来发送数据主要是为了保证数据的有序,而且槽函数保证线程安全。

void LoginDialog::slot_tcp_con_finish(bool bsuccess)
{
   if(bsuccess){
      showTip(tr("聊天服务连接成功,正在登录..."), true);
      QJsonObject jsonObj;
      jsonObj["uid"] = m_uid;
      jsonObj["token"] = m_token;

      QJsonDocument doc(jsonObj);
      QByteArray jsonString = doc.toJson(QJsonDocument::Indented);

      // 发送tcp请求给chat server
      emit TcpMgr::Getinstance()->sig_send_data(ReqId::ID_CHAT_LOGIN, jsonString);

   }else{
      showTip(tr("网络异常"), false);
      enableBtn(true);
   }
}

在向 tcp 服务器发送数据时,就需要将数据设置为大端序。使用 tlv 消息格式,使用数据流发送数据,是为了保证数据的准确性。

在这里插入图片描述

// 槽函数中有一个队列可以保证数据有序,所以使用槽函数来发送数据
// Qt中槽函数默认是在发出信号的线程中回调(直连)
void TcpMgr::slot_send_data(ReqId reqId, QByteArray dataBytes)
{
    qDebug() << "向tcp发送数据 " << dataBytes;
    uint16_t id = reqId;

    // 计算长度(使用网络字节序转换)
    quint16 len = static_cast<quint16>(dataBytes.size());

    // 创建一个QByteArray用于存储要发送的所有数据
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);

    // 设置数据流使用网络字节序(大端序)
    out.setByteOrder(QDataStream::BigEndian);

    // 写入ID和长度
    out << id << len;

    // 添加字符串数据
    block.append(dataBytes);

    // 发送数据
    m_socket.write(block);
}

判断大端序和小端序:

例如,如果当前系统为大端序,则输出结果为:

原始数据:12345678
当前系统为大端序
字节序为:12 34 56 78

如果当前系统为小端序,则输出结果为:

原始数据:12345678
当前系统为小端序
字节序为:78 56 34 12

在这里插入图片描述

如何区分本机字节序,可以通过判断低地址存储的数据是否为低字节数据,如果是则为小端,否则为大端,下面写一段代码讲述这个逻辑:

#include <iostream>
using namespace std;
// 判断当前系统的字节序是大端序还是小端序
bool is_big_endian() {
    int num = 1;
    if (*(char*)&num == 1) {
        // 当前系统为小端序
        return false;
    } else {
        // 当前系统为大端序
        return true;
    }
}
int main() {
    int num = 0x12345678;
    char* p = (char*)&num;
    cout << "原始数据:" << hex << num << endl;
    if (is_big_endian()) {
        cout << "当前系统为大端序" << endl;
        cout << "字节序为:";
        for (int i = 0; i < sizeof(num); i++) {
            cout << hex << (int)*(p + i) << " ";
        }
        cout << endl;
    } else {
        cout << "当前系统为小端序" << endl;
        cout << "字节序为:";
        for (int i = sizeof(num) - 1; i >= 0; i--) {
            cout << hex << (int)*(p + i) << " ";
        }
        cout << endl;
    }
    return 0;
}
  • 然后就是前端接收到聊天服务器发送过来的数据:

  • 先将获取的数据加入缓冲区,通过数据流读取数据,m_b_recv_pending变量判断是否接收完全,可以保证数据有序。

  • 读取完的数据会从缓冲区中移除,然后将获取的消息id和消息长度以及消息内容传入处理数据。

QObject::connect(&m_socket, &QTcpSocket::readyRead, [&]() {
       // 当有数据可读时,读取所有数据
       // 读取所有数据并追加到缓冲区
       m_buffer.append(m_socket.readAll());

       QDataStream stream(&m_buffer, QIODevice::ReadOnly);
       stream.setVersion(QDataStream::Qt_5_0);

       forever {
            // 先解析头部
           if(!m_b_recv_pending){
               // 检查缓冲区中的数据是否足够解析出一个消息头(消息ID + 消息长度)
               if (m_buffer.size() < static_cast<int>(sizeof(quint16) * 2)) {
                   return; // 数据不够,等待更多数据
               }

               // 预读取消息ID和消息长度,但不从缓冲区中移除
               stream >> m_message_id >> m_message_len;

               // 将buffer 中的前四个字节移除
               m_buffer = m_buffer.mid(sizeof(quint16) * 2);

               // 输出读取的数据
               qDebug() << "Message ID:" << m_message_id << ", Length:" << m_message_len;

           }

           // buffer剩余长读是否满足消息体长度,不满足则退出继续等待接受
           if(m_buffer.size() < m_message_len){
                m_b_recv_pending = true;
                return;
           }

           m_b_recv_pending = false;
           // 读取消息体
           QByteArray messageBody = m_buffer.mid(0, m_message_len);
           qDebug() << "receive body msg is " << messageBody ;

           m_buffer = m_buffer.mid(m_message_len);
           // 处理注册的函数
           handleMsg(ReqId(m_message_id), m_message_len, messageBody);
       }

});
sage_len);
           qDebug() << "receive body msg is " << messageBody ;

           m_buffer = m_buffer.mid(m_message_len);
           // 处理注册的函数
           handleMsg(ReqId(m_message_id), m_message_len, messageBody);
       }

});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2051980.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Superset BI封装自定义组件(堆叠柱状图)

目录 前言 封装步骤 一、创建组件文件夹 二、预设组件信息 三、使用组件 往期回顾 前言 Superset 是一个现代化的、易于使用的、轻量级的数据可视化工具&#xff0c;它允许用户通过简单的点击操作来创建和分享图表。如果你想在 Superset 中创建自定义组件&#xff0c;你…

银河麒麟服务器操作系统Kylin-Server-V10-SP3-2403-Release-20240426-x86_64安装步骤

银河麒麟服务器操作系统 Kylin-Server-V10-SP3-2403-Release-20240426-x86_64安装步骤 一、准备工作1. 下载ISO镜像2. 制作安装介质3. 设置BIOS 二、安装过程1. 启动系统2. 选择安装语言3. 选择安装配置4. 配置root密码与创建用户5. 开始安装6. 重启系统7. 同意许可协议 三、系…

mac/win视频编辑软件Premiere Pro Pr安装装下载

目录 一、PR软件介绍 1.1 PR软件概览 1.2 独特优势 1.3 应用场景 二、安装与硬件要求 2.0下载 2.1 安装步骤 2.2 硬件要求 三、使用方法 3.1 基础操作 3.2 编辑技巧 3.3 高级功能 一、PR软件介绍 1.1 PR软件概览 Adobe Premiere Pro&#xff08;简称PR&#xff0…

项目推荐——一款开源、免费、轻量级的项目工时管理系统

平台介绍 无鱼工时管理系统&#xff0c;是一款轻量级工时记录和管理工具&#xff0c;包括项目管理&#xff0c;工时上报&#xff0c;工时日报&#xff0c;工时统计等功能。 无鱼工时管理系统可通过员工工时上报的方式&#xff0c;来记录项目所花费的工时&#xff0c;帮助企业…

机器学习:一元线性回归模型

目录 前言 一、讲在前面 1.data.csv&#xff1a; 2.完整代码&#xff1a; 3.运行结果&#xff1a; 二、实现步骤 1.导入库 2.导入数据 3.绘制散点图&#xff08;这步可以省略&#xff09; 4.求特征和标签的相关系数 5.建立并训练线性回归模型 6.检验模型 7.获取线…

Element-06.案例

一.目标 实现下面这个页面&#xff0c;表格中的数据使用axois异步加载数据 二.实现步骤 首先在vue项目的views文件夹中新建一个tlias文件夹&#xff0c;用来存储该案例的相关组件。员工页面组件&#xff08;EmpView.vue&#xff09;和部门页面组件&#xff08;DeptView.vue&…

[数据集][目标检测]电力场景轭式悬架锈蚀分类数据集6351张2类别

数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;6351 分类类别数&#xff1a;2 类别名称[corrosion,good] 每个类别图片数&#xff1a; corrosion 图片数&#xff1a;310 good 图片数&#xff1a;6041 …

ChatGLM-6B 主要代码分析 RotaryEmbedding

ChatGLM-6B 主要代码分析 RotaryEmbedding flyfish 图片链接地址 传统的 Transformer 位置编码&#xff08;Positional Encoding&#xff09;被称为绝对位置编码 &#xff0c;而 Rotary Embedding 被称为相对位置编码 &#xff0c;主要是因为它们编码位置信息的方式不同&am…

python创建项目环境及项目打包

目录 创建项目环境conda创建环境常用命令创建项目虚拟环境创建虚拟环境激活虚拟环境安装第三方库 pyinstaller 打包常用参数组合 嵌入式打包下载嵌入式版本的python配置环境无参调用可完善 nuitka打包 创建项目环境 conda创建环境常用命令 conda create -n py310 python3.10.…

《学会 SpringBoot · 依赖管理机制》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

IntelliJ IDEA 集成 ShardingSphere-JDBC 访问分库分表

背景 众所周知&#xff0c;IntelliJ IDEA 是 Java 领域常用的开发工具之一&#xff0c;IDEA Ultimate&#xff08;旗舰版&#xff09;或其他例如 DataGrip 等 Intellij 平台的工具都集成了对数据库的访问能力。 但是&#xff0c;对于做了分库分表的项目&#xff0c;直接使用 …

微信支付代理商-自助提交资料源码之结算信息页面—微信支付商机版

一、支付代理上自助提交资料 一般在都在小程序完成提交 在网页中异常提示alert 但是很多小程序禁用了这个函数 并且不好看 那么久自定义一个组件每次直接调用 二、提示技术代码 function 未来之窗_VOS_通用提醒(msg){var 未来之窗内容message<cyberdiv style"font…

选择排序(直接选择排序与堆排序的比较)

选择排序 选择排序时间复杂度 1. 直接选择排序思考⾮常好理解&#xff0c;但是效率不是很好。实际中很少使用&#xff0c;思路是先进行遍历找到元最小的元素&#xff0c;然后与第一个进行交换 2. 时间复杂度&#xff1a;O&#xff08;&#xff09; 3. 空间复杂度&#…

gmapping算法核心部分

processScan函数 参考&#xff1a;https://blog.csdn.net/CV_Autobot/article/details/131058981 drawFromMotion:根据运动模型更新粒子位姿 scanMatch:进行扫描匹配 resample:重采样 逐步分解并详细解释代码 1. 获取当前扫描的相对位姿 OrientedPoint relPose reading.…

舜宇光学科技社招校招入职测评:商业推理测验真题汇总、答题要求、高分技巧

舜宇光学科技&#xff08;集团&#xff09;有限公司&#xff0c;成立于1984年&#xff0c;是全球领先的综合光学零件及产品制造商。2007年在香港联交所主板上市&#xff0c;股票代码2382.HK。公司专注于光学产品的设计、研发、生产及销售&#xff0c;产品广泛应用于手机、汽车、…

BEM架构

视频 总结&#xff1a; BEM架构&#xff1a;一个命名类的规范而已&#xff0c;说白了就是如何给类起名字使用sass的目的&#xff1a;在<style>中模块化的使用类名&#xff0c;同时减少代码数量 1、 BEM架构 &#xff08;通义灵码查询结果&#xff09; BEM (Block Ele…

【hot100篇-python刷题记录】【和为 K 的子数组】

R5-子串篇 目录 思路&#xff1a; 优化&#xff1a; tip: 代码&#xff1a; 结果&#xff1a; ps: 思路&#xff1a; 滑动&#xff0c;应该可以使用滑动窗口来解题。 貌似前缀和也可以&#xff0c;left&#xff0c;right两个指针&#xff0c;right的前缀和-left的前缀…

【学习笔记】printf中%m的含义

【学习笔记】printf中%m的含义 在有些代码中会看到如下的写法&#xff1a; printf("%m\n");printf中使用了%m来打印输出&#xff0c;那么%m又是什么意思呢&#xff1f; 其实%m 并不是在所有的 printf 实现中都通用或标准化的选项&#xff0c;而是在某些特定的编程语…

vue的markdown编辑器插件比对

vue的markdown编辑器插件比对 文章说明md-editor-v3的使用及效果展示vditor的使用及效果展示 文章说明 文章比对 md-editor-v3、vditor 这两个插件的使用及效果体验 md-editor-v3的使用及效果展示 安装 npm install md-editor-v3使用 <script setup> import {reactive} f…

图神经网络(Graph Neural Networks)是什么?

图神经网络&#xff08;Graph Neural Networks&#xff09;是什么&#xff1f; 引言 在数据科学和机器学习的广阔领域中&#xff0c;图结构数据以其独特的复杂性和丰富性成为了一个重要的研究方向。从社交网络中的用户关系&#xff0c;到生物信息学中的蛋白质交互网络&#x…