Qt开发:QTcpServer的详解

一、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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值