QTcpServer實現多客戶端鏈接

版權聲明:若無來源註明, Techie亮博客文章均爲原創。 轉載請以連接形式標明本文標題和地址:
本文標題:QTcpServer實現多客戶端鏈接     本文地址: https://www.techieliang.com/2017/12/760/

1. 介紹

QTcpServer使用請見:QTcpSocket-Qt使用Tcp通信實現服務端和客戶端git

QTcpServer類默認提供的只有無參數的newConnection的信號,這樣雖然知道有人鏈接了,而且能夠經過nextPendingConnection獲取鏈接的socket,但並不便於管理,尤爲是在鏈接斷開之後沒法判斷具體那個斷開了,由於QTcpSocket只提供了無參的disconnected信號。。。github

這樣就算在newConnection是存儲一個list或者map,也沒法在disconnected是知道具體是那一項斷開鏈接,給不一樣的QTcpSocket的信號指向不一樣的槽。服務器

實際上socket有本身的句柄,並經過下述函數在初步鏈接時就賦予了對應的socketDescriptor多線程

  1. virtual void incomingConnection(qintptr socketDescriptor)

當有client鏈接時,首先是此方法被調用,可自行在此方法內創建QTcpSocket並將socketDescriptor值賦予socket,並在socket斷開時告知此標識符app

2. 範例

gif

源碼請見GitHub:QtOtherModuleExamplessocket

tcp_server.htcp

  1. #ifndef TCP_SERVER_H
  2. #define TCP_SERVER_H
  3. #include <QTcpServer>
  4. namespace tcp_server_private {
  5. class TcpServerPrivate;
  6. }
  7. class QTcpSocket;
  8. /**
  9. * @brief Tcp多客戶端服務器
  10. */
  11. class TcpServer : public QTcpServer {
  12. Q_OBJECT
  13. public:
  14. /**
  15. * @brief 構造函數
  16. * @param parent 父QObject
  17. */
  18. explicit TcpServer(QObject *parent = Q_NULLPTR);
  19. /**
  20. * @brief 析構函數
  21. * 非多線程模式行爲:關閉全部鏈接後析構
  22. * 多線程模式行爲:關閉全部鏈接及線程池後析構
  23. */
  24. ~TcpServer();
  25. signals:
  26. /**
  27. * @brief 客戶端連入
  28. * @param 鏈接句柄
  29. * @param socket指針
  30. */
  31. void ClientConnected(qintptr, QTcpSocket*);//發送新用戶鏈接信息
  32. /**
  33. * @brief socket已斷開鏈接
  34. * 若須要在socket後析構後進行操做的可鏈接此信號
  35. * @param 鏈接句柄
  36. */
  37. void ClientDisconnected(qintptr);
  38. /**
  39. * @brief 主動斷開鏈接信號
  40. * 若服務端想要主動斷開與客戶端鏈接將會發出此信號
  41. * 此信號發出這代表進行斷開操做不代表斷開成功,成功以SocketDisconnected信號爲準
  42. * @param 鏈接句柄
  43. */
  44. void InitiativeDisConnectClient(qintptr);
  45. protected slots:
  46. /**
  47. * @brief 客戶端已斷開槽
  48. * 此槽與客戶端的已斷開信號鏈接
  49. * @param handle
  50. */
  51. void ClientDisconnectedSlot(qintptr handle);
  52. protected:
  53. /**
  54. * @brief 重寫-有鏈接到來
  55. * 鏈接到來不必定鏈接,會根據maxPendingConnections決定是否鏈接
  56. * @param handle 鏈接句柄
  57. */
  58. virtual void incomingConnection(qintptr handle);
  59. private:
  60. tcp_server_private::TcpServerPrivate *private_;
  61. };
  62. #endif // TCP_SERVER_H

