【Qt】 TCP套接字通信学习文档

【Qt】TCP套接字通信学习文档

📚 一、概述

1.1 标准C++与Qt在网络编程上的区别

特性 标准C++ Qt框架
套接字支持 无内置类,需使用操作系统C API 提供QTcpServerQTcpSocket等类
开发复杂度 较高,需要处理平台差异 较低,跨平台API统一
编程范式 面向过程/手动封装 面向对象,信号槽机制
内存管理 手动管理 父子对象机制自动管理
异步处理 需要多线程/select/poll等 信号槽机制简化异步处理

1.2 Qt网络编程优势 ✨

  • 跨平台性:一次编写,多平台运行(Windows、Linux、macOS等)
  • 信号槽机制:事件驱动,避免阻塞式编程
  • 简化开发:封装底层细节,提供高级API
  • 集成度高:与Qt GUI、数据库等模块无缝集成
  • 内存安全:父子对象机制减少内存泄漏风险

🔧 二、核心类详解

2.1 QTcpServer - 服务器类

📋 主要功能
  • 监听客户端连接请求
  • 建立与客户端的连接
  • 管理客户端连接队列
🛠️ 常用API函数

构造函数

cpp 复制代码
QTcpServer::QTcpServer(QObject *parent = nullptr);

监听绑定

cpp 复制代码
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, 
                        quint16 port = 0);

参数说明:

  • address:监听地址
    • QHostAddress::Any:IPv4任意地址(0.0.0.0)
    • QHostAddress::AnyIPv6:IPv6任意地址
    • QHostAddress::LocalHost:本地环回(127.0.0.1)
  • port:端口号,0表示自动分配可用端口

状态查询

cpp 复制代码
bool QTcpServer::isListening() const;              // 是否正在监听
QHostAddress QTcpServer::serverAddress() const;    // 服务器绑定地址
quint16 QTcpServer::serverPort() const;            // 服务器绑定端口

连接管理

cpp 复制代码
QTcpSocket *QTcpServer::nextPendingConnection();   // 获取待处理的连接
bool QTcpServer::waitForNewConnection(int msec = 0, bool *timedOut = nullptr);
🔔 重要信号
cpp 复制代码
void newConnection();  // 有新客户端连接时触发
void acceptError(QAbstractSocket::SocketError socketError);  // 接受连接出错时触发

2.2 QTcpSocket - 通信套接字类

📊 继承关系
复制代码
QObject → QIODevice → QAbstractSocket → QTcpSocket
🛠️ 常用API函数

构造函数

cpp 复制代码
QTcpSocket::QTcpSocket(QObject *parent = nullptr);

连接服务器

cpp 复制代码
void connectToHost(const QString &hostName, quint16 port, 
                   QIODevice::OpenMode openMode = QIODevice::ReadWrite);
void connectToHost(const QHostAddress &address, quint16 port,
                   QIODevice::OpenMode openMode = QIODevice::ReadWrite);

数据读写操作

cpp 复制代码
// 读取数据
qint64 read(char *data, qint64 maxSize);     // 读取到指定缓冲区
QByteArray read(qint64 maxSize);             // 读取指定字节数
QByteArray readAll();                        // 读取所有可用数据
QByteArray readLine(qint64 maxSize = 0);     // 读取一行数据

// 写入数据
qint64 write(const char *data, qint64 maxSize);
qint64 write(const char *data);
qint64 write(const QByteArray &byteArray);

// 数据缓冲区操作
qint64 bytesAvailable() const;               // 可读字节数
qint64 bytesToWrite() const;                 // 等待写入的字节数
bool waitForBytesWritten(int msecs = 30000); // 等待数据写入完成
bool waitForReadyRead(int msecs = 30000);    // 等待可读数据

连接状态

cpp 复制代码
bool isOpen() const;                         // 套接字是否打开
bool isReadable() const;                     // 是否可读
bool isWritable() const;                     // 是否可写
void close();                                // 关闭套接字
🔔 重要信号
cpp 复制代码
void readyRead();                            // 有数据可读时触发
void connected();                            // 连接建立成功时触发
void disconnected();                         // 连接断开时触发
void bytesWritten(qint64 bytes);             // 数据写入完成时触发
void errorOccurred(QAbstractSocket::SocketError socketError);  // 错误发生时触发
void stateChanged(QAbstractSocket::SocketState socketState);   // 状态改变时触发

🔄 三、TCP通信流程

3.1 服务器端完整流程

