使用QT编写有图形界面的TCP局域网聊天室(app)

server/2024/9/23 8:14:57/

服务器:

实现方法:

1.使用QTcpServer类实例化一个对象,就得到了一个服务器端
2.调用该类对象的成员函数 listen 将服务器启动监听,该函数会进行绑定ip和端口号
    ip地址可以指定也可以由系统自动绑定,端口号也可以自己指定和由系统自动指定
3.当有客户端发来连接请求后,该服务器就会自动发射一个newConnection的信号
    我们可以将该信号绑定到自定义的槽函数中完成相关逻辑
4.可以使用类中的成员函数 nextPenddingConnection 可以获取最新连接的客户端套接字
5.可以使用该客户端套接字进行数据收发
    read、readLine、readAll读取数据
    write发送数据
6.当服务器收到客户端的消息后,该服务器会自动发射一个readyRead的信号
    可以将该信号连接到对应的槽函数中,处理客户端发来的消息
7.调用成员函数close关闭监听

头文件:

// 防止头文件被重复包含
#ifndef WIDGET_H
#define WIDGET_H// 包含必要的Qt类库
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include <QMessageBox>
#include <QDebug>// 开始Qt命名空间,以使用Qt的类和函数
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE// Widget类,继承自QWidget,实现一个基于TCP的服务器
class Widget : public QWidget
{Q_OBJECTpublic:// 构造函数,初始化服务器Widget(QWidget *parent = nullptr);// 析构函数,释放资源~Widget();private slots:// 当“启动服务器”按钮被点击时的槽函数void on_startSerBtn_clicked();// 当有新的客户端连接时的槽函数void newConnection_slot();// 当客户端有数据可读时的槽函数void readyRead_slot();private:// Ui::Widget是用于管理界面组件的类Ui::Widget *ui;// QTcpServer用于监听客户端的连接请求QTcpServer *server;// 保存所有已连接客户端的socket列表QList <QTcpSocket*> socketList;};
#endif // WIDGET_H

主程序:
 

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 实例化一个服务器对象server = new QTcpServer(this); // 在堆区申请一个服务器
}Widget::~Widget()
{delete ui;
}// 启动服务器按钮对应的槽函数
void Widget::on_startSerBtn_clicked()
{if (ui->startSerBtn->text() == "启动服务器"){// 执行启动服务器的的动作// 获取ui界面上的端口号quint16 port = ui->portEdit->text().toUInt();// 启动监听: bool listen(const QHostAddress &address=QHostAddress::Any,quint16 port =0);if (!server->listen(QHostAddress::Any, port)){QMessageBox::critical(this, "错误", "服务器启动失败");return;}// 程序执行至此,表示服务器启动成功QMessageBox::information(this, "成功", "服务器启动成功");// 将行编辑器设置为不可用ui->portEdit->setEnabled(false);// 将按钮文本内容设置为关闭服务器ui->startSerBtn->setText("关闭服务器");// 此时,如果有客户端发来连接请求,那么该服务器就会自动发送一个newConnection的信号// 我们可以将该信号连接到自定义的槽函数中,处理后续操作connect(server, &QTcpServer::newConnection, this, &Widget::newConnection_slot);}else{// 执行关闭服务器的动作// 将行编辑器设置成可用状态ui->portEdit->setEnabled(true);// 将按钮文本内容设置为启动服务器ui->startSerBtn->setText("启动服务器");}
}// 自定义处理newConnection信号的槽函数的实现
void Widget::newConnection_slot()
{qDebug() << "有新的客户端发来连接请求了";// 可以通过成员函数 nextPendingConnection函数获取最新连接的客户端套接字的地址// 函数原型:QTcpSocket *newPendingConnection();// 无参函数// 返回值:最新的一个连接的套接字地址QTcpSocket *s = server->nextPendingConnection();// 将该套接字放入客户端链表中socketList.push_back(s);// 程序执行至此,一个服务器可以对应多个客户端,已经建立了连接// 此时,如果有某个客户端发来数据,那么该客户端套接字就会自动发送一个readyRead的信号// 我们可以将该信号连接到自定义的槽函数中处理相关逻辑connect(s, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);
}// 自定义处理readyRead信号的槽函数的实现
void Widget::readyRead_slot()
{// 1.遍历链表中所有的客户端,如果客户端的状态为未连接,则直接从链表中移除for (int i = 0; i < socketList.size(); i++){// 判断当前套接字 socketList[i]是否时效// 函数原型:SockState state() const;// 功能:返回当前套接字状态// 返回值为0时,表示改套接字时未连接状态if (socketList[i]->state() == 0){// 将改套接字移除出链表socketList.removeAt(i);}}for (int i = 0; i < socketList.count(); i++){if (socketList[i]->bytesAvailable() != 0){// 读取当前套接字内容QByteArray sjrhsk = socketList[i]->readAll();// 显示到ui界面上ui->msglistWidget->addItem(QString::fromLocal8Bit(sjrhsk));// 将消息发送给其他客户端// 将收到的消息,全部发给其他客户端for (int j = 0; j < socketList.length(); j++){if (i != j) // 防止自己发给自己{socketList[j]->write(sjrhsk);}}}}
}

客户端:

客户端:
1.使用该类实例化一个对象,就创建了一个客户端
2.使用该类对象的成员函数 connectToHost 向服务器发送连接请求
    如果连接服务器成功,那么该客户端套接字就会自动发射一个 connected 的信号
    可以将该信号连接到自定义的槽函数中处理相关逻辑
3.可以使用该客户端套接字进行数据收发
    read、readLine、readAll读取数据
    write发送数据
4.如果客户端收到服务器发来的消息后,该客户端就会自动发射一个readyRead的信号
可以将该信号连接到对应的槽函数中,处理客户端发来的消息
5.调用成员函数 disconnectFromHost 断开跟服务器的连接

头文件:

// 确保头文件Widget.h只被包含一次
#ifndef WIDGET_H
#define WIDGET_H// 包含必要的Qt库文件
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include <QString>
#include <QMessageBox>
#include <QDebug>// 开始使用Qt命名空间,以避免全局命名冲突
QT_BEGIN_NAMESPACE
namespace Ui
{class Widget;
}
QT_END_NAMESPACE// Widget类,继承自QWidget,实现了网络通信功能
class Widget : public QWidget
{Q_OBJECTpublic:// 构造函数,初始化Widget对象Widget(QWidget *parent = nullptr);// 析构函数,释放Widget对象占用的资源~Widget();private slots:// 当连接按钮被点击时的槽函数void on_connectbtn_clicked();// 当发送按钮被点击时的槽函数void on_sendbtn_clicked();// 连接成功时的槽函数void connected_slot();// 准备读取数据时的槽函数void readyread_slot();// 断开连接时的槽函数void disconnect_slot();private:// 用于设置UI界面Ui::Widget *ui;// TCP客户端套接字QTcpSocket *client;// 用户名QString username;
};#endif // WIDGET_H

主程序:

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 实例化客户端对象client = new QTcpSocket(this);connect(client, &QTcpSocket::readyRead, this, &Widget::readyread_slot);connect(client, &QTcpSocket::connected, this, &Widget::connected_slot);connect(client, &QTcpSocket::disconnected, this, &Widget::disconnect_slot);
}Widget::~Widget()
{delete ui;
}void Widget::on_connectbtn_clicked()
{if (ui->connectbtn->text() == "连接服务器"){QString ip = ui->ipedit->text();              // IP地址quint16 port = ui->portedit->text().toUInt(); // 端口号username = ui->nameedit->text();              // 用户名// 调用套接字成员函数,连接服务器// 函数原型:void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);// 参数1:要被连接的服务器ip地址// 参数2:服务器的端口号// 参数3:默认为可读可写// 返回值:无client->connectToHost(ip, port);ui->connectbtn->setText("断开连接");}else{// 执行断开连接的操作// 准备发送消息给服务器QString msg = username + ": 离开聊天室";client->write(msg.toLocal8Bit());// 断开连接client->disconnectFromHost();ui->connectbtn->setText("连接服务器");}
}void Widget::on_sendbtn_clicked()
{// 组织要发送的消息QString msg = username + ":" + ui->messageedit->toPlainText();// 将消息发送给服务器client->write(msg.toLocal8Bit());// 将消息展示到自己界面上// 准备一个QListWidgetItem类的对象QListWidgetItem *sjrhsk = new QListWidgetItem(msg);sjrhsk->setTextAlignment(Qt::AlignRight); // 将文本右对齐ui->msgListWidget->addItem(sjrhsk);// 清空消息发送框的内容// ui->msgEdit->clear();
}
// 处理connected信号的槽函数的定义
void Widget::connected_slot()
{QMessageBox::information(this, "成功", "连接成功");// 将相关组件禁用ui->ipedit->setEnabled(false);ui->nameedit->setEnabled(false);ui->portedit->setEnabled(false);// 向服务器发送一条消息QString msg = username + ": 进入聊天室";client->write(msg.toLocal8Bit());
}
// 自定义处理readyRead信号的槽函数
void Widget::readyread_slot()
{// 从套接字中读取数据QByteArray msg = client->readAll();// 将读取下来的数据展示到ui界面上ui->msgListWidget->addItem(QString::fromLocal8Bit(msg));
}
// 自定义处理disconnect信号的槽函数的定义
void Widget::disconnect_slot()
{QMessageBox::information(this, "提示", "成功断开与服务器的连接");// 将相关组件启用ui->ipedit->setEnabled(true);ui->nameedit->setEnabled(true);ui->portedit->setEnabled(true);
}


http://www.ppmy.cn/server/117524.html

相关文章

Vue 中常用的基础指令

一. 什么是 Vue 指令 指令的定义和作用 指令是通过 Vue 实例的directives选项进行定义的。在指令的定义中&#xff0c;需要提供一个bind函数&#xff0c;它在指令第一次绑定到元素时被调用&#xff0c;可以执行一些初始化的操作。还可以提供update函数&#xff0c;它在指令所…

PHP函数如何传递数组参数

php 函数可以使用数组参数传递大量数据。语法&#xff1a;参数类型前加上方括号 ([])。例如&#xff1a;myfunction(array $arr)。实战案例&#xff1a;计算数组元素平均值。注意&#xff1a;数组参数默认为引用传递&#xff0c;类型提示可提高代码可读性&#xff0c;数组解构可…

【网络安全】-文件上传漏洞

文件操作漏洞包括文件上传漏洞&#xff0c;文件包含漏洞&#xff0c;文件下载漏洞。 文章目录 前言 什么是文件上传漏洞&#xff1f; 文件上传的验证与绕过&#xff1a; 1.前端js验证&#xff1a;   Microsft Edge浏览器&#xff1a; Google Chrome浏览器&#xff1a; 2.后端…

el-table表格的展开行,初始化的时候展开哪一行+设置点击行可展开功能

效果&#xff1a; 表格展开行官网使用&#xff1a; 通过设置 type"expand" 和 Scoped slot 可以开启展开行功能&#xff0c;el-table-column 的模板会被渲染成为展开行的内容&#xff0c;展开行可访问的属性与使用自定义列模板时的 Scoped slot 相同。 但是这种方法…

MyBatis中多对一关系的三种处理方法

目录 MyBatis中多对一关系的三种处理方法 1.通过级联属性赋值 1&#xff09;mapper 2&#xff09;mapper.xml 3&#xff09;测试代码 4&#xff09;测试结果 2.通过标签 1&#xff09;mapper 2&#xff09;mapper.xml 3&#xff09;测试代码 4&#xff09;测试结果 3.分步查询 …

PPT技巧:如何在幻灯片中生成目录?

PPT文件如何制作目录&#xff0c;如何点击目录标题立即跳转到相应幻灯片&#xff1f;今天小奥超人和大家一起来学习一下。 现在幻灯片里制作好目录页&#xff0c;制作好目录之后&#xff0c;选中一个目录&#xff0c;点击插入 – 链接 在插入链接界面中&#xff0c;选择【本文…

一个用于翻译 CSV 文件的 Python 脚本,适用于将英文内容批量翻译成中文(或其他语言),并解决文件编码导致的中文乱码和无法翻译的问题。

将CSV文件中的英文批量翻译成中文的 Python 脚本 一个用于翻译 CSV 文件的 Python 脚本&#xff0c;适用于将英文内容批量翻译成中文&#xff08;或其他语言&#xff09;&#xff0c;并解决文件编码导致的中文乱码和无法翻译的问题。 主要功能&#xff1a; 文件编码转换&…

pycharm调试知识, 线程进程与深度学习

pycharm调试&#xff0c; 在调试&#xff08;debugging&#xff09;过程中&#xff0c;Step Over 是一个常见的操作&#xff0c;用来控制代码的执行方式。它的意思是让调试器执行当前行的代码&#xff0c;但是如果这行代码中有函数调用&#xff0c;调试器不会进入该函数的内部…