QT TCP网络编程

一、前言

在网络编程的世界中,TCP(传输控制协议)是最重要、最常用的协议之一。无论是即时通讯、文件传输还是在线游戏,TCP都扮演着关键角色。Qt作为跨平台的C++框架,提供了强大且易用的网络模块,让TCP编程变得简单高效

二、概念

TCP(Transmission Control Protocol)是一种面向连接、可靠、基于字节流的传输层通信协议。想象一下打电话的过程:

  • 面向连接:拨号 → 对方接听 → 开始通话(建立连接)

  • 可靠传输:确保你说的每一句话对方都能听到(数据不丢失)

  • 顺序保证:按说话顺序传达(数据顺序正确)

三、IP地址

 

3.2、IP地址的查找

QStringList MainWindow::getLocalIPAddresses()
{
    QStringList ipList;
    ui->comboBox->clear();  // 清空原有内容
    
    // 1. 添加特殊地址选项
    ui->comboBox->addItem("127.0.0.1 (本地回环)");
    ui->comboBox->addItem("0.0.0.0 (所有接口)");
    
    // 2. 获取所有网络接口
    QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
    
    for (const QNetworkInterface &interface : interfaces) {
        // 跳过非活动接口
        if (!(interface.flags() & QNetworkInterface::IsUp)) {
            continue;
        }
        
        // 跳过回环接口(127.0.0.1)
        if (interface.flags() & QNetworkInterface::IsLoopBack) {
            continue;
        }
        
        // 获取接口的所有IP地址
        QList<QNetworkAddressEntry> entries = interface.addressEntries();
        
        for (const QNetworkAddressEntry &entry : entries) {
            QHostAddress ip = entry.ip();
            
            // 只添加IPv4地址
            if (ip.protocol() == QAbstractSocket::IPv4Protocol) {
                QString ipString = ip.toString();
                
                // 格式化显示:IP + 接口名
                QString displayText = QString("%1 (%2)")
                                         .arg(ipString)
                                         .arg(interface.humanReadableName());
                
                ui->comboBox->addItem(displayText, ipString);
                ipList.append(ipString);
            }
        }
    }
    
    // 如果没有找到IP地址,添加默认选项
    if (ipList.isEmpty()) {
        ui->comboBox->addItem("127.0.0.1 (默认)");
        ipList.append("127.0.0.1");
    }
    
    return ipList;  // 返回所有找到的IP地址
}

三、QT中的TCP核心类

QTcpServer​ 监听连接,接受客户端请求
QTcpSocket 建立连接,发送接收数据

 

3.1、服务器端构建

// server.h
#include <QTcpServer>
#include <QTcpSocket>
#include <QObject>

class TcpServer : public QObject
{
    Q_OBJECT
public:
    explicit TcpServer(QObject *parent = nullptr);
    
    bool startServer(quint16 port = 8080);//初始值端口赋值为8080
    void stopServer();

private slots:
    void onNewConnection();
    void onClientReadyRead();
    void onClientDisconnected();

private:
    QTcpServer *server;
    QList<QTcpSocket*> clients;  // 存储所有客户端连接
};



// server.cpp
#include "server.h"
#include <QDebug>

TcpServer::TcpServer(QObject *parent) : QObject(parent)
{
    server = new QTcpServer(this);
    
    // 连接新连接信号
    connect(server, &QTcpServer::newConnection, 
            this, &TcpServer::onNewConnection);
}

bool TcpServer::startServer(quint16 port)
{
    // 监听所有网络接口的指定端口
    if (server->listen(QHostAddress::Any, port)) {
        qDebug() << "✅ 服务器启动成功";
        qDebug() << "📡 监听地址: 0.0.0.0:" << port;
        qDebug() << "🔢 客户端可连接到此服务器的地址:";
        
        // 显示本机所有IP地址
        QList<QHostAddress> ipList = QNetworkInterface::allAddresses();
        for (const QHostAddress &address : ipList) {
            //筛选IPv4类型的IP且不为回环IP
            if (address.protocol() == QAbstractSocket::IPv4Protocol && 
                address != QHostAddress::LocalHost) {
                qDebug() << "   -" << address.toString() << ":" << port;
            }
        }
        return true;
    } else {
        qDebug() << "❌ 服务器启动失败:" << server->errorString();
        return false;
    }
}

void TcpServer::onNewConnection()
{
    // 处理所有等待的连接
    while (server->hasPendingConnections()) {
        QTcpSocket *client = server->nextPendingConnection();
        
        // 获取客户端信息
        QString clientInfo = QString("%1:%2")
                                .arg(client->peerAddress().toString())
                                .arg(client->peerPort());
        
        qDebug() << "👤 新客户端连接:" << clientInfo;
        
        // 添加到客户端列表
        clients.append(client);
        
        // 连接客户端的信号
        connect(client, &QTcpSocket::readyRead,
                this, &TcpServer::onClientReadyRead); //读取信号
        connect(client, &QTcpSocket::disconnected,
                this, &TcpServer::onClientDisconnected); //断开连接信号
        
        // 发送欢迎消息
        QString welcome = QString("欢迎连接到服务器!你的ID: %1\n")
                             .arg(client->socketDescriptor());
        client->write(welcome.toUtf8()); //转为utf-8编码,兼容性强
    }
}

