QTcpServer實現多客戶端連接


博客地址已更改,文章數量較多不便批量修改,若想訪問源文請到 coologic博客 查閱,網址:www.coologic.cn

如本文記錄地址為 techieliang.com/A/B/C/ 請改為 www.coologic.cn/A/B/C/ 即可查閱

 

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

1. 介紹

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

QTcpServer類默認提供的只有無參數的newConnection的信號,這樣雖然知道有人連接了,並且可以通過nextPendingConnection獲取連接的socket,但並不便於管理,尤其是在連接斷開以后無法判斷具體那個斷開了,因為QTcpSocket只提供了無參的disconnected信號。。。

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

實際上socket有自己的句柄,並通過下述函數在初步連接時就賦予了對應的socketDescriptor

  1. virtual void incomingConnection(qintptr socketDescriptor)

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

2. 范例

gif

源碼請見GitHub:QtOtherModuleExamples

tcp_server.h

  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. }

private

  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移出

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM