使用C++结合Qt实现聊天室:QTcpSocket实现远程实时通信

既然是要实现远程实时通信,那么就需要用到网络协议。我们需要用到TCP/IP协议,不过Q提供了标准库QTcpSocket,我们只需要能够使用这个库就行了。这个标准库将远程连接通信功能封装的很好,详情可以查看QTcpSocket的文档,在Qt里面的帮助选项就可以查到。

该聊天室的功能也很简单,可以实现多个客户之间的实时通信。我们需要用到一个服务器,来监听客户端发出的消息,并将消息转发给所有的客户。服务器可以上阿里云或腾讯云去租用,没有服务器的话可以实现本地不同进程间的相互通信。

首先我们来编写服务端的逻辑代码。因为是运用的Qt,需要在服务器上安装好,我这里使用的是Qt5。同时还需要会在服务器上配置.pro文件,生成Makefile文件等,如果这些不会的话请参考我的另一篇文章在远程非桌面版Ubuntu中使用Qt5构建Hello World项目-CSDN博客

服务端完整代码

基本的Qt知识我不介绍,仅介绍sender()函数,以下程序我创建了ChatServer类。

  • sender(): 这是一个 Qt 的宏或函数(取决于具体实现),它返回当前正在处理信号的对象指针。当一个槽函数被调用时,sender() 会返回触发该槽函数的信号的发送者对象。

  • qobject_cast<QTcpSocket*>: qobject_cast 是 Qt 提供的一个类型转换函数,用于在 Qt 的对象模型中进行安全的向下类型转换。它类似于 C++ 的 dynamic_cast,但在 Qt 对象模型中更加安全和高效。qobject_cast 用于将一个 QObject 指针(或其派生类的指针)转换为另一个派生类的指针,这里转换成QTcpSocket指针。

    #include
    #include
    #include
    #include
    #include
    #include
    class ChatServer : public QObject
    {
    Q_OBJECT
    public:
    explicit ChatServer(QObject *parent = nullptr);

    private slots:
    void newConnection();
    void readyRead();

    private:
    //QTcpSoket对象,代表服务端
    QTcpServer *server;

    复制代码
      //QTcpSoket类型的数组,保存的是客户端的对象
      QList<QTcpSocket*> clients;

    };

    ChatServer::ChatServer(QObject *parent) : QObject(parent)
    {
    server = new QTcpServer(this);

    复制代码
      //&ChatServer::newConnection,这是我们自己写的成员函数
      connect(server, &QTcpServer::newConnection, this, &ChatServer::newConnection);
    
      //允许来自任何IP的主机连接,监听1234端口
      if (server->listen(QHostAddress::Any, 1234)) {
          qDebug() << "Server started on port 1234";
      } else {
          qDebug() << "Server could not start";
      }

    }

    void ChatServer::newConnection()
    {
    //将客户端发送请求的QTcpSoket对象添加到数组
    QTcpSocket *client = server->nextPendingConnection();
    clients.append(client);
    connect(client, &QTcpSocket::readyRead, this, &ChatServer::readyRead);
    }

    void ChatServer::readyRead()
    {
    QTcpSocket client = qobject_cast<QTcpSocket>(sender());
    if (client) {
    while (client->canReadLine()) {
    QString message = QString::fromUtf8(client->readLine()).trimmed();
    foreach (QTcpSocket *otherClient, clients) {
    //if (otherClient != client){
    otherClient->write(message.toUtf8());
    otherClient->write("\n");
    //}
    }
    }
    }
    }

    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);

    复制代码
      ChatServer server;
    
      return a.exec();

    }

    #include "main.moc"

  • void ChatServer::readyRead(): 这是 ChatServer 类中的一个槽函数,当客户端发送数据并且数据到达服务器时,readyRead 信号会被触发,从而调用这个槽函数。

  • QTcpSocket *client = qobject_cast<QTcpSocket*>(sender());: 这里使用 sender() 获取发送信号的对象指针,并将其转换为 QTcpSocket 类型的指针。sender() 返回的是触发槽函数的对象,通常是发出 readyRead 信号的 QTcpSocket 对象。

  • if (client) { ... }: 检查 client 是否有效(即不为 nullptr)。如果转换成功,则继续执行后续代码。

  • while (client->canReadLine()) { ... }: 这个循环会一直运行,直到从客户端读取完所有可用数据行。canReadLine() 方法检查当前是否有完整的一行数据可读。

  • QString message = QString::fromUtf8(client->readLine()).trimmed();: 这行代码从 client 中读取一行数据,并将其转换为 QString 类型。trimmed() 方法用于去除字符串两端的空白字符(如空格、换行符等)。

  • foreach (QTcpSocket *otherClient, clients) { ... }: 这是一个基于范围的 for 循环,遍历 clients 列表中的所有 QTcpSocket 对象。clients 是一个包含所有连接到服务器的客户端套接字对象的列表。

  • if (otherClient != client) { ... }: 检查当前客户端是否是发送消息的客户端。如果是,则不发送消息给自己。(注意这一段代码被我注释掉了,这样会将消息发给自己

  • otherClient->write(message.toUtf8());: 将消息以 UTF-8 编码方式写回到 otherClient 中。

  • otherClient->write("\n");: 写入一个换行符,以确保每个消息单独成行。

客户端完整代码

代码需要分成mainwindow.h,mainwindow.cpp和main.cpp三个文件。同时需要自己在界面文件创建一个名为sendButton的QPushButton的按钮,一个名为messageLineEdit的QLineEdit的文本输入框,一个名为ChatTextEdit的QTextEdit聊天框。

mainwindow.h
复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QHostAddress>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void on_sendButton_clicked();
    void readyRead();

private:
    Ui::MainWindow *ui;
    QTcpSocket *socket;
};

