【QT/C++】Qt网络编程进阶:TCP网络编程的基本原理和实际应用(超详细)

【QT/C++】Qt网络编程进阶:TCP网络编程的基本原理和实际应用(超详细)

  • 本文主要详细说明Qt中的TCP网络编程的基本原理和实际应用。

(关注不迷路哈!!!)

文章目录

  • 【QT/C++】Qt网络编程进阶:TCP网络编程的基本原理和实际应用(超详细)
    • [1. 网络基础概念回顾](#1. 网络基础概念回顾)
      • [1.1 网络类型](#1.1 网络类型)
      • [1.2 网络分层模型(4层)](#1.2 网络分层模型(4层))
      • [1.3 TCP与UDP协议区别](#1.3 TCP与UDP协议区别)
    • [2. Qt TCP网络编程](#2. Qt TCP网络编程)
      • [2.1 QTcpSocket客户端类](#2.1 QTcpSocket客户端类)
      • [2.2 QTcpServer服务器类](#2.2 QTcpServer服务器类)
    • [3. 综合示例:TCP聊天程序](#3. 综合示例:TCP聊天程序)
      • [3.1 服务器端实现](#3.1 服务器端实现)
      • [3.2 客户端实现](#3.2 客户端实现)
      • [3.3 主函数](#3.3 主函数)
      • [3.4 项目文件](#3.4 项目文件)
    • 总结

1. 网络基础概念回顾

1.1 网络类型

  • 子网网络:局域网,只能进行内网通信
  • 公网网络:互联网,支持远程通信

1.2 网络分层模型(4层)

  1. 应用层:用户自定义的网络数据传输协议(ssh, tftp, ftp, http等)
  2. 传输层:TCP/UDP传输协议
  3. 网络层:IPv4/IPv6, 3G, 4G, 5G等网络协议
  4. 物理层:光纤、路由器、基站、网线、网卡等

1.3 TCP与UDP协议区别

  • TCP :可靠传输协议,需要三步握手建立连接,通信时进行数据校验
    • 应用场景:文件传输、控制命令传输
  • UDP :不可靠传输协议,不需要建立连接,不进行数据校验
    • 应用场景:多媒体数据传输(屏幕广播软件、视频网站、直播软件等)

2. Qt TCP网络编程

TCP协议的经典编程模型

2.1 QTcpSocket客户端类

QTcpSocket Class 客户端类

Header QTcpSocket 头文件
qmake QT += network 模块
Inherits QAbstractSocket 父类
Inherited By QSctpSocket and QSslSocket 派生类
cpp 复制代码
#include <QTcpSocket>
#include <QHostAddress>
// 在.pro文件中添加: QT += network

// 构造函数
QTcpSocket *socket = new QTcpSocket(this);

// 连接服务器(使用QHostAddress)  IP+端口
socket->connectToHost(QHostAddress("192.168.1.100"), 8080);

// 连接服务器(直接使用IP地址字符串)
socket->connectToHost("192.168.1.100", 8080);

// 数据发送
socket->write("Hello Server");           // 发送字符数据
socket->write(data, dataSize);           // 发送指定大小的数据
socket->write(QByteArray("Hello"));      // 发送QByteArray数据

// 数据接收
qint64 bytesRead = socket->read(buffer, maxSize);  // 读取数据到缓冲区
QByteArray data = socket->read(1024);              // 读取指定大小数据
QByteArray allData = socket->readAll();            // 读取所有可用数据

// 信号连接
connect(socket, &QTcpSocket::readyRead, this, &MyClass::onReadyRead);              // 数据可读信号
connect(socket, &QTcpSocket::connected, this, &MyClass::onConnected);              // 连接成功信号
connect(socket, &QTcpSocket::disconnected, this, &MyClass::onDisconnected);        // 断开连接信号
connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &MyClass::onError); // 错误信号

2.2 QTcpServer服务器类

QTcpServer Class 服务器类

Header QTcpServer 头文件
qmake QT += network
Inherits QObject
Inherited By QSctpServer
cpp 复制代码
#include <QTcpServer>
#include <QTcpSocket>
// 在.pro文件中添加: QT += network

// 构造函数
QTcpServer *server = new QTcpServer(this);

// 绑定并监听端口
bool success = server->listen(QHostAddress::Any, 8080);
// QHostAddress::Any 相当于 INADDR_ANY,监听本地所有地址
// 端口范围:0-65535,建议使用1000以后的端口

// 信号连接
connect(server, &QTcpServer::newConnection, this, &MyClass::onNewConnection);          // 新连接信号

// 处理新连接
void MyClass::onNewConnection()
{
    QTcpSocket *clientSocket = server->nextPendingConnection();
    // 连接客户端socket的信号
    connect(clientSocket, &QTcpSocket::readyRead, this, &MyClass::onClientReadyRead);
    connect(clientSocket, &QTcpSocket::disconnected, this, &MyClass::onClientDisconnected);
}

// 客户端数据接收
void MyClass::onClientReadyRead()
{
    QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
    if (clientSocket) {
        QByteArray data = clientSocket->readAll();
        // 处理接收到的数据
    }
}

// 数据发送给客户端
clientSocket->write("Hello Client");
clientSocket->write(data, dataSize);
clientSocket->write(QByteArray("Response"));

3. 综合示例:TCP聊天程序

以下是一个完整的TCP聊天程序示例,包含服务器端和客户端:

3.1 服务器端实现

服务器头文件(tcpserver.h)

cpp 复制代码
#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include <QTextEdit>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>

class TcpServer : public QMainWindow
{
    Q_OBJECT

public:
    TcpServer(QWidget *parent = nullptr);
    ~TcpServer();

private slots:
    void startServer();
    void stopServer();
    void onNewConnection();
    void onClientReadyRead();
    void onClientDisconnected();
    void onSendMessage();

private:
    void setupUI();
    void broadcastMessage(const QString &message);
    
    QTcpServer *server;
    QList<QTcpSocket*> clients;
    
    QTextEdit *logTextEdit;
    QTextEdit *messageTextEdit;
    QLineEdit *portLineEdit;
    QPushButton *startButton;
    QPushButton *stopButton;
    QPushButton *sendButton;
    QLabel *statusLabel;
};

#endif // TCPSERVER_H

服务器实现(tcpserver.cpp)

cpp 复制代码
#include "tcpserver.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWidget>
#include <QDateTime>
#include <QMessageBox>

TcpServer::TcpServer(QWidget *parent)
    : QMainWindow(parent)
    , server(new QTcpServer(this))
{
    setupUI();
    
    connect(server, &QTcpServer::newConnection, this, &TcpServer::onNewConnection);
    connect(startButton, &QPushButton::clicked, this, &TcpServer::startServer);
    connect(stopButton, &QPushButton::clicked, this, &TcpServer::stopServer);
    connect(sendButton, &QPushButton::clicked, this, &TcpServer::onSendMessage);
}

TcpServer::~TcpServer()
{
}

void TcpServer::setupUI()
{
    logTextEdit = new QTextEdit;
    logTextEdit->setReadOnly(true);
    
    messageTextEdit = new QTextEdit;
    messageTextEdit->setMaximumHeight(60);
    
    portLineEdit = new QLineEdit("8080");
    startButton = new QPushButton("启动服务器");
    stopButton = new QPushButton("停止服务器");
    sendButton = new QPushButton("发送");
    statusLabel = new QLabel("服务器未运行");
    
    stopButton->setEnabled(false);
    
    auto *topLayout = new QHBoxLayout;
    topLayout->addWidget(new QLabel("端口:"));
    topLayout->addWidget(portLineEdit);
    topLayout->addWidget(startButton);
    topLayout->addWidget(stopButton);
    topLayout->addStretch();
    topLayout->addWidget(statusLabel);
    
    auto *bottomLayout = new QHBoxLayout;
    bottomLayout->addWidget(new QLabel("广播消息:"));
    bottomLayout->addWidget(messageTextEdit);
    bottomLayout->addWidget(sendButton);
    
    auto *mainLayout = new QVBoxLayout;
    mainLayout->addLayout(topLayout);
    mainLayout->addWidget(new QLabel("日志:"));
    mainLayout->addWidget(logTextEdit);
    mainLayout->addLayout(bottomLayout);
    
    auto *centralWidget = new QWidget;
    centralWidget->setLayout(mainLayout);
    setCentralWidget(centralWidget);
    
    setWindowTitle("TCP聊天服务器");
    resize(500, 400);
}

void TcpServer::startServer()
{
    quint16 port = portLineEdit->text().toUShort();
    if (server->listen(QHostAddress::Any, port)) {
        logTextEdit->append(QString("[%1] 服务器启动成功,监听端口 %2")
                           .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
                           .arg(port));
        startButton->setEnabled(false);
        stopButton->setEnabled(true);
        statusLabel->setText(QString("服务器运行中 (端口: %1)").arg(port));
    } else {
        QMessageBox::critical(this, "错误", "无法启动服务器: " + server->errorString());
    }
}

void TcpServer::stopServer()
{
    // 断开所有客户端连接
    for (QTcpSocket *client : clients) {
        client->disconnectFromHost();
    }
    clients.clear();
    
    server->close();
    logTextEdit->append(QString("[%1] 服务器已停止").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
    startButton->setEnabled(true);
    stopButton->setEnabled(false);
    statusLabel->setText("服务器未运行");
}

void TcpServer::onNewConnection()
{
    QTcpSocket *clientSocket = server->nextPendingConnection();
    clients.append(clientSocket);
    
    connect(clientSocket, &QTcpSocket::readyRead, this, &TcpServer::onClientReadyRead);
    
    connect(clientSocket, &QTcpSocket::disconnected, this, &TcpServer::onClientDisconnected);
    
    QString clientInfo = QString("%1:%2")
                        .arg(clientSocket->peerAddress().toString())
                        .arg(clientSocket->peerPort());
    
    logTextEdit->append(QString("[%1] 新客户端连接: %2 (当前客户端数: %3)")
                       .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
                       .arg(clientInfo)
                       .arg(clients.size()));
    
    // 发送欢迎消息
    clientSocket->write(QString("欢迎连接到服务器! 当前时间: %1\n")
                       .arg(QDateTime::currentDateTime().toString()).toUtf8());
}

void TcpServer::onClientReadyRead()
{
    QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
    if (!clientSocket)
        return;
    
    QByteArray data = clientSocket->readAll();
    QString message = QString::fromUtf8(data);
    
    QString clientInfo = QString("%1:%2")
                        .arg(clientSocket->peerAddress().toString())
                        .arg(clientSocket->peerPort());
    
    QString logMessage = QString("[%1] %2: %3")
                        .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
                        .arg(clientInfo)
                        .arg(message);
    
    logTextEdit->append(logMessage);
    
    // 转发消息给其他客户端
    QString broadcastMsg = QString("%1: %2")
                          .arg(clientInfo)
                          .arg(message);
    broadcastMessage(broadcastMsg);
}

void TcpServer::onClientDisconnected()
{
    QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
    if (!clientSocket)
        return;
    
    clients.removeOne(clientSocket);
    clientSocket->deleteLater();
    
    logTextEdit->append(QString("[%1] 客户端断开连接 (剩余客户端数: %2)")
                       .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
                       .arg(clients.size()));
}

void TcpServer::onSendMessage()
{
    QString message = messageTextEdit->toPlainText().trimmed();
    if (!message.isEmpty()) {
        QString serverMsg = QString("服务器广播: %1").arg(message);
        broadcastMessage(serverMsg);
        messageTextEdit->clear();
    }
}

void TcpServer::broadcastMessage(const QString &message)
{
    QByteArray data = message.toUtf8() + "\n";
    
    for (QTcpSocket *client : clients) {
        if (client->state() == QAbstractSocket::ConnectedState) {
            client->write(data);
        }
    }
    
    logTextEdit->append(QString("[%1] %2")
                       .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
                       .arg(message));
}

3.2 客户端实现

客户端头文件(tcpclient.h)

cpp 复制代码
#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QTextEdit>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>

class TcpClient : public QMainWindow
{
    Q_OBJECT

public:
    TcpClient(QWidget *parent = nullptr);
    ~TcpClient();

private slots:
    void connectToServer();
    void disconnectFromServer();
    void onConnected();
    void onDisconnected();
    void onReadyRead();
    void onError(QAbstractSocket::SocketError socketError);
    void onSendMessage();

private:
    void setupUI();
    void updateConnectionState();
    
    QTcpSocket *socket;
    
    QTextEdit *chatTextEdit;
    QTextEdit *messageTextEdit;
    QLineEdit *serverIPEdit;
    QLineEdit *serverPortEdit;
    QPushButton *connectButton;
    QPushButton *disconnectButton;
    QPushButton *sendButton;
    QLabel *statusLabel;
};

#endif // TCPCLIENT_H

客户端实现(tcpclient.cpp)

cpp 复制代码
#include "tcpclient.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QWidget>
#include <QDateTime>
#include <QMessageBox>

TcpClient::TcpClient(QWidget *parent)
    : QMainWindow(parent)
    , socket(new QTcpSocket(this))
{
    setupUI();
    
    connect(connectButton, &QPushButton::clicked, this, &TcpClient::connectToServer);
    connect(disconnectButton, &QPushButton::clicked, this, &TcpClient::disconnectFromServer);
    connect(sendButton, &QPushButton::clicked, this, &TcpClient::onSendMessage);
    connect(socket, &QTcpSocket::connected, this, &TcpClient::onConnected);
    connect(socket, &QTcpSocket::disconnected, this, &TcpClient::onDisconnected);
    connect(socket, &QTcpSocket::readyRead, this, &TcpClient::onReadyRead);
    connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &TcpClient::onError);
    
    updateConnectionState();
}

TcpClient::~TcpClient()
{
}

void TcpClient::setupUI()
{
    chatTextEdit = new QTextEdit;
    chatTextEdit->setReadOnly(true);
    
    messageTextEdit = new QTextEdit;
    messageTextEdit->setMaximumHeight(60);
    
    serverIPEdit = new QLineEdit("127.0.0.1");
    serverPortEdit = new QLineEdit("8080");
    connectButton = new QPushButton("连接");
    disconnectButton = new QPushButton("断开");
    sendButton = new QPushButton("发送");
    statusLabel = new QLabel("未连接");
    
    auto *topLayout = new QHBoxLayout;
    topLayout->addWidget(new QLabel("服务器IP:"));
    topLayout->addWidget(serverIPEdit);
    topLayout->addWidget(new QLabel("端口:"));
    topLayout->addWidget(serverPortEdit);
    topLayout->addWidget(connectButton);
    topLayout->addWidget(disconnectButton);
    topLayout->addStretch();
    topLayout->addWidget(statusLabel);
    
    auto *bottomLayout = new QHBoxLayout;
    bottomLayout->addWidget(new QLabel("消息:"));
    bottomLayout->addWidget(messageTextEdit);
    bottomLayout->addWidget(sendButton);
    
    auto *mainLayout = new QVBoxLayout;
    mainLayout->addLayout(topLayout);
    mainLayout->addWidget(new QLabel("聊天记录:"));
    mainLayout->addWidget(chatTextEdit);
    mainLayout->addLayout(bottomLayout);
    
    auto *centralWidget = new QWidget;
    centralWidget->setLayout(mainLayout);
    setCentralWidget(centralWidget);
    
    setWindowTitle("TCP聊天客户端");
    resize(500, 400);
}

void TcpClient::connectToServer()
{
    QString host = serverIPEdit->text();
    quint16 port = serverPortEdit->text().toUShort();
    
    socket->connectToHost(host, port);
    
    chatTextEdit->append(QString("[%1] 正在连接到 %2:%3...")
                        .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
                        .arg(host)
                        .arg(port));
}

void TcpClient::disconnectFromServer()
{
    socket->disconnectFromHost();
}

void TcpClient::onConnected()
{
    chatTextEdit->append(QString("[%1] 成功连接到服务器").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
    updateConnectionState();
}

void TcpClient::onDisconnected()
{
    chatTextEdit->append(QString("[%1] 与服务器断开连接").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
    updateConnectionState();
}

void TcpClient::onReadyRead()
{
    QByteArray data = socket->readAll();
    QString message = QString::fromUtf8(data);
    
    chatTextEdit->append(QString("[%1] %2")
                        .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
                        .arg(message.trimmed()));
}

void TcpClient::onError(QAbstractSocket::SocketError socketError)
{
    QString errorString = socket->errorString();
    chatTextEdit->append(QString("[%1] 错误: %2")
                        .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))
                        .arg(errorString));
    
    QMessageBox::critical(this, "连接错误", errorString);
    updateConnectionState();
}

void TcpClient::onSendMessage()
{
    QString message = messageTextEdit->toPlainText().trimmed();
    if (!message.isEmpty() && socket->state() == QAbstractSocket::ConnectedState) {
        socket->write(message.toUtf8() + "\n");
        messageTextEdit->clear();
    }
}

void TcpClient::updateConnectionState()
{
    bool connected = (socket->state() == QAbstractSocket::ConnectedState);
    
    connectButton->setEnabled(!connected);
    disconnectButton->setEnabled(connected);
    sendButton->setEnabled(connected);
    
    if (connected) {
        statusLabel->setText(QString("已连接到 %1:%2")
                            .arg(socket->peerAddress().toString())
                            .arg(socket->peerPort()));
    } else {
        statusLabel->setText("未连接");
    }
}

3.3 主函数

服务器主函数(main.cpp for server)

cpp 复制代码
#include "tcpserver.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    TcpServer server;
    server.show();
    
    return app.exec();
}

#include "main.moc"

客户端主函数(main.cpp for client)

cpp 复制代码
#include "tcpclient.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    TcpClient client;
    client.show();
    
    return app.exec();
}

#include "main.moc"

3.4 项目文件

服务器项目文件(tcpserver.pro)

cmake 复制代码
QT += core gui network widgets

CONFIG += c++11

TARGET = TcpServer
TEMPLATE = app

SOURCES += \
    main.cpp \
    tcpserver.cpp

HEADERS += \
    tcpserver.h

客户端项目文件(tcpclient.pro)

cmake 复制代码
QT += core gui network widgets

CONFIG += c++11

TARGET = TcpClient
TEMPLATE = app

SOURCES += \
    main.cpp \
    tcpclient.cpp

HEADERS += \
    tcpclient.h

总结

这个综合示例演示了以下Qt TCP网络编程知识点:

  1. QTcpServer的使用

    • 服务器的创建和监听
    • 处理新连接请求
    • 管理多个客户端连接
  2. QTcpSocket的使用

    • 客户端连接服务器
    • 数据的发送和接收
    • 连接状态管理
  3. 信号与槽机制在网络编程中的应用

    • 连接状态变化信号
    • 数据可读信号
    • 错误处理信号
  4. 实际应用技巧

    • 客户端列表管理
    • 消息广播机制
    • 错误处理和异常情况处理
    • 用户界面与网络通信的结合
相关推荐
❀͜͡傀儡师2 小时前
快速定位并解决Java应用CPU占用过高问题
java·开发语言·python
艾莉丝努力练剑2 小时前
【C++:map和set的使用】C++ map/multimap完全指南:从红黑树原理入门到高频算法实战
大数据·开发语言·c++·人工智能·stl·map
汤姆yu2 小时前
基于大数据的全国降水可视化分析预测系统
大数据·开发语言·python
浮游本尊2 小时前
React 18.x 学习计划 - 第六天:React路由和导航
前端·学习·react.js
VBA63373 小时前
VBA信息获取与处理专题五第三节:发送带附件的电子邮件
开发语言
yuxb733 小时前
Zabbix企业级分布式监控系统(上)
笔记·学习·zabbix
元亓亓亓3 小时前
Leet热题100--208. 实现 Trie (前缀树)--中等
java·开发语言
拾荒的小海螺3 小时前
C#:OpenCvSharp 实现图像处理的技术指南
开发语言·图像处理·c#
自由随风飘8 小时前
python 题目练习1~5
开发语言·python