很多情形下,我們在應用程序的最外層都使用TCP作為可靠控制,下面,我就總結一下我在項目中使用QTcpServer與QTcpSocket的一些心得和體會。
服務端TCP程序可以利用QTcpServer,首先我們聲名QTcpServer *對象(注意:QTcpServer占用的網絡資源需要我們手動釋放,任何我們程序可能異常退出或正常結束的地方QTcpServer *最後都要close())
注意我們的服務端要一直監聽客戶端
tcpPort=8888;
tcpServer=new QTcpServer();
if(!tcpServer->listen(QHostAddress::Any,tcpPort))
{
tcpServer->close();
emit MSG(QString("listen erro"));
}
connect(tcpServer,&QTcpServer::newConnection,this,&TcpServer::onNewConnection);
在新連接對應的槽裡我們把
QTcpSocket *newpeer=tcpServer->nextPendingConnection();
這個新連接的socket指針取下來,進行操作,當執行newpeer->write(QByterArray &MSG)即服務器向此peer發送數據;當執行QByterArray MSG=newpeer->write()即服務器讀此peer的數據,所有信息的傳遞均是對QTcpSocket的操作,並要要對每一個socket的disconnected連接deleteLater槽防止內存洩漏。
說到此處,就牽涉到了QString 與QByterArray的轉換,因為我們平時處理消息大多是對QSring進行處理:
QString->QByterArray(只有英文(Latin)時):
newpeer->write(QString("Server").toLatin1());
QByterArray-> QString:
QTextCodec *tc= QTextCodec::codecForName("UTF-8");
QString str=tc->toUnicode((QByteArray)
rawreply)
下面的代碼抽取自 我的服務端程序
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <QObject>
#include<QTcpServer>
#include<QAbstractSocket>
class TcpServer : public QObject
{
Q_OBJECT
public:
explicit TcpServer(QObject *parent = 0);
~TcpServer();
private:
int nb_peer;//總共的小伙伴數目,用於確定client->index的取值
QTcpServer *tcpServer;
public slots:
void ReadDataFromSocket();
void onNewConnection();
void onDisConnection();
void processMSG(QString &MSG);
// void processAddedClient(QTcpSocket *client);
};
#include "tcpserver.h"
TcpServer::TcpServer(QObject *parent) :
QObject(parent),nb_peer(0)
{
tcpPort=8888;
tcpServer=new QTcpServer();
if(!tcpServer->listen(QHostAddress::Any,tcpPort))
{
tcpServer->close();
emit MSG(QString("listen erro"));
}
peerList = QList<client>();
connect(tcpServer,&QTcpServer::newConnection,this,&TcpServer::onNewConnection);
}
void TcpServer::onNewConnection()
{
QTcpSocket *newpeer=tcpServer->nextPendingConnection();
//寫入驗證
connect(newpeer,SIGNAL(readyRead()),this,SLOT(ReadDataFromSocket()));
connect(newpeer,SIGNAL(disconnected()),newpeer,SLOT(deleteLater()));
connect(newpeer,SIGNAL(disconnected()),this,SLOT(onDisConnection()));
newpeer->write(QString("Server").toLatin1());
newpeer->flush();
}
#include<QTextCodec>
#include<iterator>
void TcpServer::ReadDataFromSocket()
{
QByteArray rawreply= static_cast<QTcpSocket*>(sender())->readAll();
QTextCodec *tc= QTextCodec::codecForName("UTF-8");
client beingCheckedClient;
beingCheckedClient.index=nb_peer;
beingCheckedClient.socket=static_cast<QTcpSocket*>(sender());
if(tc->toUnicode(rawreply)!=QString("Client")&& !peerList.contains(beingCheckedClient
))
{
//寫入信息
//emit ChangeLabel(QString(rawreply));
static_cast<QTcpSocket*>(sender())->write(
QString("connect time out! disconnecting...").toLatin1());
static_cast<QTcpSocket*>(sender())->flush();
static_cast<QTcpSocket*>(sender())->disconnectFromHost();
return ;
}
}
TcpServer::~TcpServer()
{
tcpServer->close();
}
客戶端就沒什麼好說的了,也是對QTcpSocket的一些操作,但是注意要在程序開始時對服務器發起連接:
tcpsocket=new QTcpSocket();
tcpsocket->abort();
tcpsocket->connectToHost(("127.0.0.1"),8888);
一些問題:很多大牛可能會說,我直接使用write read接受字符串命令可能並不安全,在實際應用中我發現,低頻率的收發數據這種方法非常可靠,但是在很多客戶端同時連接時,進行回調消息驗證時,會出現信息殘缺,我的解決方案是使用延時策略,但是這並不是長久之計。
按照官方給的例子:
服務端發送數據:
void Server::sendFortune()
{
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << fortunes.at(qrand() % fortunes.size());
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));
QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()),
clientConnection, SLOT(deleteLater()));
clientConnection->write(block);
clientConnection->disconnectFromHost();
}
客戶端接受數據:
void Client::readFortune()
{
QDataStream in(tcpSocket);
in.setVersion(QDataStream::Qt_4_0);
if (blockSize == 0) {
if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
return;
in >> blockSize;
}
if (tcpSocket->bytesAvailable() < blockSize)
return;
QString nextFortune;
in >> nextFortune;
if (nextFortune == currentFortune) {
QTimer::singleShot(0, this, SLOT(requestNewFortune()));
return;
}
currentFortune = nextFortune;
statusLabel->setText(currentFortune);
getFortuneButton->setEnabled(true);
}
缺點是每收發一回合信息,都要重連一次。
Ubuntu 環境下Gtk與QT編譯環境安裝與配置 http://www.linuxidc.com/Linux/2013-08/88539.htm
Linux系統下QT環境搭建 http://www.linuxidc.com/Linux/2013-07/87576.htm
Ubuntu下QT控制台程序無法運行的解決方案以及XTerm的配置方法 http://www.linuxidc.com/Linux/2013-06/86244.htm
Ubuntu 10.04下QT4.7.4移植詳解 http://www.linuxidc.com/Linux/2013-01/77930.htm
Ubuntu 14.04下安裝部署Qt5開發環境 http://www.linuxidc.com/Linux/2014-05/101774.htm
Qt 的詳細介紹:請點這裡
Qt 的下載地址:請點這裡