📝 通信流程步骤
  1. 创建QTcpServer对象 - 实例化服务器对象
  2. 启动监听 - 调用listen()方法绑定地址和端口
  3. 处理新连接 - 连接newConnection信号,在槽函数中处理
  4. 获取通信套接字 - 使用nextPendingConnection()获取客户端套接字
  5. 数据通信 - 通过客户端套接字进行读写操作
  6. 连接管理 - 处理连接断开和异常情况
📊 流程图示

创建QTcpServer listen绑定地址端口 连接newConnection信号 客户端连接触发信号 nextPendingConnection获取QTcpSocket 连接readyRead信号读取数据 使用write发送数据 处理disconnected信号清理资源

3.2 客户端完整流程

📝 通信流程步骤
  1. 创建QTcpSocket对象 - 实例化客户端套接字
  2. 连接服务器 - 调用connectToHost()连接服务器
  3. 处理连接状态 - 连接connecteddisconnected信号
  4. 数据通信 - 连接readyRead信号读取数据,使用write()发送数据
  5. 断开连接 - 调用close()或处理服务器断开
📊 流程图示

创建QTcpSocket connectToHost连接服务器 connected信号确认连接 连接readyRead信号读取数据 使用write发送数据 处理disconnected信号 close断开连接


💻 四、完整代码示例

4.1 服务器端实现

🎨 界面设计要点
  • 端口输入框:设置监听端口
  • 启动按钮:开始监听
  • 通信记录框:显示通信历史
  • 消息输入框:输入要发送的消息
  • 发送按钮:发送消息到客户端
  • 状态标签:显示服务器状态
📝 核心代码实现
cpp 复制代码
// 创建服务器对象
m_server = new QTcpServer(this);

// 监听新连接信号
connect(m_server, &QTcpServer::newConnection, this, [=]() {
    // 获取客户端连接
    m_tcp = m_server->nextPendingConnection();
    record->append("成功和客户端建立了新的连接...");
    m_status->setText("连接状态:√");
    m_status->setStyleSheet("background-color:green");
    
    // 监听客户端数据
    connect(m_tcp, &QTcpSocket::readyRead, this, [=]() {
        QString recvMsg = m_tcp->readAll();
        record->append("客户端Say: " + recvMsg);
    });
    
    // 处理客户端断开
    connect(m_tcp, &QTcpSocket::disconnected, this, [=]() {
        record->append("客户端已经断开了连接...");
        m_tcp->deleteLater();  // 安全删除对象
        m_status->setText("连接状态:×");
        m_status->setStyleSheet("background-color:red");
    });
});

// 启动监听
void MainWindow::on_startServer_clicked() {
    unsigned short port = m_port->text().toInt();
    m_server->listen(QHostAddress::Any, port);
    record->append("=========== 服务器已启动 ===========");
    startServer->setEnabled(false);  // 禁用启动按钮
}

// 发送消息
void MainWindow::on_sendMsg_clicked() {
    QString msg = msgInput->toPlainText();
    if (m_tcp && m_tcp->state() == QAbstractSocket::ConnectedState) {
        m_tcp->write(msg.toUtf8());
        record->append("服务器Say: " + msg);
        msgInput->clear();
    }
}

4.2 客户端实现

🎨 界面设计要点
  • 服务器地址输入框:输入服务器IP
  • 端口输入框:输入服务器端口
  • 连接/断开按钮:控制连接状态
  • 通信记录框:显示通信历史
  • 消息输入框:输入要发送的消息
  • 发送按钮:发送消息到服务器
  • 状态标签:显示连接状态
📝 核心代码实现
cpp 复制代码
// 创建客户端套接字
m_tcp = new QTcpSocket(this);

// 监听数据接收
connect(m_tcp, &QTcpSocket::readyRead, [=]() {
    QByteArray recvMsg = m_tcp->readAll();
    record->append("服务器Say: " + recvMsg);
});

// 处理连接成功
connect(m_tcp, &QTcpSocket::connected, this, [=]() {
    record->append("=========== 连接服务器成功 ===========");
    m_status->setText("连接状态:√");
    m_status->setStyleSheet("background-color:green");
    connectServer->setEnabled(false);
    disconnectBtn->setEnabled(true);
});

// 处理连接断开
connect(m_tcp, &QTcpSocket::disconnected, this, [=]() {
    record->append("服务器已经断开了连接...");
    m_status->setText("连接状态:×");
    m_status->setStyleSheet("background-color:red");
    connectServer->setEnabled(true);
    disconnectBtn->setEnabled(false);
});

