【Qt网络编程基础】Tcp服务器和客户端(只支持一对一)

news2024/9/24 7:21:47

目录

一、编写思路

1、服务器

总体思路

详细思路

1. 构造函数 (Widget::Widget)

2. 启动监听 (Widget::on_btn_start_clicked)

3. 停止监听 (Widget::on_btn_cease_clicked)

4. 发送消息 (Widget::on_btn_info_clicked)

5. 接收消息 (Widget::receive_message)

6. 处理客户端连接状态变化 (Widget::handler_client_changed)

7. 处理新的客户端连接 (Widget::handler_newConnection)

2、客户端

总体思路

详细思路

1. 初始化客户端对象

2. 处理“连接服务端”按钮点击事件

3. 处理“断开服务端”按钮点击事件

4. 处理“发送消息”按钮点击事件

5. 处理接收到的消息

6. 处理连接状态变化

二、实现效果

1、服务器

2、客户端

三、完整代码

1、服务器

widget.h

widget.cpp

2、客户端

widget.h

widget.cpp



如需咨询请添加个人微信:a15135158368

欢迎叨扰,多多交流


一、编写思路

1、服务器

总体思路

/* 构建服务器的步骤
 * 1. 实例化服务器对象--QTcpServer
 *    相当于:socket
 
 * 2. 进入监听状态--listen
 *    相当于:bind + listen
 
 * 3. 监测客户端连接--newConnection
 *    newConnection信号
 
 * 4. 获得客户端连接--nextPendingConnection
 *    相当于:accept
 
 * 5. 读取客户端消息--readAll
 *    相当于:recv
 
 * 6. 发送数据--write
 *    相当于:send
 
 * 7. 关闭连接--disconnectFromHost
 *    相当于:close
 */

详细思路

1. 构造函数 (Widget::Widget)

目的

  • 初始化窗口部件。

  • 创建并配置TCP服务器对象。

  • 连接信号与槽。

关键代码段

Widget::Widget(QWidget *parent)
    : QWidget(parent)  // 调用基类构造函数
    , ui(new Ui::Widget)  // 初始化UI对象
{
    ui->setupUi(this);  // 设置UI界面
    this->setWindowTitle("服务器");  // 设置窗口标题
    this->resize(1024, 960);  // 设置窗口大小
​
    // 创建QTcpServer对象
    tcp_server = new QTcpServer(this);
​
    // 连接QTcpServer的newConnection信号到槽函数
    connect(tcp_server, &QTcpServer::newConnection, this, &Widget::handler_newConnection);
​
    // QT4写法(不推荐):老式信号与槽连接
    // connect(tcpServer, SIGNAL(newConnection()), this, SLOT(handler_newConnection()));
}
2. 启动监听 (Widget::on_btn_start_clicked)

目的

  • 启动TCP服务器,开始监听客户端连接请求。

  • 绑定IP地址和端口号。

关键代码段

void Widget::on_btn_start_clicked()
{
    // 使TCP服务器进入监听状态
    if (tcp_server->listen(QHostAddress(IP), PORT))
    {
        ui->te_content->append("服务器开始监听...");  // 显示成功消息
    }
    else
    {
        ui->te_content->append("服务器监听失败");  // 显示失败消息
    }
}
3. 停止监听 (Widget::on_btn_cease_clicked)

目的

  • 停止TCP服务器的监听。

  • 关闭服务器连接。

关键代码段

void Widget::on_btn_cease_clicked()
{
    tcp_server->close();  // 关闭TCP服务器
    ui->te_content->append("服务器已停止监听");  // 显示停止消息
    // 服务器关闭后,更新UI按钮状态
}
4. 发送消息 (Widget::on_btn_info_clicked)

目的

  • 向所有已连接的客户端发送消息。

关键代码段

void Widget::on_btn_info_clicked()
{
    // 获取所有已连接的客户端
    QList<QTcpSocket*> clients = tcp_server->findChildren<QTcpSocket*>();
​
    // 遍历每个客户端
    for (int i = 0; i < clients.length(); i++)
    {
        QTcpSocket *client = clients[i];
​
        // 检查客户端的连接状态
        if (client->state() == QAbstractSocket::ConnectedState)
        {
            // 发送消息
            client->write(ui->le_info->text().toUtf8());
        }
        else
        {
            // 如果客户端未连接,显示提示信息
            ui->te_content->append("客户端未连接,无法发送消息");
        }
    }
}
5. 接收消息 (Widget::receive_message)

目的

  • 处理接收到的客户端消息,并在文本框中显示。

关键代码段

