1. 介紹
QTcpServer使用請見:QTcpSocket-Qt使用Tcp通信實現服務端和客戶端git
QTcpServer類默認提供的只有無參數的newConnection的信號,這樣雖然知道有人鏈接了,而且能夠經過nextPendingConnection獲取鏈接的socket,但並不便於管理,尤爲是在鏈接斷開之後沒法判斷具體那個斷開了,由於QTcpSocket只提供了無參的disconnected信號。。。github
這樣就算在newConnection是存儲一個list或者map,也沒法在disconnected是知道具體是那一項斷開鏈接,給不一樣的QTcpSocket的信號指向不一樣的槽。服務器
實際上socket有本身的句柄,並經過下述函數在初步鏈接時就賦予了對應的socketDescriptor多線程
- virtual void incomingConnection(qintptr socketDescriptor)
當有client鏈接時,首先是此方法被調用,可自行在此方法內創建QTcpSocket並將socketDescriptor值賦予socket,並在socket斷開時告知此標識符app
2. 範例
![gif](http://static.javashuo.com/static/loading.gif)
源碼請見GitHub:QtOtherModuleExamplessocket
tcp_server.htcp
- #ifndef TCP_SERVER_H
- #define TCP_SERVER_H
- #include <QTcpServer>
- namespace tcp_server_private {
- class TcpServerPrivate;
- }
- class QTcpSocket;
- /**
- * @brief Tcp多客戶端服務器
- */
- class TcpServer : public QTcpServer {
- Q_OBJECT
- public:
- /**
- * @brief 構造函數
- * @param parent 父QObject
- */
- explicit TcpServer(QObject *parent = Q_NULLPTR);
- /**
- * @brief 析構函數
- * 非多線程模式行爲:關閉全部鏈接後析構
- * 多線程模式行爲:關閉全部鏈接及線程池後析構
- */
- ~TcpServer();
- signals:
- /**
- * @brief 客戶端連入
- * @param 鏈接句柄
- * @param socket指針
- */
- void ClientConnected(qintptr, QTcpSocket*);//發送新用戶鏈接信息
- /**
- * @brief socket已斷開鏈接
- * 若須要在socket後析構後進行操做的可鏈接此信號
- * @param 鏈接句柄
- */
- void ClientDisconnected(qintptr);
- /**
- * @brief 主動斷開鏈接信號
- * 若服務端想要主動斷開與客戶端鏈接將會發出此信號
- * 此信號發出這代表進行斷開操做不代表斷開成功,成功以SocketDisconnected信號爲準
- * @param 鏈接句柄
- */
- void InitiativeDisConnectClient(qintptr);
- protected slots:
- /**
- * @brief 客戶端已斷開槽
- * 此槽與客戶端的已斷開信號鏈接
- * @param handle
- */
- void ClientDisconnectedSlot(qintptr handle);
- protected:
- /**
- * @brief 重寫-有鏈接到來
- * 鏈接到來不必定鏈接,會根據maxPendingConnections決定是否鏈接
- * @param handle 鏈接句柄
- */
- virtual void incomingConnection(qintptr handle);
- private:
- tcp_server_private::TcpServerPrivate *private_;
- };
- #endif // TCP_SERVER_H
tcp_server.cpp函數
- #include "tcp_server.h"
- #include "tcp_server_private.h"
- //構造函數
- TcpServer::TcpServer(QObject *parent)
- : QTcpServer(parent),
- private_(new tcp_server_private::TcpServerPrivate) {
- }
- //析構函數
- TcpServer::~TcpServer() {
- for(tcp_server_private::TcpSocket *client : private_->clients.values()) {
- client->disconnectFromHost();
- auto handle = client->socketDescriptor();
- client->deleteLater();
- //告知其餘調用者 當前socket斷開,避免有須要在socket後執行的方法
- emit ClientDisconnected(handle);
- }
- if(private_)
- delete private_;
- this->close();
- }
- //重寫-有鏈接到來
- void TcpServer::incomingConnection(qintptr handle) {
- //超出最大練級數量關閉鏈接
- if (private_->clients.size() > maxPendingConnections()) {
- QTcpSocket tcp;
- tcp.setSocketDescriptor(handle);
- tcp.disconnectFromHost();
- return;
- }
- auto client_socket = new tcp_server_private::TcpSocket(handle);
- Q_ASSERT(client_socket->socketDescriptor() == handle);
- //socket斷開鏈接的信號與server槽鏈接
- connect(client_socket,
- &tcp_server_private::TcpSocket::ClientDisconnected,
- this,
- &TcpServer::ClientDisconnectedSlot);
- //主動斷開鏈接的操做
- connect(this,
- &TcpServer::InitiativeDisConnectClient,
- client_socket,
- &tcp_server_private::TcpSocket::DisconnectSocket);
- //map記錄
- private_->clients.insert(handle, client_socket);
- qDebug()<<handle<<"connected";
- emit ClientConnected(handle, client_socket);
- }
- //客戶端已斷開槽
- void TcpServer::ClientDisconnectedSlot(qintptr handle) {
- //map中移除
- private_->clients.remove(handle);
- qDebug()<<handle<<"disconnected";
- //發出信號
- emit ClientDisconnected(handle);
- }
privatepost
- #ifndef TCP_SERVER_PRIVATE_H
- #define TCP_SERVER_PRIVATE_H
- #include <QTcpSocket>
- namespace tcp_server_private {
- class TcpSocket : public QTcpSocket {
- Q_OBJECT
- public:
- /**
- * @brief 構造函數
- * @param socketDescriptor 鏈接句柄
- * @param parent 父QObject
- */
- TcpSocket(qintptr handle, QObject *parent = 0);
- signals:
- /*
- * 已斷開鏈接信號
- */
- void ClientDisconnected(qintptr);
- public slots:
- /**
- * @brief 斷開鏈接
- * @param handle 鏈接句柄
- */
- void DisconnectSocket(qintptr handle);
- private:
- qintptr handle_;
- };
- /**
- * @brief Tcp多客戶端服務器私有類
- */
- class TcpServerPrivate {
- public:
- QMap<int, TcpSocket *> clients; ///全部鏈接的map
- };
- }//tcp_server_private
- #endif // TCP_SERVER_PRIVATE_H
- //cpp
- #include "tcp_server_private.h"
- namespace tcp_server_private {
- //構造函數
- TcpSocket::TcpSocket(qintptr handle, QObject *parent)
- : QTcpSocket(parent), handle_(handle) {
- this->setSocketDescriptor(handle_);
- //斷開鏈接消息
- connect(this,&TcpSocket::disconnected,
- [&](){
- this->deleteLater();
- emit this->ClientDisconnected(handle_);
- });
- }
- //主動斷開鏈接槽
- void TcpSocket::DisconnectSocket(qintptr handle) {
- if (handle == handle_)
- disconnectFromHost();
- }
- }
- incomingConnection首先判斷是否超出最大鏈接數量,超出就斷開新連接,最大鏈接數量在maxPendingConnections方法獲取,而當前已鏈接client在自定義server的TcpServerPrivate::clients這個QMap記錄,此map記錄了socketDescriptor和socket指針的映射關係
- 若未達最大鏈接數量,則鏈接建立新的tcpsocket,並將socketDescriptor賦值到socket對象,最後發出ClientConnected信號,此信號帶有新連接的socket的指針,能夠用於自定義收發信號槽。
- socket對象的disconnected信號直接connect到了lambda表達式以發出新的信號,新信號ClientDisconnected,並帶有socketDescriptor句柄標識符,從而避免了socket斷開鏈接後不知道具體哪一個斷開
- socket在建立時均吧ClientDisconnected信號與server的ClientDisconnectedSlot槽鏈接,當有鏈接斷開時會動態維護map記錄
- server析構時以disconnectFromHost方法主動斷開和全部客戶端鏈接,若須要等客戶端先斷開能夠用waitForDisconnected
上述範例將TcpSocket定義在tcp_server_private命名空間不對外可見,且TcpServer返回值也是QTcpSocket,若須要繼承QTcpSocket作更多自定義須要將TcpSocket移出this