概述
老生常谈,为什么写这个骨架?通讯部分的代码繁杂,条理性差,是最主要的原因。平常的工程师对这块注意的不够。Qt当中通信组件就这么几个,他们都使用信号和槽机制代替了线程,接收的while循环就被信号和槽掩盖了。那么现在梳理下,这块怎么做。权当过去对Qt网络功能认识的升级版。经典的哦。
通信骨架
不使用Qt的时候,我们网络工程师经常缠绕在通讯件/协议/报文格式/网络功能定义之中,转着圈子不清晰的来回。现在这个骨架就是为了解决这个不明了不清晰。这里只介绍Tcp通信,Udp通信照本宣科。
骨架分析图如下
1. 通讯组件
2. 通讯协议
- 报文格式
- 功能定义
- 功能时序
通讯协议不依赖于通信组件。
通讯协议包含三个要素
遵循这几个特点制订了这套通讯骨架。
通讯组件
QtScocket,QtServer,QtSerialPort三个。他们的通信过程是同样的。
通过读数据槽接收流数据或者报文数据,对数据进行切分和分发。发送数据非常容易,write即可。
通讯协议
QtProtocol,虚基类提供协议定义,在所有的通信组件中都可以使用。这个虚基类定义了一组接口,splitter/dispatacher/maxlength/minlength/write信号等提供通讯组件设定的所需的协议接口。这个协议的突出特点是不依赖通讯组件。
通讯功能
用户通过此协议基类派生出特定的协议实现特定的功能。通讯组件安装特定协议,实现特定的通讯。
通讯报文
QtMessage,虚基类定义了报文格式,提供了packer和parser两个虚接口,通过派生报文,来提供通讯组件使用的报文格式。这个报文的突出特点是不依赖协议,从属于协议。
通讯结构体
这是用户最终发送和接收时使用的结构体,应用层直接使用结构体进行设定和读取操作。
工作过程
通讯组件安装通讯协议,
通讯协议提供通讯功能,自己选定使用的报文。
通讯例程
QtServer
qteserver.h
#ifndef QTESERVER_H
#define QTESERVER_H
#include <QTcpServer>
#include "qteprotocol.h"
#include "qteclient.h"
class QteServer : public QTcpServer
{
Q_OBJECT
public:
explicit QteServer(QObject *parent = 0);
~QteServer();
void installProtocol(QteProtocol* stack);
void uninstallProtocol(QteProtocol* stack);
QteProtocol* installedProtocol();
signals:
// QTcpServer interface
protected:
void incomingConnection(int handle);
private:
QteProtocol* m_protocol;
};
qteserver.cpp
#include "qteserver.h"
QteServer::QteServer(QObject *parent) :
QTcpServer(parent)
{
}
QteServer::~QteServer()
{
close();
}
void QteServer::incomingConnection(int handle)
{
QteClient* clientSocket = new QteClient(this);
clientSocket->setSocketDescriptor(handle);
connect(clientSocket, SIGNAL(disconnected()), clientSocket, SLOT(deleteLater()));
clientSocket->installProtocol(m_protocol);
}
void QteServer::installProtocol(QteProtocol *stack)
{
if(m_protocol)
return;
m_protocol = stack;
connect(m_protocol, SIGNAL(write(const QByteArray&)), this, SLOT(write(const QByteArray&)));
}
void QteServer::uninstallProtocol(QteProtocol *stack)
{
if(!m_protocol)
return;
disconnect(m_protocol, SIGNAL(write(const QByteArray&)), this, SLOT(write(const QByteArray&)));
m_protocol = NULL;
}
QteProtocol *QteServer::installedProtocol()
{
return m_protocol;
}
QtClient
qteclient.h
#ifndef QTE_CLIENT_H
#define QTE_CLIENT_H
#include <QTcpSocket>
#include "qteprotocol.h"
#include "QStringList"
#define _TCP_BLOCKDATA_SIZE 0x400
#define _TCP_RECVBUFF_SIZE 0x800
/** * @brief 客户端决定和协议的交互关系;只跟协议打交道; */
class QteClient : public QTcpSocket
{
Q_OBJECT
public:
explicit QteClient(QObject *parent = 0);
virtual ~QteClient();
void setServerIPAddress(QStringList ip) { m_serverIP = ip; }
void setServerPort(quint32 p = 7000) { m_PORT = p; }
void installProtocol(QteProtocol* stack);
void uninstallProtocol(QteProtocol* stack);
QteProtocol* installedProtocol();
void SendConnectMessage();
int SendDisConnectFromHost();
signals:
void signalConnecting();
void signalConnectSucc();
void signalConnectFail();//
void signalDisConnectSucc();//maybe
void signalDisConnectFail();//?
void signalUpdateProgress(int value);
private slots:
void domainHostFound();
void socketStateChanged(QAbstractSocket::SocketState);
void socketErrorOccured(QAbstractSocket::SocketError);
void socketConnected();
void socketDisconnect();
void updateProgress(qint64);
protected slots:
void readyReadData();
private:
void connectToSingelHost();
//TODO:如果文件传输影响到了UI线程,那么需要将QTcpSocket局部变量化
//阻塞UI不必考虑此处
//非阻塞UI,UI却工作很慢,考虑此处。
//QTcpSocket* m_sock;
QteProtocol* m_protocol;
quint32 eConType;
QStringList m_serverIP;
quint32 m_PORT;
};
*qteclient.cpp*
#include "qteclient.h"
#include "qtelinux.h"
#include <QTcpSocket>
#include <QHostInfo>
QteClient::QteClient(QObject *parent) :
QTcpSocket(parent)
{
connect(this, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)) );
// connected
connect(this, SIGNAL(connected()), this, SLOT(socketConnected()) );
// disconnected
connect(this, SIGNAL(disconnected()), this, SLOT(socketDisconnect()) );
// domain
connect(this, SIGNAL(hostFound()), this, SLOT(domainHostFound()));
// error
connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketErrorOccured(QAbstractSocket::SocketError)) );
connect(this, SIGNAL(readyRead()), this, SLOT(readyReadData()));
connect(this, SIGNAL(bytesWritten(qint64)), this, SLOT(updateProgress(qint64)));
connect(this, SIGNAL(signalSendData()), this, SLOT(sendUploadFileData()), Qt::QueuedConnection);
connect(this, SIGNAL(signalDownData()), this, SLOT(sendDownFileData()), Qt::QueuedConnection);
connect(this, SIGNAL(signalDownSucc()), this, SLOT(sendDownFileSuccess()), Qt::QueuedConnection );
setSocketOption(QAbstractSocket::LowDelayOption, 0);
setSocketOption(QAbstractSocket::KeepAliveOption, 0);
setReadBufferSize(_TCP_RECVBUFF_SIZE);
m_PORT = 0;
//启动连接
eConType = 0;
m_protocol = NULL;
}
QteClient::~QteClient()
{
}
void QteClient::installProtocol(QteProtocol *stack)
{
if(m_protocol)
return;
m_protocol = stack;
connect(m_protocol, SIGNAL(write(const QByteArray&)), this, SLOT(write(const QByteArray&)));
}
void QteClient::uninstallProtocol(QteProtocol *stack)
{
if(!m_protocol)
return;
disconnect(m_protocol, SIGNAL(write(const QByteArray&)), this, SLOT(write(const QByteArray&)));
m_protocol = NULL;
}
QteProtocol *QteClient::installedProtocol()
{
return m_protocol;
}
void QteClient::SendConnectMessage()
{
pline() << isValid() << isOpen() << state();
if(!isValid() && !isOpen())
{
connectToSingelHost();
return;
}
if(state() == HostLookupState ||
state() == ConnectingState)
{
emit signalConnecting();
return;
}
if(state() == ConnectedState)
emit signalConnectSucc();
return;
}
int QteClient::SendDisConnectFromHost()
{
pline() << isValid() << isOpen() << state();
if(isValid() || isOpen() )
{
shutdown(this->socketDescriptor(), SHUT_RDWR);
disconnectFromHost();
waitForDisconnected();
close();
emit signalDisConnectSucc();
}
return true;
}
void QteClient::domainHostFound()
{
pline();
}
/** * @brief QteClient::socketStateChanged * @param eSocketState * 状态函数 */
void QteClient::socketStateChanged(QAbstractSocket::SocketState eSocketState)
{
pline() << eSocketState;
switch(eSocketState)
{
case QAbstractSocket::HostLookupState:
case QAbstractSocket::ConnectingState:
break;
case QAbstractSocket::ConnectedState:
break;
case QAbstractSocket::ClosingState:
break;
case QAbstractSocket::UnconnectedState:
eConType++;
break;
default:
break;
}
}
/** * @brief QteClient::socketErrorOccured * @param e * 状态函数 */
void QteClient::socketErrorOccured(QAbstractSocket::SocketError e)
{
//在错误状态下重新连接其他热点,直到确定连接类型,写入配置文件
pline() << e;
switch(e)
{
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
default:
emit signalConnectFail();
break;
}
}
/** * @brief QteClient::socketConnected * 功能接口 */
void QteClient::socketConnected()
{
pline() << peerName() << peerAddress().toString() << peerPort();
//这个步骤,socket重建,资源重新开始
emit signalConnectSucc();
}
/** * @brief QteClient::socketDisconnect * 功能接口 */
void QteClient::socketDisconnect()
{
pline();
}
void QteClient::updateProgress(qint64 bytes)
{
//pline() << bytes;
}
void QteClient::connectToSingelHost()
{
int contype = eConType % m_serverIP.size();
QString ip = m_serverIP.at(contype);
connectToHost(QHostAddress(ip), m_PORT);
pline() << peerName() << m_PORT;
}
void QteClient::readyReadData()
{
// queued conn and queued package;
// direct conn and direct package;
static QByteArray m_blockOnNet;
m_blockOnNet += readAll();
//qint64 aaa = bytesAvailable();
//pline() << aaa;
do{
quint16 nBlockLen = m_protocol->splitter(m_blockOnNet);
pline() << m_blockOnNet.size() << "..." << nBlockLen;
//收到数据不足或者解析包长小于最小包长
if(m_blockOnNet.length() < nBlockLen)
{
return;
}
//粘包
else if(m_blockOnNet.length() > nBlockLen)
{
//还没有处理完毕,数据已经接收到,异步信号处理出现这种异常
//疑问:如果异步调用这个函数绘出现什么问题?正常情况,同步获取数据,异步处理;检测异步获取并且处理会有什么状况
pline() << "stick package" << m_blockOnNet.length() << nBlockLen;
QByteArray netData;
netData.resize(nBlockLen);
m_blockOnNet >> netData;
m_protocol->dispatcher(netData);
continue;
}
//正常分发
m_protocol->dispatcher(m_blockOnNet);
break;
}while(1);
m_blockOnNet.clear();
}
QtSerialPort
qteserialport.h
#ifndef SERIALPORT_H
#define SERIALPORT_H
#include <QtSerialPort/QSerialPort>
#include "qteprotocol.h"
class QteSerialPort : public QSerialPort
{
Q_OBJECT
public:
explicit QteSerialPort(QObject *parent = 0);
~QteSerialPort();
void installProtocol(QteProtocol* stack);
void uninstallProtocol(QteProtocol* stack);
QteProtocol* installedProtocol();
private slots:
void readyReadData();
QteProtocol* m_protocol;
};
#endif // SERIALPORT_H
qteserialport.cpp
#include "qteserialport.h"
QteSerialPort::QteSerialPort(QObject *parent) :
QSerialPort(parent)
{
//connect(this, SIGNAL(bytesWritten(qint64)), this, SLOT(updateProgress(qint64)) );
connect(this, SIGNAL(readyRead()), this, SLOT(readyReadData()) );
//connect(this, SIGNAL(aboutToClose()), this, SLOT(aboutToClose()));
//connect(this, SIGNAL(readChannelFinished()), this, SLOT(readChannelFinished()));
}
QteSerialPort::~QteSerialPort()
{
close();
}
void QteSerialPort::installProtocol(QteProtocol *stack)
{
if(m_protocol)
return;
m_protocol = stack;
connect(m_protocol, SIGNAL(write(const QByteArray&)), this, SLOT(write(const QByteArray&)));
}
void QteSerialPort::uninstallProtocol(QteProtocol *stack)
{
if(!m_protocol)
return;
disconnect(m_protocol, SIGNAL(write(const QByteArray&)), this, SLOT(write(const QByteArray&)));
m_protocol = NULL;
}
QteProtocol *QteSerialPort::installedProtocol()
{
return m_protocol;
}
void QteSerialPort::readyReadData()
{
// queued conn and queued package;
// direct conn and direct package;
static QByteArray m_blockOnNet;
m_blockOnNet += readAll();
//qint64 aaa = bytesAvailable();
//pline() << aaa;
do{
quint16 nBlockLen = m_protocol->splitter(m_blockOnNet);
pline() << m_blockOnNet.size() << "..." << nBlockLen;
//收到数据不足或者解析包长小于最小包长
if(m_blockOnNet.length() < nBlockLen || nBlockLen < m_protocol->minlength())
{
return;
}
//数据包长超过了最大长度
else if(nBlockLen > m_protocol->maxlength())
{
m_blockOnNet.clear();
pline() << "forbidden package" << m_blockOnNet.length() << nBlockLen;
return;
}
//粘包
else if(m_blockOnNet.length() > nBlockLen)
{
//还没有处理完毕,数据已经接收到,异步信号处理出现这种异常
//疑问:如果异步调用这个函数绘出现什么问题?正常情况,同步获取数据,异步处理;检测异步获取并且处理会有什么状况
pline() << "stick package" << m_blockOnNet.length() << nBlockLen;
QByteArray netData;
netData.resize(nBlockLen);
m_blockOnNet >> netData;
m_protocol->dispatcher(netData);
continue;
}
//正常分发
m_protocol->dispatcher(m_blockOnNet);
break;
}while(1);
m_blockOnNet.clear();
}
QtProtocol
qteprotocol.h
#ifndef QTEPROTOCOL_H
#define QTEPROTOCOL_H
#include <QObject>
#include "qtemessage.h"
class QteProtocol : public QObject
{
Q_OBJECT
public:
explicit QteProtocol(QObject *parent = 0);
signals:
qint64 write(const QByteArray& l);
public:
virtual quint16 minlength() {}
/** * @brief 最大包长 * @return */
virtual quint16 maxlength() {}
/** * @brief 语法解析器 从流中解析报文长度 * @param 接收到的数据段 * @return 按照协议解析到的数据长度 可用,继续接收,丢弃,粘包。 */
virtual quint16 splitter(const QByteArray &s){}
/** * @brief 语义解析器 * @param 数据包 * @return 0 no dispatched(others) 1 dispatched(own) */
virtual bool dispatcher(const QByteArray &m){}
};
#endif // QTEPROTOCOL_H
qteprotocol.cpp
#include "qteprotocol.h"
QteProtocol::QteProtocol(QObject *parent) : QObject(parent)
{
}
QtNetworkProtocol
自行实现
QtC3SerialProtocol
自行实现
QtC0SerialProtocol
自行实现
QtThirdpartyProtocol
qtethirdpartyprotocol.h
#ifndef QTEAPROTOCOL_H
#define QTEAPROTOCOL_H
#include "qteprotocol.h"
#define __LOGIN 0x00BB
class QteLogin : public QteThirdpartyMessage
{
Q_OBJECT
public:
explicit QteLogin(QObject *parent = 0) : QteThirdpartyMessage(parent){}
void pack(QByteArray& l)
{
setCmd(__LOGIN);
translate();
QteThirdpartyMessage::packer(l);
}
};
class QteLoginAck : public QteThirdpartyMessage
{
Q_OBJECT
public:
explicit QteLoginAck(QObject *parent = 0) : QteThirdpartyMessage(parent){}
void parse(const QByteArray& l)
{
QByteArray _l = l;
_l >> aid;
}
quint8 aid;
};
class QteThirdpartyProtocol : public QteProtocol
{
public:
explicit QteThirdpartyProtocol(QObject *parent = 0);
void recvLogin(const QByteArray& data)
{
QteLoginAck ack;
ack.parse(data);
qDebug() << ack.mid;
}
// QteProtocol interface
public:
quint16 minlength() override;
quint16 maxlength() override;
quint16 splitter(const QByteArray &s) override;
bool dispatcher(const QByteArray &m) override;
};
#endif // QTEAPROTOCOL_H
qtethirdpartyprotocol.cpp
#include "qtelanprotocol.h"
#include "qtethirdpartymessage.h"
QteThirdpartyProtocol::QteThirdpartyProtocol(QObject *parent) :
QteProtocol(parent)
{
}
quint16 QteThirdpartyProtocol::minlength()
{
return 0x0A;
}
quint16 QteThirdpartyProtocol::maxlength()
{
return 23456;
}
quint16 QteThirdpartyProtocol::splitter(const QByteArray &s)
{
QByteArray l = s;
quint16 b0 = 0, b1 = 0;
l >> b0 >> b1;
return b1;
}
bool QteThirdpartyProtocol::dispatcher(const QByteArray &m)
{
QteThirdpartyMessage qMsg;
qMsg.parser(m);
switch(qMsg.cmd())
{
case __LOGIN:
recvLogin(qMsg.data());
break;
default:
break;
}
QtC788Protocol
自行实现
QtMessage
qtemessage.h
#ifndef QTEMESSAGE_H
#define QTEMESSAGE_H
#include <QObject>
/** * @brief 语法类 定义报文格式 */
class QteMessage : public QObject
{
Q_OBJECT
public:
explicit QteMessage(QObject *parent = 0);
protected:
/**
* @brief 从流中解析报文
* @param m
* @param l
*/
virtual void parser(const QByteArray &l) = 0;
/** * @brief 将报文组装为流 * @param l * @param m */
virtual void packer(QByteArray& l) = 0;
/** * @brief 最小包长 * @return */
signals:
public slots:
private:
};
#endif // QTEMESSAGE_H
qtemessage.cpp
#include "qtemessage.h"
QteMessage::QteMessage(QObject *parent) : QObject(parent)
{
}
QtNetworkMessage
自行实现
QtSerialMessage
自行实现
QtThirdpartyMessage
qtethirdpartymessage.h
#ifndef QTETHIRDPARTYMESSAGE_H
#define QTETHIRDPARTYMESSAGE_H
#include "qtemessage.h"
#define __HEAD 0xEEFF
#define __TAIL 0xFFEE
class QteThirdpartyMessage : public QteMessage
{
public:
explicit QteThirdpartyMessage(QObject* parent = 0);
const quint16& head() const;
void setHead(quint16 head);
const quint16& size() const;
void setSize(quint16 size);
//user
const quint16& cmd() const;
void setCmd(quint16 cmd);
//user
const QByteArray& data() const;
void setData(QByteArray& data);
const quint16& sum() const;
void setSum(quint16 sum);
const quint16& tail() const;
void setTail(quint16 tail);
void translate();
protected:
void parser(const QByteArray &l) override;
void packer(QByteArray &l) override;
private:
quint16 m_Head;
quint16 m_Size;
quint16 m_Cmd;
QByteArray m_Data;
quint16 m_Sum;
quint16 m_Tail;
};
qtethirdpartymessage.cpp
#include "qtethirdpartymessage.h"
QteThirdpartyMessage::QteThirdpartyMessage(QObject *parent) :
QteMessage(parent)
{
m_Head = __HEAD;
m_Size = m_Cmd = m_Sum = 0;
m_Data.clear();;
m_Tail = __TAIL;
}
const quint16 &QteThirdpartyMessage::head() const { return m_Head; }
void QteThirdpartyMessage::setHead(quint16 head) { m_Head = head; }
const quint16 &QteThirdpartyMessage::size() const { return m_Size; }
void QteThirdpartyMessage::setSize(quint16 size) { m_Size = size; }
const quint16 &QteThirdpartyMessage::cmd() const { return m_Cmd; }
void QteThirdpartyMessage::setCmd(quint16 cmd) { m_Cmd = cmd; }
const QByteArray &QteThirdpartyMessage::data() const { return m_Data; }
void QteThirdpartyMessage::setData(QByteArray &data) { m_Data = data; }
const quint16 &QteThirdpartyMessage::sum() const { return m_Sum; }
void QteThirdpartyMessage::setSum(quint16 sum) { m_Sum = sum; }
const quint16 &QteThirdpartyMessage::tail() const { return m_Tail; }
void QteThirdpartyMessage::setTail(quint16 tail) { m_Tail = tail; }
void QteThirdpartyMessage::translate()
{
m_Size = m_Data.length() + 0x0A;
QByteArray qbaVerify;
qbaVerify << m_Size << m_Cmd << m_Data;
m_Sum = 0;
for(int i = 0; i < qbaVerify.length(); i++)
m_Sum += quint8(qbaVerify.at(i)) + 34;
}
void QteThirdpartyMessage::parser(const QByteArray &netData)
{
QByteArray l = netData;
quint16 b0 = 0, b1 = 0, b2 = 0, b4 = 0, b5 = 0;
QByteArray b3;
l >> b0 >> b1 >> b2;
b3.resize(b1-0x0A);
l >> b3 >> b4 >> b5;
setHead(b0);
setSize(b1);
setCmd(b2);
setData(b3);
setSum(b4);
setTail(b5);
}
void QteThirdpartyMessage::packer(QByteArray &l)
{
l << head();
l << size();
l << cmd();
l << data();
l << sum();
l << tail();
}
工具函数
QByteArray &operator<<(QByteArray &l, const quint8 r)
{
return l.append(r);
}
QByteArray &operator<<(QByteArray &l, const quint16 r)
{
return l<<quint8(r>>8)<<quint8(r);
}
QByteArray &operator<<(QByteArray &l, const quint32 r)
{
return l<<quint16(r>>16)<<quint16(r);
}
QByteArray &operator<<(QByteArray &l, const QByteArray &r)
{
return l.append(r);
}
QByteArray &operator>>(QByteArray &l, quint8 &r)
{
r = l.left(sizeof(quint8))[0];
return l.remove(0, sizeof(quint8));
}
QByteArray &operator>>(QByteArray &l, quint16 &r)
{
quint8 r0 = 0, r1 = 0;
l >> r0 >> r1;
r = ( r0 << 8 ) | r1;
return l;
}
QByteArray &operator>>(QByteArray &l, quint32 &r)
{
quint8 r0 = 0, r1 = 0, r2 = 0, r3 = 0;
l >> r0 >> r1 >> r2 >> r3;
r = ( r0 << 24 ) | ( r1 << 16 ) | ( r2 << 8 ) | r3;
return l;
}
QByteArray &operator>>(QByteArray &l, QByteArray &r)
{
r = l.left(r.size());
return l.remove(0, r.size());
}
QByteArray &operator<<(QByteArray &l, const qint8 r)
{
quint8 ubyte = quint8(r);
l << ubyte;
return l;
}
QByteArray &operator<<(QByteArray &l, const qint16 r)
{
quint16 ubyte = quint16(r);
l << ubyte;
return l;
}
QByteArray &operator<<(QByteArray &l, const qint32 r)
{
quint32 ubyte = quint32(r);
l << ubyte;
return l;
}
QByteArray &operator>>(QByteArray &l, qint8 r)
{
quint8 ubyte = 0;
l >> ubyte;
r = qint8(ubyte);
return l;
}
QByteArray &operator>>(QByteArray &l, qint16 r)
{
quint16 ubyte = 0;
l >> ubyte;
r = qint16(ubyte);
return l;
}
QByteArray &operator>>(QByteArray &l, qint32 r)
{
quint32 ubyte = 0;
l >> ubyte;
r = qint32(ubyte);
return l;
}