// 连接服务器
void MainWindow::on_connectServer_clicked() {
    QString ip = m_ip->text();
    unsigned short port = m_port->text().toInt();
    m_tcp->connectToHost(QHostAddress(ip), port);
}

// 发送消息
void MainWindow::on_sendMsg_clicked() {
    QString msg = msgInput->toPlainText();
    if (m_tcp->state() == QAbstractSocket::ConnectedState) {
        m_tcp->write(msg.toUtf8());
        record->append("客户端Say: " + msg);
        msgInput->clear();
    }
}

// 断开连接
void MainWindow::on_disconnect_clicked() {
    m_tcp->close();
}

🔑 五、关键技术与注意事项

5.1 数据编码与处理

  • 文本数据 :使用QStringtoUtf8()进行编码转换
  • 二进制数据 :使用QByteArray直接处理
  • 数据分帧 :TCP是流式协议,需要应用层处理消息边界
    • 固定长度法:每条消息固定长度
    • 分隔符法:使用特定分隔符区分消息
    • 长度前缀法:在消息前添加长度信息

5.2 内存管理与资源释放

cpp 复制代码
// 正确释放方式
clientSocket->deleteLater();  // 安全删除,由事件循环处理

// 避免野指针
connect(clientSocket, &QTcpSocket::disconnected, 
        clientSocket, &QTcpSocket::deleteLater);

// 服务器关闭时清理所有连接
void Server::closeAllConnections() {
    for (QTcpSocket *socket : connections) {
        socket->disconnectFromHost();
        socket->deleteLater();
    }
    connections.clear();
}

5.3 错误处理机制

cpp 复制代码
// 连接错误处理
connect(m_tcp, &QTcpSocket::errorOccurred, [](QAbstractSocket::SocketError error){
    qDebug() << "套接字错误:" << error;
    switch(error) {
        case QAbstractSocket::ConnectionRefusedError:
            qDebug() << "连接被拒绝";
            break;
        case QAbstractSocket::RemoteHostClosedError:
            qDebug() << "远程主机关闭";
            break;
        // ... 其他错误处理
    }
});

// 操作错误检查
qint64 bytesWritten = socket->write(data);
if (bytesWritten == -1) {
    qDebug() << "发送失败:" << socket->errorString();
    // 重试或处理逻辑
}

5.4 并发连接处理

cpp 复制代码
// 多客户端连接管理
QList<QTcpSocket*> clientSockets;

connect(m_server, &QTcpServer::newConnection, [=]() {
    QTcpSocket *clientSocket = m_server->nextPendingConnection();
    clientSockets.append(clientSocket);
    
    // 为每个客户端单独处理
    connect(clientSocket, &QTcpSocket::readyRead, [=]() {
        // 处理特定客户端的数据
    });
    
    // 客户端断开时清理
    connect(clientSocket, &QTcpSocket::disconnected, [=]() {
        clientSockets.removeOne(clientSocket);
        clientSocket->deleteLater();
    });
});

🚀 六、高级特性与最佳实践

6.1 心跳机制实现

cpp 复制代码
// 心跳定时器
QTimer *heartbeatTimer = new QTimer(this);
connect(heartbeatTimer, &QTimer::timeout, [=]() {
    if (m_tcp->state() == QAbstractSocket::ConnectedState) {
        m_tcp->write("HEARTBEAT");
        // 检查上次心跳回复时间
        if (lastHeartbeat.elapsed() > 30000) { // 30秒无回复
            m_tcp->abort();  // 强制断开
        }
    }
});
heartbeatTimer->start(5000);  // 每5秒发送一次心跳

6.2 数据序列化

cpp 复制代码
// 自定义协议结构
struct Message {
    int type;           // 消息类型
    int length;         // 数据长度
    QByteArray data;    // 实际数据
    
    QByteArray serialize() const {
        QByteArray buffer;
        QDataStream stream(&buffer, QIODevice::WriteOnly);
        stream << type << length << data;
        return buffer;
    }
    
    static Message deserialize(const QByteArray &buffer) {
        Message msg;
        QDataStream stream(buffer);
        stream >> msg.type >> msg.length >> msg.data;
        return msg;
    }
};

6.3 性能优化建议

  1. 缓冲区管理:合理设置缓冲区大小
  2. 批量发送:合并小数据包,减少系统调用
  3. 异步处理:使用信号槽避免阻塞主线程
  4. 连接池:频繁连接时使用连接池复用
  5. 数据压缩:传输大量数据时考虑压缩

