Chap12-TcpManager
Chap12-TcpManager
上一节,我们完成了状态服务器,状态服务器用来获取可用的负载小的服务器的信息。
那么我们的Qt前端发送了ID_USER_LOGIN信号之后,posthttp发送登陆请求给GateWayServer。这一步主要是进行验证作用,比如密码邮箱什么的是否正确。正确之后,我们服务器这里看到,还要去调用状态服务器的服务,获取服务器信息。
得到这里信息之后,我们的前端需要主动的连接,因此我们需要封装Tcp管理类,用于和服务器进行连接。
// tcpmanager.h
#ifndef TCPMANAGER_H
#define TCPMANAGER_H
#include "Properties/singleton.h"
#include <QObject>
#include <memory>
#include <QTcpSocket>
#include <QHash>
#include <functional>
class TcpManager
: public QObject
, public Singleton<TcpManager>
, public std::enable_shared_from_this<TcpManager>
{
Q_OBJECT
friend class Singleton<TcpManager>;
public:
~TcpManager();
private:
TcpManager();
// 注册回调
void initHandlers();
// 连接
void connections();
// 处理单个数据->hash找回调处理
void handleMessage(RequestType requestType,int len,QByteArray data);
private:
QTcpSocket _socket;
QString _host;
uint16_t _port;
QByteArray _buffer;
bool _recv_pending;
quint16 _msg_id;
quint16 _msg_len;
// 存放请求和对应的回调函数
QHash<RequestType,std::function<void(RequestType,int,QByteArray)>>_handlers;
public slots:
void do_tcp_connect(ServerInfo); // from LoginScreen::on_tcp_connect
void do_send_data(RequestType requestType,QString data); // from TcpManager::to_send_data
signals:
void on_connect_success(bool success); // to LoginScreen::do_connect_success
void on_send_data(RequestType requestType,QString data); // to TcpManager::do_send_data
void on_switch_interface();
void on_login_failed(int);
};
#endif // TCPMANAGER_H
// .cpp
#include "tcpmanager.h"
#include <QAbstractSocket>
#include <QDataStream>
#include <QJsonObject>
TcpManager::~TcpManager() = default;
TcpManager::TcpManager()
: _host("")
, _port(0)
, _recv_pending(false)
, _msg_id(0)
, _msg_len(0)
{
initHandlers();
connections();
}
void TcpManager::initHandlers()
{
_handlers[RequestType::ID_CHAT_LOGIN_RSP] = [this](RequestType requestType,int len,QByteArray data){
QJsonDocument jsonDoc = QJsonDocument::fromJson(data);
if (jsonDoc.isNull()){
qDebug() << "Error occured about Json";
return;
}
QJsonObject jsonObj = jsonDoc.object();
if (!jsonObj.contains("error")){
int err = static_cast<int>(ErrorCodes::ERROR_JSON);
qDebug() << "Login Failed,Error Is Json Parse Error " <<err;
emit on_login_failed(err);
return;
}
int err = jsonObj["error"].toInt();
if (err != static_cast<int>(ErrorCodes::SUCCESS)){
qDebug() << "Login Failed,Error Is " << err;
emit on_login_failed(err);
return;
}
emit on_switch_interface();
};
}
void TcpManager::connections()
{
// 连接成功
connect(&_socket,&QTcpSocket::connected,[&](){
qDebug() << "Connected to Server.";
emit on_connect_success(true);
});
// 读取数据
connect(&_socket,&QTcpSocket::readyRead,[&](){
_buffer.append(_socket.readAll());
QDataStream stream(&_buffer,QIODevice::ReadOnly);
stream.setByteOrder(QDataStream::BigEndian);
forever{
if (!_recv_pending){
if (_buffer.size() < static_cast<int>(sizeof(qint16)*2)){
return;
}
stream >> _msg_id >> _msg_len;
_buffer.remove(0,4);
qDebug() << "Message Id:" << _msg_id << " len:" << _msg_len ;
}
if (_buffer.size() < _msg_len){
_recv_pending = true;
return;
}
_recv_pending = false;
QByteArray msgBody = _buffer.mid(_msg_len);
qDebug() << "Receive body:" <<msgBody;
_buffer.remove(0,_msg_len);
handleMessage(RequestType(_msg_id),_msg_len,msgBody);
}
});
// 错误
connect(&_socket,&QTcpSocket::errorOccurred,[&](QTcpSocket::SocketError socketError){
qDebug() << "Socket Error["<<socketError<< "]:" << _socket.errorString();
});
// 断开连接
connect(&_socket,&QTcpSocket::disconnected,[&](){
qDebug() << "Disconnected from server - " << _socket.peerAddress().toString() <<":"<<_socket.peerPort();
});
// 发送数据
connect(this,&TcpManager::on_send_data,this,&TcpManager::do_send_data);
}
void TcpManager::handleMessage(RequestType requestType, int len, QByteArray data)
{
auto it = _handlers.find(requestType);
if ( it == _handlers.end()){
qDebug() << "Not Found[" << static_cast<int>(requestType) << "] to handle";
return;
}
it.value()(requestType,len,data);
}
void TcpManager::do_tcp_connect(ServerInfo si)
{
qDebug() << "Connecting to server...";
_host = si.host;
_port = static_cast<uint16_t>(si.port.toInt());
_socket.connectToHost(_host,_port);
}
void TcpManager::do_send_data(RequestType requestType, QString data)
{
auto id =static_cast<uint16_t>(requestType);
QByteArray dataBytes = data.toUtf8();
quint16 len = static_cast<quint16>(data.size());
QByteArray block;
QDataStream out(&block,QIODevice::WriteOnly);
out.setByteOrder(QDataStream::BigEndian);
out << id << len;
block.append(dataBytes);
_socket.write(block);
}
也就是在得到GateWayServer发来的信息之后,我们在回调处理,处理的时候对于这个登陆操作,我们发送一个on_tcp_connect信号,这样tcpmanager的槽函数do_tcp_connect开始工作前去连接服务器:
void TcpManager::do_tcp_connect(ServerInfo si)
{
qDebug() << "Connecting to server...";
_host = si.host;
_port = static_cast<uint16_t>(si.port.toInt());
_socket.connectToHost(_host,_port);
}
这个tcp管理类是真正和聊天服务器进行持久化连接数据交换的实际执行者:
connect(this,&TcpManager::on_send_data,this,&TcpManager::do_send_data);
void TcpManager::do_send_data(RequestType requestType, QString data)
{
auto id =static_cast<uint16_t>(requestType);
QByteArray dataBytes = data.toUtf8();
quint16 len = static_cast<quint16>(data.size());
QByteArray block;
QDataStream out(&block,QIODevice::WriteOnly);
out.setByteOrder(QDataStream::BigEndian);
out << id << len;
block.append(dataBytes);
_socket.write(block);
}
我们注册了这样的信号和槽,只要我们制作好要发送的数据,然后把要发送的请求类型的数据通过信号on_send_data交给槽函数处理,就能沟通了。

浙公网安备 33010602011771号