void Widget::receive_message()
{
    QTcpSocket *client_socket = qobject_cast<QTcpSocket*>(sender());  // 获取发信的客户端套接字
    if (client_socket)
    {
        QByteArray data = client_socket->readAll();  // 读取客户端发送的数据
        ui->te_content->append("客户端 : " + QString(data));  // 显示消息
    }
}
6. 处理客户端连接状态变化 (Widget::handler_client_changed)

目的

  • 处理客户端连接状态的变化,例如断开连接或连接成功。

  • 更新UI按钮状态。

关键代码段

void Widget::handler_client_changed(QAbstractSocket::SocketState socket_state)
{
    QTcpSocket *client_socket = (QTcpSocket*)sender();  // 获取发信的客户端套接字
​
    if (!client_socket) return;
​
    // 根据连接状态进行处理
    switch (socket_state)
    {
    case QAbstractSocket::UnconnectedState:  // 客户端断开连接
        ui->te_content->append("客户端断开连接");  // 显示断开连接信息
        client_socket->deleteLater();  // 删除客户端套接字对象
        ui->btn_start->setEnabled(true);  // 启用“开始监听”按钮
        ui->btn_cease->setEnabled(false);  // 禁用“停止监听”按钮
        ui->btn_info->setEnabled(false);  // 禁用“发送消息”按钮
        break;
​
    case QAbstractSocket::ConnectedState:  // 客户端已连接
        ui->btn_start->setEnabled(false);  // 禁用“开始监听”按钮
        ui->btn_cease->setEnabled(true);  // 启用“停止监听”按钮
        ui->btn_info->setEnabled(true);  // 启用“发送消息”按钮
        ui->te_content->append("客户端已连接");  // 显示连接成功信息
        break;
​
    default:
        break;
    }
}
7. 处理新的客户端连接 (Widget::handler_newConnection)

目的

  • 处理新客户端的连接请求。

  • 获取客户端的IP地址和端口号。

  • 连接相关的信号和槽,处理客户端的消息和状态变化。

关键代码段

void Widget::handler_newConnection()
{
    // 获取新的客户端连接
    QTcpSocket *client_socket = tcp_server->nextPendingConnection();
​
    // 错误处理:检查是否成功获取客户端连接
    if (client_socket == nullptr)
    {
        ui->te_content->append("错误:无法获取客户端连接");  // 显示错误信息
        qDebug() << "Error: Failed to get new client connection.";
        return;  // 获取失败,直接返回
    }
​
    ui->btn_start->setEnabled(false);  // 禁用“开始监听”按钮
    ui->btn_cease->setEnabled(true);  // 启用“停止监听”按钮
    ui->btn_info->setEnabled(true);  // 启用“发送消息”按钮
​
    // 获取客户端的IP地址和端口号
    QString ip_addr = client_socket->peerAddress().toString();
    quint16 port = client_socket->peerPort();
​
    // 错误处理:检查是否成功获取IP地址和端口号
    if (ip_addr.isEmpty() || port == 0) {
        ui->te_content->append("错误:无法获取客户端的IP地址或端口号");  // 显示错误信息
        qDebug() << "Error: Failed to get client's IP address or port.";
        client_socket->disconnectFromHost();  // 断开连接
        client_socket->deleteLater();  // 删除客户端套接字对象
        return;  // 获取失败,直接返回
    }
​
    // 打印客户端的IP地址和端口号
    ui->te_content->append("客户端的IP地址为: " + ip_addr);
    ui->te_content->append("客户端的端口号为: " + QString::number(port));
​
    // 连接信号与槽,处理客户端的消息和状态变化
    connect(client_socket, &QTcpSocket::readyRead, this, &Widget::receive_message);
    connect(client_socket, &QTcpSocket::stateChanged, this, &Widget::handler_client_changed);
​
    // 错误处理:处理客户端的异常断开情况
    connect(client_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::errorOccurred),
            this, [=](QAbstractSocket::SocketError socketError)
            {
                ui->te_content->append("错误:客户端连接错误,错误代码:" + QString::number(socketError));  // 显示错误信息
                qDebug() << "Client connection error, error code: " << socketError;
                client_socket->deleteLater();  // 删除客户端套接字对象
            });
}

2、客户端

总体思路

/*
 * 客户端构建步骤:
 * 1、实例化QTcpSocket对象
 
 * 2、连接服务器--connectToHost
 
 * 3、判断是否连接成功--waitForConnected
 
 * 4、连接对端接收信号--readyRead
 
 * 5、发送数据--write
 
 * 6、关闭连接--disconnectFromHost
 */

详细思路

