作业8/21

 client

cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    , socket(new QTcpSocket(this))
{
    ui->setupUi(this); // 设置 UI 界面
    // 控件初始状态设置为禁用,防止未连接时误操作
    ui->sendBtn->setEnabled(false);
    ui->sendEdit->setEnabled(false);
    ui->brokenBtn->setEnabled(false);

    // 信号与槽的连接
    connect(socket, &QTcpSocket::connected, this, &Widget::connected_slot);
    connect(socket, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);

    //如果成功断开与服务器的连接,那么客户端就会自动发射一个disconnected的信号
    //我们就可以将该信号连接到自定义的槽函数,。由于只需连接一次,所以连接函数写在构造函数中。
    connect(socket, &QTcpSocket::disconnected, this, &Widget::disconnected_slot);

}

Widget::~Widget()
{
    delete ui;
    delete socket; // 释放socket的内存
}

// 当连接成功时的处理函数
void Widget::connected_slot()
{
    userName = ui->userEdit->text(); // 读取用户输入的用户名
    QString msg = userName + ":进入聊天室"; // 构建进入聊天室的欢迎消息
    // 将消息写入 socket,并发送到服务器
    socket->write(msg.toLocal8Bit());
    socket->flush(); // 确保所有数据都被发送

    // 显示连接成功的信息
    QMessageBox::information(this,"提示","已连接到服务器");


    // 启用发送控件,禁用连接控件
    ui->sendEdit->setEnabled(true);
    ui->sendBtn->setEnabled(true);
    ui->brokenBtn->setEnabled(true);

    ui->userEdit->setEnabled(false);
    ui->ipEdit->setEnabled(false);
    ui->portEdit->setEnabled(false);
    ui->connectBtn->setEnabled(false);
}

// 当 socket 准备读数据时的处理函数
void Widget::readyRead_slot()
{
    // 读取 socket 中的所有数据
    QByteArray msg = socket->readAll();
    qDebug() << msg;
    //将数据放入ui界面上
    ui->listWidget->addItem(QString::fromLocal8Bit(msg));
}

//disconnected()信号对应的槽函数
void Widget::disconnected_slot()
{
    //组件可用的相关设置
    ui->sendEdit->setEnabled(false);
    ui->sendBtn->setEnabled(false);
    ui->brokenBtn->setEnabled(false);


    ui->userEdit->setEnabled(true);
    ui->ipEdit->setEnabled(true);
    ui->portEdit->setEnabled(true);
    ui->connectBtn->setEnabled(true);
}

//连接服务器按钮对应的槽函数
void Widget::on_connectBtn_clicked()
{
    //获取ui界面上的ip和端口号
    QString ip = ui->ipEdit->text();
    quint16 port = ui->portEdit->text().toUShort(); // 转换为 quint16 //字符串转换成整型

    socket->connectToHost(ip,port);
}

//发送按钮对应的槽函数
void Widget::on_sendBtn_clicked()
{
     //获取ui界面的信息
    QString msg = ui->sendEdit->text();
    msg = userName + ":" + msg;

     //发送给服务器
    socket->write(msg.toLocal8Bit());
    socket->flush();
    //清空行编辑器
    ui->sendEdit->clear();
}

//断开连接按钮对应的槽函数
void Widget::on_brokenBtn_clicked()
{
    //告诉服务器 我走了
    QString msg = userName + ":离开聊天室";
    //发送给服务器
    socket->write(msg.toLocal8Bit());
    socket->flush();
    //断开与服务器的连接
    socket->disconnectFromHost(); // 断开连接
    //如果成功断开与服务器的连接,那么客户端就会自动发射一个disconnected的信号
    //我们就可以将该信号连接到自定义的槽函数,。由于只需连接一次,所以连接函数写在构造函数中。

}
h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer> // 注意:QTcpServer 通常用于服务器端,这里应使用 QTcpSocket
#include <QTcpSocket> // 客户端类
#include <QMessageBox> // 消息对话框
#include <QDebug>
#include <QByteArray>
#include <QIODevice>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

signals:
    void disconnected();

// 私有槽函数,由信号触发执行
private slots:
    // 当 QTcpSocket 连接成功时调用
    void connected_slot();

    // 当 QTcpSocket 准备读取数据时调用
    void readyRead_slot();

    void disconnected_slot();

// 公共槽函数,由 UI 控件触发执行
public slots:
    void on_connectBtn_clicked();
    void on_sendBtn_clicked();
    void on_brokenBtn_clicked();

private:
    Ui::Widget *ui;
    QTcpSocket *socket; // QTcpSocket 对象,用于客户端连接
    QString userName; // 用户名

};
#endif // WIDGET_H

服务器 

.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QMessageBox>
#include <QDebug>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

public slots:
    void newConnection_slot();

    void readyRead_slot();

private slots:
    void on_startBtn_clicked();

private:
    Ui::Widget *ui;

    QTcpServer *server;

    QList<QTcpSocket *> socketList;
};
#endif // WIDGET_H
.cpp
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    , server(new QTcpServer(this))
{
    ui->setupUi(this);

}

Widget::~Widget()
{
    delete ui;
}
// newConnection信号对应槽函数
void Widget::newConnection_slot()
{
    qDebug() << "有新的客户端连接····";
    // 获取最新客户端套接字
    // virtual QTcpSocket *nextPendingConnection();
    // 返回值:客户端套接字————QTcpSocket类
    QTcpSocket *s = server->nextPendingConnection();

    // 将获取的客户端套接字放入容器
    socketList.push_back(s);

    //
    connect(s,&QTcpSocket::readyRead,this,&Widget::readyRead_slot);
}

void Widget::readyRead_slot()
{
    for (int i = 0; i < socketList.count(); i++) {
        if (socketList.at(i)->state() == 0)
        {
            socketList.removeAt(i);
        }
    }

    for (int i = 0; i < socketList.count(); i++) {
        // 判断哪个客户端有数据待读
        if (socketList.at(i)->bytesAvailable() != 0)
        {
            // 读取数据
            QByteArray msg = socketList.at(i)->readAll();

            //放入ui界面
            ui->listWidget->addItem(QString::fromLocal8Bit(msg));

            // 广播所有客户端
            for (int j = 0; j < socketList.count(); j++) {
                socketList.at(j)->write(msg);
            }
        }
    }
}

//启动服务器按钮的槽函数
void Widget::on_startBtn_clicked()
{
    // ui界面的端口号
    quint16 port = ui->portEdit->text().toUInt();
    // 服务器监听连接
    // bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
    // const QHostAddress &address = QHostAddress::Any,
    // quint16 port = 0
    if( server->listen(QHostAddress::Any,port) )
    {
        QMessageBox::information(this,"","启动服务器成功!");
    }
    else
    {
        QMessageBox::information(this,"","启动服务器失败!");
        return;
    }

    connect(server,&QTcpServer::newConnection,this,&Widget::newConnection_slot);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值