一、QTcpServer 简介
QTcpServer 是 Qt 网络模块中的一个核心类,用于实现 基于 TCP 协议的服务端(Server),它负责监听端口、接收客户端连接请求,并通过 QTcpSocket 与客户端通信。
头文件:
#include <QTcpServer>
#include <QTcpSocket>
所属模块:
QT += network
二、常用成员函数的使用
2.1 void close()
作用:
- 停止当前的监听;
- 不再接受新的客户端连接;
- 但 不会主动断开已连接的客户端(你需要自行关闭相关的 QTcpSocket);
使用方式:
QTcpServer server;
// 启动监听
server.listen(QHostAddress::Any, 12345);
// 停止监听
server.close();
调用 close() 后,服务器不再监听新的连接。如果要重新监听,可以再次调用 listen()。
配合判断函数使用:
if (server.isListening()) {
server.close();
qDebug() << "Server closed.";
}
注意事项:
- close() 只是关闭监听端口,不会自动关闭通过 nextPendingConnection() 创建的 QTcpSocket;
- 如果要释放所有资源,应在 close() 之后手动删除或管理已连接的 socket。
2.2 QString errorString() const
作用:
- 当 QTcpServer::listen() 调用失败时,可以使用 errorString() 获取失败原因;
- 返回值是一个用户可读的字符串(QString),便于调试或显示在日志/界面中;
- 不适用于成功的监听,仅在发生错误时有效。
典型用法示例:
QTcpServer server;
if (!server.listen(QHostAddress::Any, 12345)) {
qDebug() << "Server failed to start:" << server.errorString();
} else {
qDebug() << "Server started on port" << server.serverPort();
}
常见输出示例:
- “The address is already in use”(端口已被占用)
- “Permission denied”(在受限端口启动服务)
- “Operation not permitted”(无权限)
- “Unknown error”
2.3 virtual bool hasPendingConnections() const
作用:
- 返回 true 表示有客户端连接已到达,但尚未被你用 nextPendingConnection() 拿出来;
- 返回 false 表示当前没有等待处理的连接;
- 常配合 nextPendingConnection() 使用,用于服务器主动轮询处理连接(非 newConnection 信号方式)。
常见使用场景:
场景 1:在主循环或定时器中轮询客户端连接
if (server.hasPendingConnections()) {
QTcpSocket* clientSocket = server.nextPendingConnection();
// 处理 socket...
}
场景 2:与 newConnection 信号搭配,增强安全性(防止 nextPendingConnection 为 nullptr)
void MyServer::onNewConnection() {
while (server.hasPendingConnections()) {
QTcpSocket* socket = server.nextPendingConnection();
if (socket) {
connect(socket, &QTcpSocket::readyRead, this, &MyServer::onReadyRead);
}
}
}
完整使用示例:
QTcpServer server;
server.listen(QHostAddress::Any, 12345);
QTimer* timer = new QTimer(this);
connect(timer, &QTimer::timeout, [&]() {
while (server.hasPendingConnections()) {
QTcpSocket* socket = server.nextPendingConnection();
qDebug() << "New client:" << socket->peerAddress();
connect(socket, &QTcpSocket::readyRead, [socket]() {
qDebug() << "Data:" << socket->readAll();
});
}
});
timer->start(100); // 每 100ms 检查新连接
2.4 bool isListening() const
作用:
- 返回 true 表示服务器已经成功调用 listen() 并正在监听端口;
- 返回 false 表示服务器当前未监听任何端口,例如尚未调用 listen(),或者已调用了 close() 停止监听。
使用示例:
示例 1:启动服务器前判断状态
if (!server.isListening()) {
if (!server.listen(QHostAddress::Any, 12345)) {
qDebug() << "Failed to listen:" << server.errorString();
} else {
qDebug() << "Server started.";
}
}
示例 2:停止监听前判断是否正在监听
if (server.isListening()) {
server.close();
qDebug() << "Server stopped.";
}
2.5 bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
作用:
- 在指定 IP 地址和端口上开启监听;
- 等待客户端连接(触发 newConnection() 信号);
- 返回 true 表示监听成功,false 表示失败(可配合 errorString() 查看原因)。
使用示例:
示例 1:监听所有网卡的 12345 端口
QTcpServer server;
if (!server.listen(QHostAddress::Any, 12345)) {
qDebug() << "Listen failed:" << server.errorString();
} else {
qDebug() << "Server started on port" << server.serverPort();
}
示例 2:监听本机(127.0.0.1)
server.listen(QHostAddress::LocalHost, 9999);
监听成功后该做什么?
连接 newConnection() 信号:
connect(&server, &QTcpServer::newConnection, [&]() {
QTcpSocket *client = server.nextPendingConnection();
// 处理客户端
});
2.6 int maxPendingConnections() const
- 表示服务器还没有调用 nextPendingConnection() 接收的连接最大数量;
- 超过该数量的新连接会被拒绝;
- 实际是 listen() 的 backlog 参数在 Qt 封装下的表现。
使用示例:
示例:获取最大挂起连接数
QTcpServer server;
qDebug() << "Default max pending connections:" << server.maxPendingConnections(); // 默认是 30
server.setMaxPendingConnections(100);
qDebug() << "Updated max pending connections:" << server.maxPendingConnections(); // 现在是 100
配合使用函数:
*2.7 virtual QTcpSocket nextPendingConnection()
作用:
- 当客户端连接到服务器时,QTcpServer 会触发 newConnection() 信号;
- 可以在信号槽中调用 nextPendingConnection() 来取出并处理这个连接;
- 返回一个指向新建 QTcpSocket 的指针,代表该客户端的连接;
- 多次调用可以获取多个挂起的连接(由 hasPendingConnections() 控制);
基本用法:
示例:处理新连接
connect(&server, &QTcpServer::newConnection, [&]() {
while (server.hasPendingConnections()) {
QTcpSocket* clientSocket = server.nextPendingConnection();
if (clientSocket) {
qDebug() << "New client from" << clientSocket->peerAddress();
connect(clientSocket, &QTcpSocket::readyRead, [clientSocket]() {
QByteArray data = clientSocket->readAll();
qDebug() << "Received:" << data;
clientSocket->write("Echo: " + data);
});
connect(clientSocket, &QTcpSocket::disconnected, clientSocket, &QObject::deleteLater);
}
}
});
2.8 void QTcpServer::pauseAccepting()
作用:
- 暂时“挂起”新连接的接收;
- QTcpServer 不会再触发 newConnection() 信号;
- 内部仍然监听端口,只是不处理新连接;
- 可通过 resumeAccepting() 恢复正常接收。
使用示例:
示例:服务繁忙时暂停接收
QTcpServer server;
// 假设客户端过多时调用
if (tooManyClients()) {
server.pauseAccepting();
qDebug() << "Server paused accepting new connections.";
}
// 一段时间后或条件满足时恢复
server.resumeAccepting();
qDebug() << "Server resumed accepting new connections.";
注意事项:
2.9 void QTcpServer::resumeAccepting()
功能简介:
- 当调用 pauseAccepting() 后,服务器会停止处理传入连接;
- 此时客户端仍然可以连接服务器的端口,但 incomingConnection() 和 newConnection() 信号不会触发;
- 当调用 resumeAccepting() 时,服务器恢复监听并处理新的连接请求。
示例:每秒只允许一个连接(含 resumeAccepting)
// 在 MyServer.h 中
QTimer *m_acceptTimer;
bool m_paused;
// 在 MyServer.cpp 中
MyServer::MyServer(QObject *parent)
: QTcpServer(parent), m_paused(false) {
m_acceptTimer = new QTimer(this);
m_acceptTimer->setInterval(1000); // 每秒一次
connect(m_acceptTimer, &QTimer::timeout, this, [this]() {
if (m_paused) {
this->resumeAccepting(); // 恢复接受连接
m_paused = false;
qDebug() << "Resumed accepting connections.";
}
});
m_acceptTimer->start();
}
void MyServer::incomingConnection(qintptr socketDescriptor) {
QTcpSocket *socket = new QTcpSocket(this);
socket->setSocketDescriptor(socketDescriptor);
qDebug() << "New connection from:" << socket->peerAddress();
// 立即暂停后续连接
pauseAccepting();
m_paused = true;
}
2.10 QHostAddress serverAddress() const
**函数作用:**回服务器监听的本地 IP 地址(QHostAddress 类型),它表示服务器是在哪个地址上监听的连接。
使用示例:
QTcpServer server;
if (server.listen(QHostAddress::AnyIPv4, 12345)) {
qDebug() << "Listening on:"
<< server.serverAddress().toString()
<< ":" << server.serverPort();
} else {
qDebug() << "Failed to listen:" << server.errorString();
}
监听本地并输出地址:
QTcpServer server;
if (server.listen(QHostAddress::LocalHost, 0)) {
qDebug() << "Server running at:"
<< server.serverAddress().toString()
<< "on port" << server.serverPort();
}
输出:
Server running at: "127.0.0.1" on port 54321
2.11 QAbstractSocket::SocketError QTcpServer::serverError() const
作用:
- 返回最近一次调用 listen()、setSocketDescriptor() 等操作中发生的错误枚举值;
- 搭配 errorString() 使用,可以获得详细的错误描述;
- 仅在失败时调用有意义(如 listen() 返回 false 时);
- 错误类型为 QAbstractSocket::SocketError 枚举。
常见错误类型(枚举值):
完整列表见官方文档:QAbstractSocket::SocketError
示例代码:
QTcpServer server;
if (!server.listen(QHostAddress::Any, 12345)) {
qDebug() << "Listen failed!";
qDebug() << "Error code:" << server.serverError();
qDebug() << "Error message:" << server.errorString();
}
2.12 quint16 serverPort() const
作用:
- 返回服务器当前监听的端口号;
- 通常在调用 listen() 成功之后使用;
- 如果 listen() 中传入的端口号是 0(表示让系统自动分配),则通过此函数可以获取实际分配的端口;
- 返回值类型为 quint16(即无符号16位整数,范围为 0~65535)。
使用示例:
示例 1:指定端口监听
QTcpServer server;
if (server.listen(QHostAddress::Any, 12345)) {
qDebug() << "Server is listening on port:" << server.serverPort();
}
示例 2:动态分配端口
QTcpServer server;
if (server.listen(QHostAddress::LocalHost, 0)) {
qDebug() << "Server is listening on dynamically assigned port:"
<< server.serverPort();
}
2.13 void setMaxPendingConnections(int numConnections)
作用说明:
- 控制“连接等待队列”的长度;
- 当客户端连接服务器但尚未被 accept()(即 nextPendingConnection() 处理)时,它们会进入“等待队列”;
- 如果等待队列已满,操作系统会拒绝新的连接尝试;
- 默认为 30 个连接(Qt 默认值)。
使用示例:
QTcpServer server;
server.setMaxPendingConnections(100); // 设置最多等待100个连接
if (server.listen(QHostAddress::Any, 12345)) {
qDebug() << "Server is listening. Max pending:" << server.maxPendingConnections();
} else {
qDebug() << "Listen failed:" << server.errorString();
}
2.14 bool setSocketDescriptor(qintptr socketDescriptor)
作用说明:
- 用于将一个外部创建的监听 socket(底层 socket fd)交给 QTcpServer 管理;
- 可用于支持“继承 socket”,如服务热更新(fork 子进程继承父进程监听的 socket);
- 成功绑定后,QTcpServer 会像自己调用 listen() 一样工作;
- 参数 socketDescriptor 是一个平台相关的 socket 文件描述符(int 或 SOCKET)。
使用示例:
示例:使用已有 socket 描述符初始化 QTcpServer
QTcpServer *server = new QTcpServer();
int nativeSocket = ::socket(AF_INET, SOCK_STREAM, 0);
// 配置绑定端口、listen 等(略)
// ...
// 假设 nativeSocket 已处于 listen 状态
if (!server->setSocketDescriptor(nativeSocket)) {
qDebug() << "Failed to set socket descriptor:"
<< server->errorString();
} else {
qDebug() << "Server is using existing socket descriptor!";
}
2.15 qintptr QTcpServer::socketDescriptor() const
作用说明:
- 返回当前 QTcpServer 使用的操作系统原生 socket 描述符;
- 适用于需要与非 Qt 网络库协同工作,或进行底层操作(如跨进程传递 socket、设置 socket 选项等);
- 与 setSocketDescriptor(qintptr) 配合使用,可以实现 socket 的“迁移”或“重用”;
- qintptr 是一个平台无关的整数类型,表示 native socket(在 Linux 是 int,在 Windows 是 SOCKET)。
使用示例:
QTcpServer server;
if (server.listen(QHostAddress::Any, 12345)) {
qintptr fd = server.socketDescriptor();
qDebug() << "Server socket descriptor:" << fd;
// 可以传递 fd 给系统调用
}
2.16 bool QTcpServer::waitForNewConnection(int msec = 0, bool *timedOut = nullptr)
作用:用于在阻塞模式下等待一个新的客户端连接的到来,常用于非事件驱动(非 signal-slot)或子线程中手动接收连接。
参数说明:
使用示例:
示例 1:等待连接 3 秒
QTcpServer server;
if (server.listen(QHostAddress::Any, 12345)) {
bool timeout = false;
if (server.waitForNewConnection(3000, &timeout)) {
QTcpSocket *client = server.nextPendingConnection();
qDebug() << "Client connected from" << client->peerAddress();
} else {
if (timeout)
qDebug() << "Waited 3 seconds: no client connected.";
else
qDebug() << "Error:" << server.errorString();
}
}
示例 2:在子线程中轮询连接
while (running) {
if (server.waitForNewConnection(1000)) {
QTcpSocket *client = server.nextPendingConnection();
handleClient(client);
}
}
三、信号函数的使用
3.1 void QTcpServer::acceptError(QAbstractSocket::SocketError socketError)
作用说明:
- 信号,在服务器尝试接收连接(即内部 accept() 系统调用)失败时触发;
- 可通过连接该信号,打印错误日志、报警、记录等;
- 参数 socketError 是 QAbstractSocket::SocketError 枚举类型,表示具体的错误类型,如 SocketResourceError、SocketAccessError 等。
常见错误类型(QAbstractSocket::SocketError):
使用示例:
#include <QTcpServer>
#include <QDebug>
class MyServer : public QTcpServer {
Q_OBJECT
public:
MyServer(QObject *parent = nullptr) : QTcpServer(parent) {
connect(this, &QTcpServer::acceptError, this, &MyServer::onAcceptError);
}
private slots:
void onAcceptError(QAbstractSocket::SocketError socketError) {
qDebug() << "Accept error:" << socketError << "-" << this->errorString();
}
};
3.2 void QTcpServer::newConnection()
作用说明:
- 当有新的客户端尝试连接,并被成功 accept() 后,Qt 会自动发出该信号;
- 此时可以调用 nextPendingConnection() 来获取新的 QTcpSocket;
- 这是 Qt 中推荐的事件驱动方式 来处理新连接,避免使用阻塞式 waitForNewConnection()。
使用示例:
// MyServer.h
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
class MyServer : public QTcpServer {
Q_OBJECT
public:
MyServer(QObject *parent = nullptr) : QTcpServer(parent) {
connect(this, &QTcpServer::newConnection, this, &MyServer::handleNewConnection);
}
private slots:
void handleNewConnection() {
QTcpSocket *client = nextPendingConnection();
qDebug() << "New client connected from" << client->peerAddress().toString();
connect(client, &QTcpSocket::readyRead, this, [client]() {
QByteArray data = client->readAll();
qDebug() << "Received:" << data;
client->write("Echo: " + data);
});
connect(client, &QTcpSocket::disconnected, client, &QTcpSocket::deleteLater);
}
};
四、虚函数的使用
4.1 void QTcpServer::addPendingConnection(QTcpSocket *socket)
作用:用于手动将一个已经建立连接的 QTcpSocket 添加到服务器的等待连接队列中,从而触发 newConnection() 信号,供后续通过 nextPendingConnection() 获取。
这个函数通常用于自定义服务端行为,例如:
使用示例:
自定义 incomingConnection() 并使用 addPendingConnection()
class CustomTcpServer : public QTcpServer {
Q_OBJECT
public:
CustomTcpServer(QObject *parent = nullptr) : QTcpServer(parent) {}
protected:
void incomingConnection(qintptr socketDescriptor) override {
QTcpSocket *socket = new QTcpSocket(this);
if (socket->setSocketDescriptor(socketDescriptor)) {
qDebug() << "Custom socket accepted from:" << socket->peerAddress();
addPendingConnection(socket); // 添加到等待队列,触发 newConnection()
} else {
delete socket;
}
}
};
4.2 virtual void QTcpServer::incomingConnection(qintptr socketDescriptor)
作用:允许你自定义服务器如何处理新连接的底层行为。它通常在你需要更精细控制 socket 创建方式、实现自定义 socket 类型或与多线程结合时使用。
参数说明:
- qintptr socketDescriptor:这是一个原始 socket 文件描述符(FD),可以用来构造一个 QTcpSocket。
- 该函数在新连接到达时由 Qt 的事件循环自动调用。
典型使用示例:自定义线程分发
// CustomServer.h
#include <QTcpServer>
#include <QTcpSocket>
#include <QThread>
#include <QDebug>
class ClientHandler : public QThread {
Q_OBJECT
public:
ClientHandler(qintptr socketDescriptor, QObject *parent = nullptr)
: QThread(parent), socketDescriptor(socketDescriptor) {}
protected:
void run() override {
QTcpSocket socket;
if (!socket.setSocketDescriptor(socketDescriptor)) {
qDebug() << "Socket descriptor error";
return;
}
connect(&socket, &QTcpSocket::readyRead, [&]() {
QByteArray data = socket.readAll();
socket.write("Echo: " + data);
});
connect(&socket, &QTcpSocket::disconnected, &socket, &QTcpSocket::deleteLater);
exec(); // 进入线程事件循环
}
private:
qintptr socketDescriptor;
};
class CustomServer : public QTcpServer {
Q_OBJECT
protected:
void incomingConnection(qintptr socketDescriptor) override {
qDebug() << "New incoming connection:" << socketDescriptor;
ClientHandler *handler = new ClientHandler(socketDescriptor);
handler->start(); // 每个连接交给新线程处理
}
};
五、连接多客户端-服务端示例
结构说明:
- MyServer:监听端口,接受连接;
- ClientHandler:线程对象,处理每个客户端 socket;
- 每个客户端连接一个线程。
ClientHandler.h
#pragma once
#include <QThread>
#include <QTcpSocket>
class ClientHandler : public QThread {
Q_OBJECT
public:
ClientHandler(QTcpSocket* socket, QObject* parent = nullptr);
~ClientHandler();
protected:
void run() override;
private:
QTcpSocket* m_socket;
};
ClientHandler.cpp
#include "ClientHandler.h"
#include <QDebug>
ClientHandler::ClientHandler(QTcpSocket* socket, QObject* parent)
: QThread(parent), m_socket(socket) {}
ClientHandler::~ClientHandler() {
if (m_socket) {
m_socket->disconnectFromHost();
m_socket->deleteLater();
}
}
void ClientHandler::run() {
connect(m_socket, &QTcpSocket::readyRead, [=]() {
QByteArray data = m_socket->readAll();
qDebug() << "Thread" << QThread::currentThreadId() << "Received:" << data;
m_socket->write("Echo: " + data);
});
connect(m_socket, &QTcpSocket::disconnected, [=]() {
qDebug() << "Client disconnected";
m_socket->deleteLater();
quit();
});
exec(); // 启动事件循环
}
MyServer.h
#pragma once
#include <QTcpServer>
class MyServer : public QTcpServer {
Q_OBJECT
public:
explicit MyServer(QObject* parent = nullptr);
bool startServer(quint16 port);
protected:
void incomingConnection(qintptr socketDescriptor) override;
};
MyServer.cpp
#include "MyServer.h"
#include "ClientHandler.h"
#include <QTcpSocket>
#include <QDebug>
MyServer::MyServer(QObject* parent)
: QTcpServer(parent) {}
bool MyServer::startServer(quint16 port) {
if (!listen(QHostAddress::Any, port)) {
qDebug() << "Server failed:" << errorString();
return false;
}
qDebug() << "Server started on port" << port;
return true;
}
void MyServer::incomingConnection(qintptr socketDescriptor) {
QTcpSocket* socket = new QTcpSocket;
if (!socket->setSocketDescriptor(socketDescriptor)) {
qDebug() << "Failed to set socket descriptor";
socket->deleteLater();
return;
}
qDebug() << "New client from" << socket->peerAddress().toString();
auto* handler = new ClientHandler(socket);
handler->start(); // 启动线程
}
main.cpp
#include <QCoreApplication>
#include "MyServer.h"
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
MyServer server;
if (!server.startServer(12345)) {
return -1;
}
return a.exec();
}