1. 初始化客户端对象
  • 步骤

    创建 QTcpSocket 对象,设置 UI 界面,并连接信号与槽函数。

  • 关键代码段:

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);  // 设置UI界面
        this->setWindowTitle("-客户端-");  // 设置窗口标题
        this->resize(1024, 960);  // 设置窗口大小
    ​
        tcp_socket = new QTcpSocket(this);  // 创建QTcpSocket对象
    ​
        // 连接QTcpSocket的信号与槽函数
        connect(tcp_socket, &QTcpSocket::readyRead, this, &Widget::receiveMessage);
        connect(tcp_socket, &QTcpSocket::stateChanged, this, &Widget::mStateChanged);
    }
  • 详细说明:

    • ui->setupUi(this): 初始化UI界面。

    • tcp_socket = new QTcpSocket(this): 创建TCP套接字对象,用于与服务器进行通信。

    • connect(): 连接QTcpSocket的信号readyReadstateChanged到对应的槽函数receiveMessagemStateChanged

2. 处理“连接服务端”按钮点击事件
  • 步骤

    调用 QTcpSocketconnectToHost() 方法来连接服务器。

  • 关键代码段:

    void Widget::on_btn_start_clicked()
    {
        // 尝试连接到指定的IP地址和端口号
        tcp_socket->connectToHost(QHostAddress(IP), PORT);
    }
  • 详细说明:

    • tcp_socket->connectToHost(QHostAddress(IP), PORT): 连接到指定的IP地址和端口号。此函数异步执行,连接成功或失败将触发相应的信号。

3. 处理“断开服务端”按钮点击事件
  • 步骤

    调用 QTcpSocketdisconnectFromHost() 方法断开与服务器的连接。

  • 关键代码段:

    void Widget::on_btn_cease_clicked()
    {
        // 断开与服务器的连接
        tcp_socket->disconnectFromHost();
    }
  • 详细说明

    • tcp_socket->disconnectFromHost(): 断开与服务器的连接,释放资源。

4. 处理“发送消息”按钮点击事件
  • 步骤

    检查客户端是否已连接到服务器,然后发送消息。

  • 关键代码段:

    void Widget::on_btn_info_clicked()
    {
        // 检查当前是否处于连接状态
        if (tcp_socket->state() == QAbstractSocket::ConnectedState)
        {
            // 发送消息到服务器
            tcp_socket->write(ui->le_info->text().toUtf8());
        }
        else
        {
            // 如果未连接,显示提示信息
            ui->te_content->append("请先连接服务器!");
        }
    }
  • 详细说明

    • tcp_socket->state() == QAbstractSocket::ConnectedState: 确保在发送消息之前客户端已连接到服务器。

    • tcp_socket->write(): 发送消息到服务器。消息需转换为字节数组(QByteArray)。

5. 处理接收到的消息
  • 步骤

    读取服务器发送的数据并显示在文本框中。

  • 关键代码段:

    void Widget::receiveMessage()
    {
        // 读取所有接收到的数据,并显示在文本框中
        ui->te_content->append("服务器发送的消息:" + tcp_socket->readAll());
    }
  • 详细说明:

    • tcp_socket->readAll(): 读取从服务器接收到的所有数据,并以字符串形式显示在文本框中。

6. 处理连接状态变化
  • 步骤

    根据连接状态的变化更新UI和显示状态信息。

  • 关键代码段:

    void Widget::mStateChanged(QAbstractSocket::SocketState socketstate)
    {
        switch (socketstate)
        {
        case QAbstractSocket::UnconnectedState:
            ui->te_content->append("与服务器断开连接");
            ui->btn_start->setEnabled(true);   // 允许重新连接
            ui->btn_cease->setEnabled(false);  // 禁用断开按钮
            ui->btn_info->setEnabled(false);   // 禁用发送按钮
            break;
    ​
        case QAbstractSocket::ConnectedState:
            ui->te_content->append("与服务器建立连接");
            ui->btn_start->setEnabled(false);  // 禁用连接按钮
            ui->btn_cease->setEnabled(true);   // 允许断开
            ui->btn_info->setEnabled(true);    // 允许发送消息
            break;
    ​
        case QAbstractSocket::HostLookupState:
            ui->te_content->append("正在查找主机...");
            break;
    ​
        case QAbstractSocket::ConnectingState:
            ui->te_content->append("正在连接服务器...");
            break;
    ​
        case QAbstractSocket::ClosingState:
            ui->te_content->append("正在关闭连接...");
            break;
    ​
        default:
            // 显示未知状态的调试信息
            qDebug() << "未知的错误, 当前状态: " << socketstate;
            break;
        }
    }
  • 详细说明

    • 根据 socketstate 的不同值更新UI和显示状态信息。

    • QAbstractSocket::UnconnectedState: 客户端已断开连接。

    • QAbstractSocket::ConnectedState: 客户端已连接到服务器。

    • QAbstractSocket::HostLookupState, QAbstractSocket::ConnectingState, QAbstractSocket::ClosingState: 其他状态信息。

