服务段头文件
#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_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
服务段测试文件
#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 列表,检查是否有断开连接的 socketfor(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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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_slotconnect(socket, &QTcpSocket::readyRead, this, &Widget::readyRead_slot); // 当有数据可读时,调用 readyRead_slotconnect(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);
}