• 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实现多客户端连接
  • 相关阅读:
    EOF:getchar()函数返回的一个特殊值
    elementUI的el-select使用filterable无效
    element table单元格相关的样式
    关于前端权限的一点想法--续
    IE浏览器与PDF文件
    vue form表单数据无法修改
    computed数据变动,table加载不全
    vue插件开发(本地使用)
    element table
    对象删除(消耗时间验证)
  • 原文地址:https://www.cnblogs.com/techiel/p/8128007.html
Copyright © 2020-2023  润新知