概述
老生常谈,为什么写这个骨架?通讯部分的代码繁杂,条理性差,是最主要的原因。平常的工程师对这块注意的不够。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