3.2、客户端构建

// client.h
#include <QTcpSocket>
#include <QObject>

class TcpClient : public QObject
{
    Q_OBJECT
public:
    explicit TcpClient(QObject *parent = nullptr);
    
    void connectToServer(const QString &host, quint16 port);
    void disconnectFromServer();
    void sendMessage(const QString &message);
    
    bool isConnected() const;

signals:
    void connected();
    void disconnected();
    void messageReceived(const QString &message);
    void errorOccurred(const QString &error);

private slots:
    void onConnected();
    void onDisconnected();
    void onReadyRead();
    void onError(QAbstractSocket::SocketError error);

private:
    QTcpSocket *socket;
};


// client.cpp
#include "client.h"
#include <QDebug>

TcpClient::TcpClient(QObject *parent) : QObject(parent)
{
    socket = new QTcpSocket(this);
    
    // 连接信号
    connect(socket, &QTcpSocket::connected,
            this, &TcpClient::onConnected);
    connect(socket, &QTcpSocket::disconnected,
            this, &TcpClient::onDisconnected);
    connect(socket, &QTcpSocket::readyRead,
            this, &TcpClient::onReadyRead);
    connect(socket, &QTcpSocket::errorOccurred,
            this, &TcpClient::onError);
}

void TcpClient::connectToServer(const QString &host, quint16 port)
{
    qDebug() << "🔄 正在连接到服务器" << host << ":" << port;
    socket->connectToHost(host, port);
}

void TcpClient::onConnected()
{
    qDebug() << "✅ 连接成功!";
    qDebug() << "📍 本地地址:" << socket->localAddress().toString() 
             << ":" << socket->localPort();
    qDebug() << "🌍 远程地址:" << socket->peerAddress().toString() 
             << ":" << socket->peerPort();
    
    emit connected();
}

void TcpClient::sendMessage(const QString &message)
{
    if (socket->state() == QTcpSocket::ConnectedState) {
        QByteArray data = (message + "\n").toUtf8();
        socket->write(data);
    } else {
        qDebug() << "⚠️ 未连接,无法发送消息";
    }
}

 

一句话总结:

  • 服务器在指定IP和端口监听,客户端连接正确的IP和端口,网络通畅且无防火墙阻挡,两者即可建立TCP连接。

 四、QTcpServer信号介绍

4.1、newConnection() - 新连接信号

  触发时机:当有新的客户端连接请求到达时

  信号特点:

    • 每次有连接到达时触发

    • 可能同时有多个连接到达,需要用 while 循环处理

    • 必须配合 hasPendingConnections()和 nextPendingConnection()使用

    • // 基本用法
      connect(server, &QTcpServer::newConnection, 
              this, &MyServer::handleNewConnection);
      
      void MyServer::handleNewConnection() {
          // 用while循环处理所有连接
          while (server->hasPendingConnections()) { //判断是否还有等待的连接
              QTcpSocket *client = server->nextPendingConnection();
              if (client) {
                  processClient(client);
              }
          }
      }

 

4.2、acceptError() - 接受错误信号

  触发时机:接受新连接时发生错误


五、QTcpSocket信号介绍

5.1、connected() - 连接建立信号

  触发时机:客户端成功连接到服务器,或服务器套接字完成连接

  重要特性:

    • 连接完全建立后才触发

    • 适用于初始握手完成后的操作

    • 对于服务器端的客户端套接字,这个信号也会触发

    • // 客户端使用示例
      connect(socket, &QTcpSocket::connected,
              this, &MyClient::onConnected); //客户端连接成功后触发
      
      void MyClient::onConnected() {
          qDebug() << "✅ 连接成功!";
          qDebug() << "本地端点:" << socket->localAddress().toString() 
                   << ":" << socket->localPort();
          qDebug() << "远程端点:" << socket->peerAddress().toString() 
                   << ":" << socket->peerPort();
          
          // 连接成功后可以开始发送数据
          socket->write("Hello Server!");
         
      }

       

5.2、disconnected() - 连接断开信号

   触发时机:连接被正常关闭或异常断开

5.3、readyRead() - 数据可读信号

   触发时机:有新的数据到达,可以读取

    • void MainWindow::socketreaddata()
      {
          while(!tcpsocket->atEnd()){
              ui->textEdit->append("[in]:" + tcpsocket->readLine() + '\n');
          }
      
      }

       

posted @ 2026-02-12 23:39  菜鸡の编程日常  阅读(31)  评论(0)    收藏  举报