6.4 安全性考虑

  • 数据验证:验证接收数据的合法性
  • 访问控制:实现身份验证机制
  • 加密传输:敏感数据使用SSL/TLS加密
  • 限流控制:防止客户端过度消耗资源
  • 日志审计:记录重要操作和异常

🔍 七、调试与测试

7.1 常见问题排查

  1. 连接失败

    • 检查防火墙设置
    • 验证IP地址和端口
    • 确认服务器是否正在监听
  2. 数据丢失

    • 检查缓冲区大小
    • 验证数据分帧逻辑
    • 确认网络稳定性
  3. 内存泄漏

    • 使用Qt Creator内存分析工具
    • 检查deleteLater()调用
    • 验证父子对象关系

7.2 调试技巧

cpp 复制代码
// 启用详细日志
qSetMessagePattern("[%{time yyyy-MM-dd hh:mm:ss.zzz}] "
                   "[%{if-debug}DEBUG%{endif}%{if-info}INFO%{endif}"
                   "%{if-warning}WARN%{endif}%{if-critical}ERROR%{endif}"
                   "%{if-fatal}FATAL%{endif}] %{file}:%{line} - %{message}");

// 网络状态监控
qDebug() << "Socket state:" << m_tcp->state();
qDebug() << "Bytes available:" << m_tcp->bytesAvailable();
qDebug() << "Peer address:" << m_tcp->peerAddress().toString();
qDebug() << "Peer port:" << m_tcp->peerPort();

💬 八、聊天室项目完整代码示例

注意:此代码只实现了单个客户端连接

8.1 服务器端实现

服务器端的窗口界面如下图所示:

头文件:mainwindow.h
cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QTextEdit>
#include <QLabel>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_startServer_clicked();
    void on_sendMsg_clicked();

private:
    Ui::MainWindow *ui;
    QTcpServer* m_server;
    QTcpSocket* m_tcp;

    QLabel *plabel, *m_status;
    QLineEdit *m_port;
    QTextEdit *msg, *record;
    QPushButton *startServer, *sendMsg;
};

#endif // MAINWINDOW_H
源文件:mainwindow.cpp
cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QWidget *centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);
    centralWidget->setMinimumSize(500, 650);
    
    // 整体垂直布局
    QVBoxLayout *vlayout = new QVBoxLayout(centralWidget);

    // 第一行:端口选择和服务器启动按钮
    QHBoxLayout *playout = new QHBoxLayout(centralWidget);
    vlayout->addLayout(playout);
    
    plabel = new QLabel("端口:", this);
    m_port = new QLineEdit(centralWidget);
    m_port->setText("8888");  // 设置默认端口
    m_port->setFixedWidth(100);
    
    playout->addWidget(plabel);
    playout->addWidget(m_port);
    playout->addStretch();  // 加弹簧
    
    startServer = new QPushButton("启动服务器", this);
    startServer->setFixedWidth(100);
    playout->addWidget(startServer);

    // 通信记录对话框
    QVBoxLayout *recordLayout = new QVBoxLayout;
    vlayout->addLayout(recordLayout);
    
    QLabel *recordLabel = new QLabel("通信记录:", centralWidget);
    recordLabel->setMaximumWidth(150);
    record = new QTextEdit(centralWidget);
    record->resize(200, 100);
    
    recordLayout->addWidget(recordLabel);
    recordLayout->addWidget(record);

    // 发送数据对话框
    QVBoxLayout *msgLayout = new QVBoxLayout;
    vlayout->addLayout(msgLayout);
    
    QLabel *msgLabel = new QLabel("发送数据:", centralWidget);
    msgLabel->setMaximumWidth(150);
    msg = new QTextEdit(centralWidget);
    msg->resize(200, 100);
    
    msgLayout->addWidget(msgLabel);
    msgLayout->addWidget(msg);
    
    sendMsg = new QPushButton("广播消息", centralWidget);
    msgLayout->addWidget(sendMsg);

    // 服务器连接状态标签
    m_status = new QLabel("服务器状态", centralWidget);
    m_status->setStyleSheet("background-color:grey");
    m_status->setMaximumWidth(150);
    vlayout->addWidget(m_status);

    // ============================ 服务器代码编写 ============================
    setWindowTitle("TCP - 服务器");
    
    // 创建 QTcpServer 对象
    m_server = new QTcpServer(this);

    // 检测是否有新的客户端连接
    connect(m_server, &QTcpServer::newConnection, this, [=]() {
        // 当有新的客户端连接时,会触发newConnection信号
        m_tcp = m_server->nextPendingConnection();
        // 通过nextPendingConnection()获取与客户端连接的QTcpSocket对象
        
        record->append("成功和客户端建立了新的连接...");
        m_status->setText("连接状态:√");
        m_status->setStyleSheet("background-color:green");

        // 检测是否有客户端数据
        connect(m_tcp, &QTcpSocket::readyRead, this, [=]() {
            // 当客户端有数据发送过来,触发readyRead信号
            QString recvMsg = m_tcp->readAll();
            record->append("客户端Say: " + recvMsg);
        });

        // 客户端断开了连接
        connect(m_tcp, &QTcpSocket::disconnected, this, [=]() {
            // 当客户端断开连接时,触发disconnected信号
            record->append("客户端已经断开了连接...");
            m_tcp->deleteLater();
            // 将QTcpSocket对象标记为延迟删除,由Qt事件循环在适当时候安全删除
            
            m_status->setText("连接状态:×");
            m_status->setStyleSheet("background-color:red");
        });
    });
    
    // 连接按钮信号和槽
    connect(startServer, &QPushButton::clicked, this, &MainWindow::on_startServer_clicked);
    connect(sendMsg, &QPushButton::clicked, this, &MainWindow::on_sendMsg_clicked);
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 启动服务器端的服务按钮
void MainWindow::on_startServer_clicked()
{
    record->append("=========== 德玛西亚聊天室服务器已启动 ===========");
    m_status->setText("连接状态:×");
    m_status->setStyleSheet("background-color:red");

    unsigned short port = m_port->text().toInt();
    // 设置服务器监听
    m_server->listen(QHostAddress::Any, port);
    startServer->setEnabled(false);
}