二、实现效果

1、服务器

2、客户端

三、完整代码

1、服务器

widget.h

#ifndef WIDGET_H
#define WIDGET_H
​
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>  // TCP服务器和客户端都使用
​
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
​
// Widget类继承自QWidget,是一个用于实现TCP服务器的界面
class Widget : public QWidget
{
    Q_OBJECT
​
public:
    // 构造函数,初始化Widget对象
    Widget(QWidget *parent = nullptr);
​
    // 析构函数,清理资源
    ~Widget();
​
private slots:
    // 处理“开始”按钮点击事件的槽函数
    void on_btn_start_clicked();
​
    // 处理“停止”按钮点击事件的槽函数
    void on_btn_cease_clicked();
​
    // 处理“信息”按钮点击事件的槽函数
    void on_btn_info_clicked();
​
    // 处理接收到消息的槽函数
    void receive_message();
​
    // 处理客户端连接状态变化的槽函数
    void handler_client_changed(QAbstractSocket::SocketState socket_state);
​
    // 处理新的连接请求的槽函数
    void handler_newConnection();
​
private:
    // UI对象指针,用于管理界面组件
    Ui::Widget *ui;
​
    // TCP服务器对象指针
    QTcpServer *tcp_server;
};
​
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
​
const char* IP = "127.0.0.1";    // 定义IP地址常量,表示本地地址
const unsigned int PORT = 8888;  // 定义端口号常量,表示服务器监听的端口
​
/* 构建服务器的步骤
 * 1. 实例化服务器对象--QTcpServer
 *    相当于:socket
 * 2. 进入监听状态--listen
 *    相当于:bind + listen
 * 3. 监测客户端连接--newConnection
 *    newConnection信号
 * 4. 获得客户端连接--nextPendingConnection
 *    相当于:accept
 * 5. 读取客户端消息--readAll
 *    相当于:recv
 * 6. 发送数据--write
 *    相当于:send
 * 7. 关闭连接--disconnectFromHost
 *    相当于:close
 */
​
/*
 * te_content
 * btn_start btn_cease
 * btn_info le_info
 */
​
/***********************************************************
  * @函数名:Widget
  * @功  能:构造函数---创建服务端对象,与客户端连接
  * @参  数:parent---父对象
  * @返回值:无
  *********************************************************/
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)  // 初始化UI对象
{
    ui->setupUi(this);  // 设置UI界面
    this->setWindowTitle("服务器");  // 设置窗口标题
    this->resize(1024, 960);  // 设置窗口大小
​
    // 创建QTcpServer对象,用于监听客户端连接
    tcp_server = new QTcpServer(this);
​
    // 连接QTcpServer的newConnection信号到处理新的连接的槽函数
    connect(tcp_server, &QTcpServer::newConnection, this, &Widget::handler_newConnection);
​
    // QT4写法(不推荐使用):连接信号与槽的老式写法
    // connect(tcpServer, SIGNAL(newConnection()), this, SLOT(handler_newConnection()));
}
​
Widget::~Widget()
{
    delete ui;  // 删除UI对象,释放资源
}
​
/***********************************************************
  * @函数名:on_btn_start_clicked()
  * @功  能:按钮"开始监听"的槽函数,开始监听服务器,绑定IP和端口
  * @参  数:无
  * @返回值:无
  *********************************************************/
void Widget::on_btn_start_clicked()
{
    // 进入监听状态,绑定IP和端口
    if (tcp_server->listen(QHostAddress(IP), PORT))
    {
        ui->te_content->append("服务器开始监听...");  // 在文本框中显示成功消息
    }
    else
    {
        ui->te_content->append("服务器监听失败");  // 在文本框中显示失败消息
    }
}
​
/***********************************************************
  * @函数名:on_btn_cease_clicked()
  * @功  能:按钮"停止监听"的槽函数,停止监听并关闭服务器
  * @参  数:无
  * @返回值:无
  *********************************************************/
void Widget::on_btn_cease_clicked()
{
    tcp_server->close();  // 关闭TCP服务器
    ui->te_content->append("服务器已停止监听");  // 在文本框中显示停止消息
    // 服务器关闭后,相关UI按钮状态更新
}
​
/***********************************************************
  * @函数名:on_btn_info_clicked()
  * @功  能:按钮"发送消息"的槽函数,将文本信息发送给所有客户端
  * @参  数:无
  * @返回值:无
  *********************************************************/
