天天看點

QTcpServer和QTcpSocket使用詳解QTcpServer和QTcpSocket使用詳解

QTcpServer和QTcpSocket使用詳解

1、基本使用方法

QTcpServer和QTcpSocket的使用是密不可分的,是以兩者一塊示範使用方法。

QTcpServer常用信号:

  • newConnection()信号,該信号用于處理新接入的連接配接

QTcpSocket常用信号:

  • disconnected():斷開連接配接後會觸發該信号,一般在該信号的槽函數内處理記憶體清理釋放等工作
  • readyRead():資料到達信号,在該信号的槽函數内處理資料接收

補充:如果想要擷取遠端接入的IP位址端口等資訊,可以使用QTcpSocket的peerAddress()函數擷取IP位址,peerPort()擷取端口。

基于QTcpServer的TCP伺服器多接入echo代碼demo:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget *ui;

private:
    QTcpServer* tcpServer;

private slots:
    void slotReadyRead();
    void slotNewConnected();
};

#endif // WIDGET_H

           
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    tcpServer = new QTcpServer(this);
    QObject::connect(tcpServer, SIGNAL(newConnection()), this, SLOT(slotNewConnected()));
    tcpServer->listen(QHostAddress::Any, 10126);
}

Widget::~Widget()
{
    delete ui;
}


void Widget::slotNewConnected()
{
    while(tcpServer->hasPendingConnections())
    {
        QTcpSocket* socket = tcpServer->nextPendingConnection();
        QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
        QObject::connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
        qDebug("[%s:%d] connected", socket->peerAddress().toString().toStdString().c_str(), socket->peerPort());
    }
}

void Widget::slotReadyRead()
{
    QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());  // 取得信号發送者對象
    socket->write(socket->readAll());                          // 将資料在發送回去
}

           

2、基于多線程的使用

         ~~~~~~~~         處理網絡的資料一般不會是單線程處理,因為要更新UI界面且還有任務,不可能讓tcpSocket死等接收,正常情況下通過QTcpServer獲得新接入的用戶端QTcpSocket對象來進行收發資料操作,新接入一個連接配接就建立一個新的線程去處理,但是這裡有一個問題就是

QTcpSocket是不支援跨線程調用的

,哪怕是以引用或者指針的形式傳入到子線程中,也是報錯跨線程調用!

         ~~~~~~~~         要想實作多線程則需要繼承QTcpServer去重寫

incomingConnection

函數,在該函數中将

socketDescriptor

參數用信号把他發射出來,或者直接在該函數中建立自己的任務處理線程,線上程中使用QTcpSocket類的

setsetSocketDescriptor

函數初始化一個socket就可以了。示例代碼如下:

#ifndef QMULTITCPSERVER_H
#define QMULTITCPSERVER_H

#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>

class QMultiTcpServer : public QTcpServer
{
    Q_OBJECT

public:
    QMultiTcpServer(QObject* parent = nullptr);
    virtual ~QMultiTcpServer();

public:
    bool listen(const QHostAddress &address, quint16 port);

protected:
    void incomingConnection(int socketDescriptor);

signals:
    void newConnection(int socketDescriptor);
};

#endif // QMULTITCPSERVER_H

           
#include "qmultitcpserver.h"

QMultiTcpServer::QMultiTcpServer(QObject* parent):QTcpServer(parent)
{

}

QMultiTcpServer::~QMultiTcpServer()
{

}

void QMultiTcpServer::incomingConnection(int socketDescriptor)
{
    emit this->newConnection(socketDescriptor);
}

bool QMultiTcpServer::listen(const QHostAddress &address, quint16 port)
{
    return QTcpServer::listen(address, port);
}

           

示例代碼中,我又新增了一個帶參數的newConnected信号,目的是為了把socketDescriptor通過信号發射到槽函數中。

然後在主窗體執行個體化對象:

tcpServer = new QMultiTcpServer(this);
    QObject::connect(tcpServer, SIGNAL(newConnection(int)), this, SLOT(slotNewConnected(int)));
           

對應的槽函數如下:

void Widget::slotNewConnected(int socketDescriptor)
{
    QTcpSocket* socket = new QTcpSocket(this);
    if(!socket->setSocketDescriptor(socketDescriptor))
    {
        delete socket;
        return;
    }

	//
	// 你可以在這裡建立一個處理線程,将socketDescriptor傳入處理線程,我這裡直接是示範怎麼使用
	//

    QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
    QObject::connect(socket, SIGNAL(disconnected()), this, SLOT(slotDisconnected()));
    qDebug("[%s:%d]: connected.", socket->peerAddress().toString().toStdString().c_str(), socket->peerPort());
}

void Widget::slotReadyRead()
{
    QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());  // 取得目前socket對象
    QByteArray data = socket->readAll();
    qDebug("[%s:%d]:%s", socket->peerAddress().toString().toStdString().c_str(), socket->peerPort(), data.toStdString().c_str());
}

void Widget::slotDisconnected()
{
    QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());  // 取得目前socket對象
    socket->close();
    qDebug("[%s:%d]: disconnected.", socket->peerAddress().toString().toStdString().c_str(), socket->peerPort());
    delete socket;
}

           

         ~~~~~~~~         附上多線程tcp伺服器的一個demo,代碼功能比較簡單,就是隻有回顯功能。用戶端接入後新開一個線程處理資料的發送和接收,執行效果如下:

https://github.com/wowyyy/QtThreadServer

QTcpServer和QTcpSocket使用詳解QTcpServer和QTcpSocket使用詳解

繼續閱讀