当前位置: 首页 > news >正文

客户端登录逻辑

将网关服务器发送的数据通过信号传递给 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 serveremit 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.mrgr.cn/news/522.html

相关文章:

  • WLAN射频调优
  • 物流快递外卖管理平台系统-计算机毕设Java|springboot实战项目
  • OpenHarmony网络协议通信—libevent [GN编译] - 事件通知库
  • 洁净区环境监测法规依据、评估方法与原则风险评估策略解析
  • spring bean的循环依赖
  • Android10.0 人脸解锁流程分析
  • 使用flask遇到的问题及解决方法
  • WebDeveloper:1靶机
  • 红黑树的模拟实现
  • Java面试八股之什么是MQTT协议
  • Leetcode3228. 将 1 移动到末尾的最大操作次数
  • STM32使用串口DMA发送+空闲中断
  • 4.1 SQL的起源与发展
  • 3D 技术对我们的生活有哪些影响?
  • 流量分析0.o
  • 安卓App开发 篇五:签名和打包
  • Kafka系列之:Kafka Connect深入探讨 - 错误处理和死信队列
  • 微前端架构下的响应式设计实现策略
  • 腾讯云短信正文模板每个变量取值最多支持6个字符出现的问题及应对方法
  • MyBatis入门