void Widget::on_btn_info_clicked()
{
    // 获取所有已连接的客户端
    QList<QTcpSocket*> clients = tcp_server->findChildren<QTcpSocket*>();
​
    // 遍历每个客户端
    for (int i = 0; i < clients.length(); i++)
    {
        QTcpSocket *client = clients[i];
​
        // 检查客户端的连接状态
        if (client->state() == QAbstractSocket::ConnectedState)
        {
            // 发送数据
            client->write(ui->le_info->text().toUtf8());
        }
        else
        {
            // 如果客户端未连接,输出信息提示
            ui->te_content->append("客户端未连接,无法发送消息");
        }
    }
}
​
/***********************************************************
  * @函数名:receive_message
  * @功  能:槽函数:接收客户端发送的消息并在文本框中显示
  * @参  数:无
  * @返回值:无
  *********************************************************/
void Widget::receive_message()
{
    QTcpSocket *client_socket = qobject_cast<QTcpSocket*>(sender());  // 获取发信的客户端套接字
    if (client_socket)
    {
        QByteArray data = client_socket->readAll();  // 读取客户端发送的所有数据
        ui->te_content->append("客户端 : " + QString(data));  // 在文本框中显示消息
    }
}
​
/***********************************************************
  * @函数名:handler_client_changed
  * @功  能:槽函数:处理客户端连接状态的改变
  * @参  数:socketState---客户端连接状态
  * @返回值:无
  *********************************************************/
void Widget::handler_client_changed(QAbstractSocket::SocketState socket_state)
{
    QTcpSocket *client_socket = (QTcpSocket*)sender();  // 获取发信的客户端套接字
​
    if (!client_socket) return;
​
    // 根据连接状态进行处理
    switch (socket_state)
    {
    case QAbstractSocket::UnconnectedState:  // 客户端断开连接
        ui->te_content->append("客户端断开连接");  // 在文本框中显示断开连接信息
        client_socket->deleteLater();  // 删除客户端套接字对象
        ui->btn_start->setEnabled(true);  // 启用“开始监听”按钮
        ui->btn_cease->setEnabled(false);  // 禁用“停止监听”按钮
        ui->btn_info->setEnabled(false);  // 禁用“发送消息”按钮
        break;
​
    case QAbstractSocket::ConnectedState:  // 客户端已连接
        ui->btn_start->setEnabled(false);  // 禁用“开始监听”按钮
        ui->btn_cease->setEnabled(true);  // 启用“停止监听”按钮
        ui->btn_info->setEnabled(true);  // 启用“发送消息”按钮
        ui->te_content->append("客户端已连接");  // 在文本框中显示连接成功信息
        break;
​
    default:
        break;
    }
}
​
/***********************************************************
  * @函数名:handler_newConnection
  * @功  能:槽函数:当有新的客户端连接时,打印客户端IP和端口
  * @参  数:无
  * @返回值:无
  *********************************************************/
void Widget::handler_newConnection()
{
    // 获取新的客户端连接
    QTcpSocket *client_socket = tcp_server->nextPendingConnection();
​
    // 错误处理:检查是否成功获取客户端连接
    if (client_socket == nullptr)
    {
        ui->te_content->append("错误:无法获取客户端连接");  // 在文本框中显示错误信息
        qDebug() << "Error: Failed to get new client connection.";
        return;  // 如果获取失败,直接返回
    }
​
    ui->btn_start->setEnabled(false);  // 禁用“开始监听”按钮
    ui->btn_cease->setEnabled(true);  // 启用“停止监听”按钮
    ui->btn_info->setEnabled(true);  // 启用“发送消息”按钮
​
    // 获取客户端的IP地址和端口号
    QString ip_addr = client_socket->peerAddress().toString();
    quint16 port = client_socket->peerPort();
​
    // 错误处理:检查是否成功获取IP地址和端口号
    if (ip_addr.isEmpty() || port == 0) {
        ui->te_content->append("错误:无法获取客户端的IP地址或端口号");  // 在文本框中显示错误信息
        qDebug() << "Error: Failed to get client's IP address or port.";
        client_socket->disconnectFromHost();  // 断开连接
        client_socket->deleteLater();  // 删除客户端套接字对象
        return;  // 如果获取失败,直接返回
    }
​
    // 打印客户端的IP地址和端口号
    ui->te_content->append("客户端的IP地址为: " + ip_addr);
    ui->te_content->append("客户端的端口号为: " + QString::number(port));
​
    // 处理接收数据的槽函数
    connect(client_socket, &QTcpSocket::readyRead, this, &Widget::receive_message);
​
    // 处理连接状态变化的槽函数
    connect(client_socket, &QTcpSocket::stateChanged, this, &Widget::handler_client_changed);
​
    // 错误处理:处理客户端的异常断开情况
    connect(client_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::errorOccurred),
            this, [=](QAbstractSocket::SocketError socketError)
            {
                ui->te_content->append("错误:客户端连接错误,错误代码:" + QString::number(socketError));  // 显示错误信息
                qDebug() << "Client connection error, error code: " << socketError;
                client_socket->deleteLater();  // 删除客户端套接字对象
            });
}

