服务段头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTcpServer>
#include<QMessageBox>
#include<QDebug>
#include<QList>
#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
服务段测试文件
#include "widget.h"
#include "ui_widget.h"
#include "widget.h"
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
,server(new QTcpServer(this)) // 创建新的 QTcpServer 对象,指定当前对象为其父对象
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
// 槽函数:有新连接时调用,处理新的客户端连接
void Widget::newConnection_slot()
{
qDebug() << "有新客户连接。。。"; // 输出调试信息,表示有新客户端连接
QTcpSocket *s = server->nextPendingConnection(); // 获取下一个等待处理的连接
socketList.push_back(s); // 将新连接的 socket 加入到 socketList 列表中
connect(s, &QTcpSocket::readyRead, this, &Widget::readyRead_slot); // 连接 readyRead 信号,当有数据可读时,调用 readyRead_slot 函数
}
// 槽函数:有数据可读时调用,处理客户端数据
void Widget::readyRead_slot()
{
// 遍历 socketList 列表,检查是否有断开连接的 socket
for(int i = 0; i < socketList.count(); i++)
{
if(socketList.at(i)->state() == 0) // 如果 socket 状态为 0,表示已断开连接
{
socketList.removeAt(i); // 从列表中移除断开连接的 socket
}
}
// 再次遍历 socketList,读取每个客户端的可用数据
for(int i = 0; i < socketList.count(); i++)
{
if(socketList.at(i)->bytesAvailable() != 0) // 如果当前 socket 有可用数据
{
QByteArray msg = socketList.at(i)->readAll(); // 读取所有可用数据并存储在 msg 中
ui->listWidget->addItem(QString::fromLocal8Bit(msg)); // 将收到的消息转换为 QString 并添加到 listWidget 中显示
// 将接收到的消息发送给其他所有客户端(除了当前客户端)
for(int j = 0; j < socketList.count(); j++)
{
if(j != i) // 确保不向发送消息的客户端回发消息
{
socketList.at(j)->write(msg); // 将消息写入其他客户端的 socket
}
}
}
}
}
// 槽函数:点击 "启动服务器" 按钮时调用,启动服务器监听指定端口
void Widget::on_startBtn_clicked()
{
quint16 port = ui->portEdit->text().toUInt(); // 从用户界面的端口输入框中获取端口号,并转换为无符号整型
if(server->listen(QHostAddress::Any, port)) // 服务器开始监听指定端口上的所有地址
{
QMessageBox::information(this, "", "启动服务器成功!"); // 弹出信息框,提示服务器启动成功
}
else
{
QMessageBox::information(this, "", "启动服务器失败!"); // 弹出信息框,提示服务器启动失败
return; // 如果启动失败,直接返回,不执行后续代码
}
connect(server, &QTcpServer::newConnection, this, &Widget::newConnection_slot); // 连接服务器的新连接信号,处理新连接
}
客户端头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpSocket>
#include <QMessageBox>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_connectBtn_clicked();
void on_disconnectBtn_clicked();
void on_sendBtn_clicked();
public slots:
void connected_slot();
void readyRead_slot();
void disconnected_slot();
private:
Ui::Widget *ui;
QTcpSocket *socket;
QString userName;
};
#endif // WIDGET_H
客户端测试文件
#include "widget.h"
#include "ui_widget.h"
#include "widget.h"
#include "widget.h"
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
, socket(new QTcpSocket(this)) // 创建 QTcpSocket 对象,用于连接服务器
{
ui->setupUi(this);
ui->msgEdit->setEnabled(false); // 禁用消息输入框,直到连接成功
ui->sendBtn->setEnabled(false); // 禁用发送按钮,直到连接成功
ui->disconnectBtn->setEnabled(false); // 禁用断开连接按钮,直到连接成功
// 连接信号与槽
connect(socket, &QTcpSocket::connected, this, &Widget::connected_slot); // 当连接成功时,调用 connected_slot
connect(socket, &QTcpSocket::readyRead, this, &Widget::readyRead_slot); // 当有数据可读时,调用 readyRead_slot
connect(socket, &QTcpSocket::disconnected, this, &Widget::disconnected_slot); // 当断开连接时,调用 disconnected_slot
}
Widget::~Widget()
{
delete ui;
}
// 槽函数:处理服务器发送的数据
void Widget::readyRead_slot()
{
QByteArray msg = socket->readAll(); // 读取服务器发来的所有数据
ui->listWidget->addItem(QString::fromLocal8Bit(msg)); // 将收到的消息显示在 listWidget 中
}
// 槽函数:点击 "连接" 按钮时触发,连接到服务器
void Widget::on_connectBtn_clicked()
{
QString ip = ui->ipEdit->text(); // 获取用户输入的 IP 地址
quint16 port = ui->portEdit->text().toUInt(); // 获取用户输入的端口号并转换为整数
socket->connectToHost(ip, port); // 连接到服务器
}
// 槽函数:处理连接成功的情况
void Widget::connected_slot()
{
userName = ui->userEdit->text(); // 获取用户名
QString msg = userName + ":进入聊天室"; // 构建进入聊天室的提示消息
socket->write(msg.toLocal8Bit()); // 将消息发送到服务器
QMessageBox::information(this, "", "连接服务成功!"); // 显示连接成功的提示框
// 启用消息输入框、发送按钮和断开按钮
ui->msgEdit->setEnabled(true);
ui->sendBtn->setEnabled(true);
ui->disconnectBtn->setEnabled(true);
// 禁用用户名、IP 地址和端口号输入框,以及 "连接" 按钮
ui->userEdit->setEnabled(false);
ui->ipEdit->setEnabled(false);
ui->portEdit->setEnabled(false);
ui->connectBtn->setEnabled(false);
}
// 槽函数:点击 "发送" 按钮时触发,发送消息到服务器
void Widget::on_sendBtn_clicked()
{
QString msg = ui->msgEdit->text(); // 获取输入框中的消息
msg = userName + ": " + msg; // 将消息格式化为 "用户名: 消息"
// 创建新的 QListWidgetItem 对象,用于显示发送的消息
QListWidgetItem *item = new QListWidgetItem(msg);
// 设置消息为右对齐(自己发送的消息)
item->setTextAlignment(Qt::AlignRight);
ui->listWidget->addItem(item); // 将消息添加到 listWidget 中显示
socket->write(msg.toLocal8Bit()); // 将消息发送到服务器
ui->msgEdit->clear(); // 清空输入框
}
// 槽函数:点击 "断开" 按钮时触发,断开与服务器的连接
void Widget::on_disconnectBtn_clicked()
{
QString msg = userName + ": 离开聊天室"; // 构建离开聊天室的提示消息
socket->write(msg.toLocal8Bit()); // 将消息发送到服务器
socket->disconnectFromHost(); // 断开与服务器的连接
}
// 槽函数:处理断开连接的情况
void Widget::disconnected_slot()
{
// 禁用消息输入框、发送按钮和断开按钮
ui->msgEdit->setEnabled(false);
ui->sendBtn->setEnabled(false);
ui->disconnectBtn->setEnabled(false);
// 启用用户名、IP 地址和端口号输入框,以及 "连接" 按钮
ui->userEdit->setEnabled(true);
ui->ipEdit->setEnabled(true);
ui->portEdit->setEnabled(true);
ui->connectBtn->setEnabled(true);
}