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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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 = 0if( server->listen(QHostAddress::Any,port) ){QMessageBox::information(this,"","启动服务器成功!");}else{QMessageBox::information(this,"","启动服务器失败!");return;}connect(server,&QTcpServer::newConnection,this,&Widget::newConnection_slot);}