2、客户端

widget.h

#ifndef WIDGET_H
#define WIDGET_H
​
#include <QWidget>
#include <QTcpSocket>
#include <QHostAddress>
​
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
​
// 定义Widget类,继承自QWidget
class Widget : public QWidget
{
    Q_OBJECT  // Qt的元对象系统宏,支持信号和槽
​
public:
    // 构造函数:初始化Widget对象
    // @param parent:父对象指针,默认为nullptr
    Widget(QWidget *parent = nullptr);
​
    // 析构函数:释放资源
    ~Widget();
​
private slots:
    // 槽函数:处理点击"开始"按钮事件
    void on_btn_start_clicked();
​
    // 槽函数:处理点击"停止"按钮事件
    void on_btn_cease_clicked();
​
    // 槽函数:处理点击"发送信息"按钮事件
    void on_btn_info_clicked();
​
    // 槽函数:接收并处理来自客户端的消息
    void receive_message();
​
    // 槽函数:处理TCP连接状态改变事件
    // @param socketstate:连接状态
    void state_changed(QAbstractSocket::SocketState socketstate);
​
private:
    Ui::Widget *ui;  // 指向UI界面的指针
​
    QTcpSocket *tcp_socket;  // 指向TCP套接字对象的指针
};
​
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QTcpSocket>
#include <QHostAddress>
#include <QDebug>
​
const char* IP = "127.0.0.1";  // 定义服务器的IP地址
const unsigned int PORT = 8888;  // 定义服务器的端口号
​
/*
 * 客户端构建步骤:
 * 1、实例化QTcpSocket对象
 * 2、连接服务器--connectToHost
 * 3、判断是否连接成功--waitForConnected
 * 4、连接对端接收信号--readyRead
 * 5、发送数据--write
 * 6、关闭连接--disconnectFromHost
 */
​
/*
 * te_content:文本框,用于显示接收到的消息和状态信息
 * btn_start:按钮,用于启动与服务器的连接
 * btn_cease:按钮,用于断开与服务器的连接
 * btn_info:按钮,用于发送消息到服务器
 * le_info:文本框,用于输入待发送的消息
 */
​
/***********************************************************
 * @函数名:Widget
 * @功  能:构造函数,初始化客户端界面和TCP套接字对象
 * @参  数:parent---父对象
 * @返回值:无
 *********************************************************/
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);  // 设置UI界面
    this->setWindowTitle("-客户端-");  // 设置窗口标题
    this->resize(1024, 960);  // 设置窗口大小
​
    tcp_socket = new QTcpSocket(this);  // 创建QTcpSocket对象
​
    // 连接QTcpSocket的信号与槽函数
    connect(tcp_socket, &QTcpSocket::readyRead, this, &Widget::receive_message);
    connect(tcp_socket, &QTcpSocket::stateChanged, this, &Widget::state_changed);
​
    // QT4写法(不推荐):老式信号与槽连接
    // connect(tcp_socket, SIGNAL(readyRead()), this, SLOT(receive_message()));
    // connect(tcp_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
    //         this, SLOT(state_changed(QAbstractSocket::SocketState)));
}
​
/***********************************************************
 * @函数名:~Widget
 * @功  能:析构函数,释放资源
 * @参  数:无
 * @返回值:无
 *********************************************************/
Widget::~Widget()
{
    delete ui;  // 删除UI对象
}
​
/***********************************************************
 * @函数名:on_btn_start_clicked()
 * @功  能:处理点击“连接服务端”按钮的事件
 * @参  数:无
 * @返回值:无
 *********************************************************/
void Widget::on_btn_start_clicked()
{
    // 尝试连接到指定的IP地址和端口号
    tcp_socket->connectToHost(QHostAddress(IP), PORT);
}
​
/***********************************************************
 * @函数名:on_btn_cease_clicked()
 * @功  能:处理点击“断开服务端”按钮的事件
 * @参  数:无
 * @返回值:无
 *********************************************************/
void Widget::on_btn_cease_clicked()
{
    // 断开与服务器的连接
    tcp_socket->disconnectFromHost();
}
​
/***********************************************************
 * @函数名:on_btn_info_clicked()
 * @功  能:处理点击“发送消息”按钮的事件
 *        发送消息到服务器,只有在连接状态时才允许发送
 * @参  数:无
 * @返回值:无
 *********************************************************/