// 点击发送数据按钮
void MainWindow::on_sendMsg_clicked()
{
    QString m = msg->toPlainText();
    m_tcp->write(m.toUtf8());
    record->append("服务器Say: " + m);
    msg->clear();
}

8.2 客户端实现

客户端的窗口界面如下图所示:

头文件:mainwindow.h
cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QTextEdit>
#include <QLabel>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_connectServer_clicked();
    void on_sendMsg_clicked();
    void on_disconnect_clicked();

private:
    Ui::MainWindow *ui;
    QTcpSocket* m_tcp;

    QLabel *plabel, *slabel, *m_status;
    QLineEdit *m_port, *m_ip;
    QTextEdit *msg, *record;
    QPushButton *connectServer, *disconnect, *sendMsg;
};

#endif // MAINWINDOW_H
源文件:mainwindow.cpp
cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("TCP - 客户端");

    QWidget *centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);
    centralWidget->setMinimumSize(500, 650);
    
    // 整体垂直布局
    QVBoxLayout *vlayout = new QVBoxLayout(centralWidget);

    // 第一行:端口选择和服务器连接按钮
    QHBoxLayout *playout = new QHBoxLayout(centralWidget);
    vlayout->addLayout(playout);
    
    plabel = new QLabel("服务器端口:", this);
    m_port = new QLineEdit(centralWidget);
    m_port->setText("8888");  // 设置默认端口
    m_port->setFixedWidth(100);
    
    playout->addWidget(plabel);
    playout->addWidget(m_port);
    playout->addStretch();  // 加弹簧
    
    connectServer = new QPushButton("连接服务器", this);
    connectServer->setFixedWidth(100);
    playout->addWidget(connectServer);

    // 第二行:地址选择和服务器断开连接按钮
    QHBoxLayout *slayout = new QHBoxLayout(centralWidget);
    vlayout->addLayout(slayout);
    
    slabel = new QLabel("服务器地址:", this);
    m_ip = new QLineEdit(centralWidget);
    m_ip->setText("127.0.0.1"); // 设置默认地址
    m_ip->setFixedWidth(100);
    
    slayout->addWidget(slabel);
    slayout->addWidget(m_ip);
    slayout->addStretch();
    
    disconnect = new QPushButton("断开连接", this);
    disconnect->setFixedWidth(100);
    slayout->addWidget(disconnect);

    // 通信记录对话框
    QVBoxLayout *recordLayout = new QVBoxLayout;
    vlayout->addLayout(recordLayout);
    
    QLabel *recordLabel = new QLabel("通信记录:", centralWidget);
    recordLabel->setMaximumWidth(150);
    record = new QTextEdit(centralWidget);
    record->resize(200, 100);
    
    recordLayout->addWidget(recordLabel);
    recordLayout->addWidget(record);

    // 发送数据对话框
    QVBoxLayout *msgLayout = new QVBoxLayout;
    vlayout->addLayout(msgLayout);
    
    QLabel *msgLabel = new QLabel("发送数据:", centralWidget);
    msgLabel->setMaximumWidth(150);
    msg = new QTextEdit(centralWidget);
    msg->resize(200, 100);
    
    msgLayout->addWidget(msgLabel);
    msgLayout->addWidget(msg);
    
    sendMsg = new QPushButton("发送消息", centralWidget);
    vlayout->addWidget(sendMsg);

    // 服务器连接状态标签
    m_status = new QLabel("服务器状态:×", centralWidget);
    m_status->setStyleSheet("background-color:red");
    m_status->setMaximumWidth(150);
    vlayout->addWidget(m_status);

    // ============================ 客户端代码编写 ============================
    // 创建通信的套接字对象
    m_tcp = new QTcpSocket(this);
    
    // 检测服务器是否回复了数据
    connect(m_tcp, &QTcpSocket::readyRead, [=]() {
        // 接收服务器发送的数据
        QByteArray recvMsg = m_tcp->readAll();
        record->append("服务器Say: " + recvMsg);
    });

    // 检测是否和服务器是否连接成功了
    connect(m_tcp, &QTcpSocket::connected, this, [=]() {
        record->append("=========== 欢迎来到德玛西亚聊天室!!! ===========");
        m_status->setText("连接状态:√");
        m_status->setStyleSheet("background-color:green");
        // m_status->setPixmap(QPixmap("1.png").scaled(20, 20));
    });

    // 检测服务器是否和客户端断开了连接
    connect(m_tcp, &QTcpSocket::disconnected, this, [=]() {
        record->append("服务器已经断开了连接, ...");
        m_status->setText("连接状态:×");
        m_status->setStyleSheet("background-color:red");
        connectServer->setEnabled(true);
        disconnect->setEnabled(false);
    });
    
    // 连接按钮信号和槽
    connect(connectServer, &QPushButton::clicked, this, &MainWindow::on_connectServer_clicked);
    connect(sendMsg, &QPushButton::clicked, this, &MainWindow::on_sendMsg_clicked);
    connect(disconnect, &QPushButton::clicked, this, &MainWindow::on_disconnect_clicked);
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 连接服务器按钮按下之后的处理动作
void MainWindow::on_connectServer_clicked()
{
    QString ip = m_ip->text();
    unsigned short port = m_port->text().toInt();
    
    // 连接服务器
    m_tcp->connectToHost(QHostAddress(ip), port);
    connectServer->setEnabled(false);
    disconnect->setEnabled(true);
}

