此文章只实现简单的多客户的TCP 的 Qt 网络通信,仅供参考。
单客户端TCP通信请参考
基于 TCP 的 Qt 网络通信
使用线程池实现服务端同时处理多个客户端操作,每当有一个客户端连接到服务端,则服务端创建一个子线程与其客户端通信,以此实现实现处理多个客户端。当客户端断开连接时,及关闭对应的通信套接字,并释放对应子线程资源;
在 Qt 中使用线程池需要先创建任务,添加到线程池中的每一个任务都需要是一个 QRunnable 类型,因此在程序中需要创建子类继承 QRunnable 这个类,然后重写 run() 方法,在这个函数中编写要在线程池中执行的任务,并将这个子类对象传递给线程池,这样任务就可以被线程池中的某个工作的线程处理掉了。
1. QTcpServer
1.1 通信流程
创建套接字服务器 QTcpServer 对象
通过 QTcpServer 对象设置监听,即:QTcpServer::listen()
基于 QTcpServer::newConnection() 信号检测是否有新的客户端连接
如果有新的客户端连接:
1)判断是否有未使用的通信队列;
2)将得到的通信套接字赋值给一个未使用的通信套接字;
3)将当前通信套接字加入到线程池中;
再子线程中使用套接字对象 QTcpSocket 和客户端进行通信
断开连接时释放队列中对应的通信套接字;
1.2 代码片段
服务器端的窗口界面如下图所示:
runnable.h
typedef struct _SOCKETINFO
{
int index;
int fd;
QTcpSocket* socket;
_SOCKETINFO(){
index = -1;
fd = 0;
socket = nullptr;
}
}SOCKETINFO;
class Runnable : public QObject,public QRunnable
{
Q_OBJECT
public:
explicit Runnable(SOCKETINFO socket,QObject *parent = nullptr);
void run() override;
private slots:
void slotDisconnected();
signals:
void sigDisconnected(int index);
private:
SOCKETINFO m_socket;
};
runnable.cpp
#include "runnable.h"
Runnable::Runnable(SOCKETINFO socket, QObject *parent) : m_socket(socket), QObject(parent)
{
this->setAutoDelete(false);
}
void Runnable::run()
{
// 检测是否有客户端数据
connect(m_socket.socket, &QTcpSocket::readyRead, this, [=]()
{
// 接收数据
QString recvMsg = m_socket.socket->readAll();
m_socket.socket->write(recvMsg.toUtf8());
});
// 客户端断开了连接
connect(m_socket.socket, &QTcpSocket::disconnected, this, &Runnable::slotDisconnected);
}
void Runnable::slotDisconnected()
{
// 客户端断开了连接
emit sigDisconnected(m_socket.index);
this->deleteLater();
}
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_pushButton_clicked();
void nextPendingConnection();
void slotDisconnected(int index);
private:
Ui::MainWindow *ui;
QTcpServer* m_server;
SOCKETINFO m_tcpConn[128];
};
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle(QStringLiteral("TCP - 服务器"));
// 设置 QThreadPool 线程池最大线程个数
QThreadPool::globalInstance()->setMaxThreadCount(10);
// 创建 QTcpServer 对象
m_server = new QTcpServer(this);
// 检测是否有新的客户端连接
connect(m_server, &QTcpServer::newConnection, this, &MainWindow::nextPendingConnection);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
unsigned short port = ui->lineEdit->text().toInt();
// 设置服务器监听
m_server->listen(QHostAddress::Any, port);
ui->pushButton->setEnabled(false);
}
void MainWindow::nextPendingConnection()
{
for(int i=0;i<128;i++)
{
if(m_tcpConn[i].fd == 0)
{
m_tcpConn[i].index = i;
m_tcpConn[i].fd = 1;
m_tcpConn[i].socket = m_server->nextPendingConnection();
ui->textBrowser->append(QStringLiteral("成功和客户端建立了新的连接..."));
Runnable *run = new Runnable(m_tcpConn[i]);
connect(run,&Runnable::sigDisconnected,this,&MainWindow::slotDisconnected);
QThreadPool::globalInstance()->start(run);
break;
}
}
}
void MainWindow::slotDisconnected(int index)
{
ui->textBrowser->append(QStringLiteral("客户端断开连接..."));
m_tcpConn[index].index = -1;
m_tcpConn[index].fd = 0;
m_tcpConn[index].socket->deleteLater();
m_tcpConn[index].socket = nullptr;
}
2 客户端
客户端代码请参照基于 TCP 的 Qt 网络通信中的客户端代码;