void Widget::on_btn_info_clicked()
{
    // 检查当前是否处于连接状态
    if (tcp_socket->state() == QAbstractSocket::ConnectedState)
    {
        // 发送消息到服务器
        tcp_socket->write(ui->le_info->text().toUtf8());
    }
    else
    {
        // 如果未连接,显示提示信息
        ui->te_content->append("请先连接服务器!");
    }
}
​
/***********************************************************
 * @函数名:receive_message
 * @功  能:槽函数---接收服务器发送的数据,并显示在文本框中
 * @参  数:无
 * @返回值:无
 *********************************************************/
void Widget::receive_message()
{
    // 读取所有接收到的数据,并显示在文本框中
    ui->te_content->append("服务器发送的消息:" + tcp_socket->readAll());
}
​
/***********************************************************
 * @函数名:state_changed
 * @功  能:槽函数---处理客户端连接状态的改变
 * @参  数:socketstate---当前连接状态
 * @返回值:无
 *********************************************************/
void Widget::state_changed(QAbstractSocket::SocketState socketstate)
{
    // 根据不同的连接状态更新UI和显示状态信息
    switch (socketstate)
    {
    case QAbstractSocket::UnconnectedState:
        ui->te_content->append("与服务器断开连接");
        ui->btn_start->setEnabled(true);   // 允许重新连接
        ui->btn_cease->setEnabled(false);  // 禁用断开按钮
        ui->btn_info->setEnabled(false);   // 禁用发送按钮
        break;
​
    case QAbstractSocket::ConnectedState:
        ui->te_content->append("与服务器建立连接");
        ui->btn_start->setEnabled(false);  // 禁用连接按钮
        ui->btn_cease->setEnabled(true);   // 允许断开
        ui->btn_info->setEnabled(true);    // 允许发送消息
        break;
​
    case QAbstractSocket::HostLookupState:
        ui->te_content->append("正在查找主机...");
        break;
​
    case QAbstractSocket::ConnectingState:
        ui->te_content->append("正在连接服务器...");
        break;
​
    case QAbstractSocket::ClosingState:
        ui->te_content->append("正在关闭连接...");
        break;
​
    default:
        // 显示未知状态的调试信息
        qDebug() << "未知的错误, 当前状态: " << socketstate;
        break;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2121920.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

顶象图标点选模型识别

注意,本文只提供学习的思路,严禁违反法律以及破坏信息系统等行为,本文只提供思路 如有侵犯,请联系作者下架 (水文一篇)使用resnet分类模型配合yolo训练的图标点选验证码,用于顶象图标点选,案例如下图:

类型参数传值问题

一、基本数据类型传参问题 public static void main(String[] args) throws Exception {Integer number null;method01(number);}private static void method01(int number){System.out.println("number " number);}Ps: 基于int基本数据类型传参的时候&#xff0c…

常用的Adobe XD插件就看这10款!

Adobe XD是为 UI/UX 设计量身定制的协作工具&#xff0c;它与 Adobe 其他产品的无缝集成使得设计工作更加高效。与专注于 MacOS 的 Sketch不同&#xff0c;Adobe XD支持多平台操作&#xff0c;这种灵活性让它在原型设计领域备受瞩目。为了更高效地使用 Adobe XD&#xff0c;插件…

【大模型专栏—入门篇】机器学习与深度学习基础测试

大模型专栏介绍 &#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本文为大模型专栏子篇&#xff0c;大模型专栏将持续更新&#xff0c;主要讲解大模型从入门到实战打怪升级。如有兴趣&#xff0c;欢迎您的阅读。 &#x1f4…

开源项目低代码表单FormCreate中ElementPlus表单使用校验规则示例

在开源项目低代码表单FormCreate 中&#xff0c;可以通过 validate 配置项为表单组件设置验证规则。无论是内置的表单组件还是自定义的表单组件&#xff0c;都支持表单校验。本文将详细介绍验证规则的使用方法&#xff0c;并提供一些示例来帮助您更好地理解和应用这些功能。 源…

2024年最新微短剧系统重构版,短剧小程序源码开源源码搭建部署

技术栈 前端&#xff1a;vueuniapp 后端&#xff1a;php 数据库&#xff1a;MySQL <?php class Drama {private $title;private $description;private $cast;private $genre;public function __construct($title, $description, $cast, $genre) {$this->title $tit…

iPhone手机备忘录转移到Windows电脑上的方法

备忘录作为我们日常生活中常用的软件&#xff0c;帮助我们记录下重要事项、待办任务、灵感创意等&#xff0c;已成为许多人不可或缺的工具。然而&#xff0c;当我们需要在不同设备间转移备忘录内容时&#xff0c;常常会遇到一些困难。特别是从iPhone手机转移到Windows电脑上&am…

从0到1实现线程池(C语言版)

目录 &#x1f324;️1. 基础知识 ⛅1.1 线程概述 ⛅1.2 linux下线程相关函数 &#x1f325;️1.2.1 线程ID &#x1f325;️1.2.2 线程创建 &#x1f325;️1.2.3 线程回收 &#x1f325;️1.2.4 线程分离 &#x1f324;️2. 线程池概述 ⛅2.1 线程池的定义 ⛅2.2 为…

多种现货黄金的简介 第三种你要特别注意

近期黄金的价格全线上涨&#xff0c;虽然作为商品&#xff0c;一般人看到其价格上涨&#xff0c;购买它的欲望就下跌了。但是作为一种投资品种&#xff0c;在近期黄金价格的上涨以及未来美联储货币政策的影响下&#xff0c;他的投资潜力是巨大的。但市场中有那么多的黄金理财产…

JavaScript编程精粹:语法、函数与对象

JavaScript编程精粹&#xff1a;语法、函数与对象 一 . JavaScript 介绍1.1 应用场景1.2 JavaScript 介绍1.3 JavaScript 组成部分 二 . JavaScript 引入方式2.1 内部脚本2.2 外部脚本 三 . JavaScript 基础语法3.1 输出语句3.2 变量3.3 数据类型3.3.1 number 类型3.3.2 string…

W11电脑无法找到音频输出设备,所有声音设备都是空的的解决方法

搜索&#xff1a;设备管理器 然后下滑查看。 在查看的时候&#xff0c;发现OED上面有一个黄色的感叹号 右键更新之后即可&#xff0c;笔者是选择的本地更新。 之后右下角的声音设备就恢复正常了

局域网设备自动发现常用方法

文章目录 需求实现方法ARP (Address Resolution Protocol)Ping ip的流程抓包如下代码实现 mDNS 对比测试Avahi 介绍Avahi 安装Avahi 使用测试代码 需求 局域网设备自动发现是软件开发中的一个常见且重要的需求&#xff0c;它简化了设备间的协作机制&#xff0c;降低了软件各模…

自动化测试之Selenium的使用

06、Selenium的使用 Selenium 是一个自动化测试工具&#xff0c;利用它可以驱动浏览器执行特定的动作&#xff0c;如点击、下拉等操作&#xff0c;同时还可以获取浏览器当前呈现的页面的源代码&#xff0c;做到可见即可爬。对于一些 JavaScript 动态渲染的页面来说&#xff0c…

CleanMyMac X2024破解版mac电脑清理工具

今天&#xff0c;我要跟大家分享一个让我彻底告别电脑卡顿的秘密武器——CleanMyMac X。这不仅仅是一款普通的清理工具&#xff0c;它是你电脑的私人健身教练&#xff0c;让电脑焕发青春活力&#xff01; CleanMyMac直装官方版下载地址&#xff1a; http://wm.makeding.com/i…

公共英语三级考试时间安排

公共英语三级考试时间安排

STM32学习笔记(二、初识stm32单片机)

一、stm32的含义是什么&#xff1f; 首先stm32是意法半导体公司&#xff08;ST&#xff09;使用ARM公司的Cortex-M为核心生产的32位的单片机。 其中&#xff0c;ST---意法半导体公司&#xff0c;即SOC厂商。 M---为Microelectronics的缩写&#xff0c;即微型处理器。 32---表示…

erp系统有哪些品牌?盘点2024年值得关注的十个爆款erp品牌!

本文将盘点主流的erp品牌&#xff0c;为企业选型提供参考&#xff01; ERP系统是企业数字化转型的基石&#xff0c;选择一款适合企业自身需求的ERP系统&#xff0c;不仅能够显著提升企业的运营效率&#xff0c;还能为企业的长期发展奠定坚实的基础。 随着市场竞争的日益激烈&a…

比较:#define,const,typedef

目录 1. #define 2. const 3. typedef #define 用于文本替换&#xff0c;没有类型检查&#xff0c;适用于定义简单的常量或宏。const 是类型安全的编译时常量&#xff0c;适合定义不可变的变量&#xff0c;有范围控制。typedef 用于定义类型的别名&#xff0c;简化代码复杂性…

ubuntu20.04 Qt6引用dcmtk库实现dicom文件读取和字符集转换

1 环境问题 安装完Qt6&#xff0c;新建Qt/QtQuick CMake工程编译出现如下错误: Found package configuration file: Qt6Config.cmake but it set Qt6 FOUND to FALSE so package "Qt6" is considered to be NOT FOUND. 原因&#xff1a; 这是因为系统中缺少OpenG…