QTcpServer实现多客户端连接
本文标题: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
- virtual void incomingConnection(qintptr socketDescriptor)
当有client连接时,首先是此方法被调用,可自行在此方法内建立QTcpSocket并将socketDescriptor值赋予socket,并在socket断开时告知此标识符
2. 范例

源码请见GitHub:QtOtherModuleExamples
tcp_server.h
- #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);
- }
private
- #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移出
QTcpServer实现多客户端连接的更多相关文章
- 高版本->低版本迁移,低版本客户端连接高版本数据库EXP导出报错EXP-00008,ORA-01455,EXP-00000
生产环境: 源数据库:RHEL + Oracle 11.2.0.3 目标数据库:HP-UX + Oracle 10.2.0.4 需求:迁移部分表 11.2.0.3-->10.2.0.4,若 ...
- Redis客户端连接池
使用场景 对于一些大对象,或者初始化过程较长的可复用的对象,我们如果每次都new对象出来,那么意味着会耗费大量的时间. 我们可以将这些对象缓存起来,当接口调用完毕后,不是销毁对象,当下次使用的时候,直 ...
- 【RabbitMQ】CentOS安装RabbitMQ,及简单的Java客户端连接
在CentOS安装 因Rabbit MQ使用Erlang,所以需要先安装Erlang,安装过程中可能会遇到种种问题,可参考CentOS 6.5安装Erlang/OTP 17.0.然后就可以安装MQ了. ...
- redis客户端连接异常
本文参考:http://mdba.cn/2015/04/02/redistwemproxy-%e5%ae%a2%e6%88%b7%e7%ab%af%e8%bf%9e%e6%8e%a5%e5%bc%82 ...
- Mysql从客户端连接服务器连不上的问题
Mysql从客户端连接服务器连不上的问题 公司要用Mysql做一个测试,开始在自己的本地建一个Mysql数据库自己本地的程序再连上去,没有遇到过连接不上的问题.这次数据库在服务器上,从本地客户端连 ...
- Mark一下,一上午就这么过去了,关于客户端连接oracle10G的问题
Mark一下,一上午就这么过去了,关于客户端连接oracle10G的问题 正常的客户端PLSQL和Navicat都可以正常连接Oracle(局域网内),但代码生成器和VS2015死活连不上,在网上找了 ...
- atitit.客户端连接oracle数据库的方式总结
客户端连接oracle数据库的方式总结 目录 Java程序连接一般使用jar驱动连接..... 桌面GUI一般采取c语言驱动oci.dll 直接连接... 间接连接(需要配置tns及其envi var ...
- 今天遇到的一个问题(windows的ssh客户端连接不到虚拟机Ubuntu)
今天比较郁闷,想用windows上的ssh客户端连接虚拟机中的Ubuntu. 但是死活连不上,之前是能脸上的,所以比较郁闷. 我首先在windows上ping Ubuntu的ip地址,竟然发不了数据包 ...
- Redis基础知识之————如何处理客户端连接
redis 连接建立 Redis Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作: 首先,客户端 ...
随机推荐
- Altera三速以太网IP核使用(下篇)--- 百兆网接口设计与使用
MAC IP核的主要作用是:实现数据链路层协议,分为TX方向与RX方向,TX方向实现的是在原包文的前面加上7个55和1个D5,RX方向则相反.在使用这个 MAC IP核之前,首先确认下自己使用的网卡是 ...
- 那些年我们追过的C#奇葩关键字——忐忑
说到中国的歌坛,不能光说张学友这种大咖吧,我看那些怪咖更给力,比如我们的龚琳娜童鞋,一首神曲<忐忑>唱的那叫不可收拾,而且听到的改编版本更多,每一次都是心怀忐忑,就像C#里的那些关键字 说 ...
- 《JAVA程序设计》 20155208 实验四 Android程序设计
<JAVA程序设计> 20155208 实验四 Android程序设计 实验一: 实验要求: Android Stuidio的安装测试: 参考<Java和Android开发学习指南( ...
- 20155229实验二 《Java面向对象程序设计》实验报告
20155229实验二 <Java面向对象程序设计>实验报告 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 ...
- 20155307 《Java程序设计》课堂实践项目MyOD
一开始没理解老师的要求,交的截图是错误的. import java.io.FileInputStream; import java.io.IOException; import java.io.Inp ...
- GridSQL--Stado 学习初步
磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL集群方案相关索引页 回到顶级页面:PostgreSQL索引页 作者 高健@博客园 luckyjackgao ...
- Google Chrome插件分享
前言 浏览器是大家日常使用最多的工具之一,对于程序员来说,Google Chrome浏览器当然是大家优选的最爱之一.面对Chrome丰富的插件真的是爱不释手,如何把自己的Chrome调教成自己心仪的样 ...
- CSP201312-3:最大的矩形
引言:CSP(http://www.cspro.org/lead/application/ccf/login.jsp)是由中国计算机学会(CCF)发起的"计算机职业资格认证"考试, ...
- windows python MySQL-python安装过程
问题表述: pip install MySQL-python==1.2.5 出现如下报错: C:\Users\Administrator\AppData\Local\Programs\Common\M ...
- Office 365 Powershell 连接命令
国内版 第一步: Import-Module msonline Connect-MsolService 输入用户名密码 第二步: Get-MsolUser" 第三步: Set-Executi ...