tcp_server.cpp函數

  1. #include "tcp_server.h"
  2. #include "tcp_server_private.h"
  3. //構造函數
  4. TcpServer::TcpServer(QObject *parent)
  5. : QTcpServer(parent),
  6. private_(new tcp_server_private::TcpServerPrivate) {
  7. }
  8. //析構函數
  9. TcpServer::~TcpServer() {
  10. for(tcp_server_private::TcpSocket *client : private_->clients.values()) {
  11. client->disconnectFromHost();
  12. auto handle = client->socketDescriptor();
  13. client->deleteLater();
  14. //告知其餘調用者 當前socket斷開,避免有須要在socket後執行的方法
  15. emit ClientDisconnected(handle);
  16. }
  17. if(private_)
  18. delete private_;
  19. this->close();
  20. }
  21. //重寫-有鏈接到來
  22. void TcpServer::incomingConnection(qintptr handle) {
  23. //超出最大練級數量關閉鏈接
  24. if (private_->clients.size() > maxPendingConnections()) {
  25. QTcpSocket tcp;
  26. tcp.setSocketDescriptor(handle);
  27. tcp.disconnectFromHost();
  28. return;
  29. }
  30. auto client_socket = new tcp_server_private::TcpSocket(handle);
  31. Q_ASSERT(client_socket->socketDescriptor() == handle);
  32. //socket斷開鏈接的信號與server槽鏈接
  33. connect(client_socket,
  34. &tcp_server_private::TcpSocket::ClientDisconnected,
  35. this,
  36. &TcpServer::ClientDisconnectedSlot);
  37. //主動斷開鏈接的操做
  38. connect(this,
  39. &TcpServer::InitiativeDisConnectClient,
  40. client_socket,
  41. &tcp_server_private::TcpSocket::DisconnectSocket);
  42. //map記錄
  43. private_->clients.insert(handle, client_socket);
  44. qDebug()<<handle<<"connected";
  45. emit ClientConnected(handle, client_socket);
  46. }
  47. //客戶端已斷開槽
  48. void TcpServer::ClientDisconnectedSlot(qintptr handle) {
  49. //map中移除
  50. private_->clients.remove(handle);
  51. qDebug()<<handle<<"disconnected";
  52. //發出信號
  53. emit ClientDisconnected(handle);
  54. }

privatepost

  1. #ifndef TCP_SERVER_PRIVATE_H
  2. #define TCP_SERVER_PRIVATE_H
  3. #include <QTcpSocket>
  4. namespace tcp_server_private {
  5. class TcpSocket : public QTcpSocket {
  6. Q_OBJECT
  7. public:
  8. /**
  9. * @brief 構造函數
  10. * @param socketDescriptor 鏈接句柄
  11. * @param parent 父QObject
  12. */
  13. TcpSocket(qintptr handle, QObject *parent = 0);
  14. signals:
  15. /*
  16. * 已斷開鏈接信號
  17. */
  18. void ClientDisconnected(qintptr);
  19. public slots:
  20. /**
  21. * @brief 斷開鏈接
  22. * @param handle 鏈接句柄
  23. */
  24. void DisconnectSocket(qintptr handle);
  25. private:
  26. qintptr handle_;
  27. };
  28. /**
  29. * @brief Tcp多客戶端服務器私有類
  30. */
  31. class TcpServerPrivate {
  32. public:
  33. QMap<int, TcpSocket *> clients; ///全部鏈接的map
  34. };
  35. }//tcp_server_private
  36. #endif // TCP_SERVER_PRIVATE_H
  37. //cpp
  38. #include "tcp_server_private.h"
  39. namespace tcp_server_private {
  40. //構造函數
  41. TcpSocket::TcpSocket(qintptr handle, QObject *parent)
  42. : QTcpSocket(parent), handle_(handle) {
  43. this->setSocketDescriptor(handle_);
  44. //斷開鏈接消息
  45. connect(this,&TcpSocket::disconnected,
  46. [&](){
  47. this->deleteLater();
  48. emit this->ClientDisconnected(handle_);
  49. });
  50. }
  51. //主動斷開鏈接槽
  52. void TcpSocket::DisconnectSocket(qintptr handle) {
  53. if (handle == handle_)
  54. disconnectFromHost();
  55. }
  56. }
  • 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

轉載請以連接形式標明本文標題和地址: Techie亮博客 » QTcpServer實現多客戶端鏈接
相關文章
相關標籤/搜索