// 发送数据按钮按下之后的处理动作
void MainWindow::on_sendMsg_clicked()
{
    QString sendMsgText = msg->toPlainText();
    m_tcp->write(sendMsgText.toUtf8());
    record->append("客户端Say: " + sendMsgText);
    msg->clear();
}

// 断开连接按钮被按下之后的处理动作
void MainWindow::on_disconnect_clicked()
{
    m_tcp->close();
    connectServer->setEnabled(true);
    disconnect->setEnabled(false);
}

相关推荐
烤麻辣烫2 小时前
黑马大事件学习-15(前端登录页面)
前端·css·vue.js·学习·html
ULTRA??2 小时前
QT向量类实现GJK碰撞检测算法3d版本
c++·qt·算法
数据门徒2 小时前
《人工智能现代方法(第4版)》 第11章 自动规划 学习笔记
人工智能·笔记·学习
老蒋新思维2 小时前
创客匠人万人峰会启示:AI+IP 生态重构,知识变现进入 “共生增长” 时代
网络·人工智能·网络协议·tcp/ip·重构·创始人ip·创客匠人
上海云盾-小余2 小时前
TCP业务DDoS防护专项方案
网络协议·tcp/ip·ddos
很㗊2 小时前
BSP之以太网接口学习笔记
linux·驱动开发·笔记·学习
apihz3 小时前
反向DNS查询与蜘蛛验证免费API接口详细教程
android·开发语言·数据库·网络协议·tcp/ip·dubbo
YJlio3 小时前
Active Directory 工具学习笔记(10.9):AdInsight——命令行选项与自动化采集模板
笔记·学习·自动化