#endif // MAINWINDOW_H
mainwindow.cpp
复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 初始化 TCP 套接字
    socket = new QTcpSocket(this);
    //改为自己的服务器主机名
    QString host = '114.132.169.5';
    socket->connectToHost(QHostAddress(host), 1234); 

    // 连接数据读取信号
    connect(socket, &QTcpSocket::readyRead, this, &MainWindow::readyRead);

    // 连接发送按钮点击信号
    connect(ui->sendButton, &QPushButton::clicked, this, &MainWindow::on_sendButton_clicked);
}

MainWindow::~MainWindow()
{
    delete ui;
    delete socket;
}

void MainWindow::on_sendButton_clicked()
{
    // 获取输入的文本
    QString message = ui->messageLineEdit->text();

    // 发送消息到服务器
    socket->write(message.toUtf8());
    socket->write("\n"); // 发送一个换行符表示消息结束

    // 清空输入框
    ui->messageLineEdit->clear();
}

void MainWindow::readyRead()
{
    // 读取服务器发送的消息
    while (socket->canReadLine()) {
        QString message = QString::fromUtf8(socket->readLine()).trimmed();
        // 将消息添加到聊天记录
        ui->chatTextEdit->append(message);
    }
}
main.cpp
复制代码
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
手动创建 .pro 文件

创建一个名为 chat_server.pro 的文件,并在其中添加以下内容:

复制代码
QT += core
QT -= gui

CONFIG += c++11

TARGET = chat_server
TEMPLATE = app

SOURCES += main.cpp

这个 .pro 文件配置了项目的基本设置:

  • 使用 Qt Core 模块。
  • 不使用 Qt GUI 模块。
  • 启用 C++11 特性。
  • 目标文件名为 chat_server
  • 项目模板为应用程序。
  • 包含一个源文件 main.cpp

在项目目录中运行以下命令来生成 Makefile:

复制代码
qmake

qmake 会读取 .pro 文件并生成相应的 Makefile

使用 make 命令编译项目:

复制代码
make

这将生成可执行文件 chat_server 在当前目录中。

使用 ./ 运算符运行生成的可执行文件:

复制代码
./chat_server

服务端和客户端都部署完了之后就可以开始测试运行了,先运行服务端的程序,手动创建.pro文件并编译,运行生成的可执行文件(它会一直监听来自客户端的请求)。然后再运行客户端的程序,期间不要关闭服务端的程序。如果有什么问题,欢迎私信我,可能还有些细节需要注意,不懂得找我就好了。

相关推荐
用户8055336980311 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner11 小时前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz5 天前
QML Hello World 入门示例
qt
xcyxiner8 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner9 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner9 天前
DicomViewer (添加模型类)3
qt
xcyxiner10 天前
DicomViewer (目录调整) 2
qt
xcyxiner10 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00612 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术12 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript