使用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 <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include <QDebug> 
#include <QObject>
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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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 -= guiCONFIG += c++11TARGET = chat_server
TEMPLATE = appSOURCES += 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文件并编译,运行生成的可执行文件(它会一直监听来自客户端的请求)。然后再运行客户端的程序,期间不要关闭服务端的程序。如果有什么问题,欢迎私信我,可能还有些细节需要注意,不懂得找我就好了。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1556567.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Final Glory推出“荣耀勋章-神龙”,推动游戏叙事范式发展

GameFi赛道因基建设施的缺失而长期处于加密市场的边缘位置&#xff0c;该叙事在市场中的占有率正在下降。不过好的一面是&#xff0c;随着MetaArena引擎面向市场&#xff0c;正在推动区块链游戏市场的叙事向全新的范式发展。 MetaArena引擎是以零知识证明方案为基础的Web3游戏基…

实现一个计算器的功能(一般形式、函数指针数组的形式、回调函数的形式)

实现一个计算器的功能&#xff1a; 一般的形式&#xff1a; #include<stdio.h> int Add(int x, int y) {return x y; } int Sub(int x, int y) {return x - y; } int Mul(int x, int y) {return x * y; } int Div(int x, int y) {return x / y; } void menu() {printf…

Java中TreeMap,HashMap和LinkedHashMap的区别

先决条件&#xff1a;Java 中的 HashMap 和 TreeMap TreeMap、HashMap 和 LinkedHashMap&#xff1a;有什么相似之处&#xff1f; 所有类都提供键->值映射和遍历键的方法。这些类之间最重要的区别是时间保证和键的顺序。 HashMap、TreeMap 和LinkedHashMap三个类都实现了…

【数据结构】【队列】算法汇总

一、顺序队列【相当于一维数组】 1.准备工作 #define MAXQSIZE 100 typedef struct{QElemType*base;int front;int rear; }SqQueue; 2.队满&#xff0c;队空。入队&#xff0c;出队 二、链式队列 1.准备工作 typedef struct Qnode{ElemType data;struct Qnode*next; }Qnod…

Github优质项目推荐 - 第五期

文章目录 Github优质项目推荐 - 第五期一、【localsend】&#xff0c;47.5k stars - 附近设备文件互传二、【Pake】&#xff0c;29.9k stars - 网页变成桌面应用三、【laravel-crm】&#xff0c;10.7k stars - CRM 解决方案四、【localstack】&#xff0c;55.7k stars - 本地 A…

RabbitMQ(学习前言)

目录 学习MQ之前有必要先去温故下微服务知识体系&#xff0c;以加深本章节的理解 一、微服务间的通讯方式 1. 基本介绍 2. 同步通讯 2.1. 什么是同步通讯 2.2. 同步通讯存在的问题 问题一&#xff1a;耦合度高 问题二&#xff1a;性能和吞吐能力下降 问题三&#xff1a…

时序必读论文16|ICLR24 CARD:通道对齐鲁棒混合时序预测Transformer

论文标题&#xff1a;CARD: Channel Aligned Robust Blend Transformer for Time Series Forecasting 论文链接&#xff1a;https://arxiv.org/abs/2305.12095 代码链接&#xff1a;https://github.com/wxie9/CARD 前言 Transformer取得成功的一个关键因素是通道独立&#…

SpringBoot框架下旅游管理系统的创新设计与实现

第二章 相关技术简介 2.1 JAVA技术 本次系统开发采用的是面向对象的Java作为软件编程语言&#xff0c;Java表面上很像C&#xff0c;但是Java仅仅是继承了C的某些优点&#xff0c;程序员很少使用的C语言的特征在Java设计中去掉了。Java编程语言并没有什么结构&#xff0c;它把数…

Arduino UNO R3自学笔记21 之 Arduino电机的闭环控制(PID)

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;上篇写了电机速度测定&#xff0c;这篇主要是讲测定出的速度用于反馈&#xff0c;使得实际速度快速响应到需要的速度。 1.控制系统介绍 分2大类&#x…

​​​​​​​如何使用Immersity AI将图片转换成3D效果视频

随着技术的进步&#xff0c;图片处理变得越来越强大和直观。借助Immersity AI这样的工具&#xff0c;我们现在可以轻松地将平面图片转换成3D效果视频。以下是如何使用Immersity AI进行这一转换的详细步骤。 第一步&#xff1a;访问Immersity AI网站 首先&#xff0c;打开你的…

Spring开发最佳实践之跨域处理

1. 跨域处理 1.1 异常现象 1.2 异常原因分析 跨源资源共享的官方定义如下&#xff1a; 跨源资源共享&#xff08;CORS&#xff0c;Cross Origin Resource Sharing。或通俗地译为跨域资源共享&#xff09;是一种基于 HTTP 头的机制&#xff0c;该机制通过允许服务器标示除了它自…

在mac中通过ip连接打印机并实现双面打印

首先需要找到电脑自带的打印。添加打印机。 填写好打印机的ip地址&#xff0c;然后添加。 填写好ip地址后&#xff0c;直接添加就行 添加完打印机后其实就可以打印了。但是有些功能可能实现不了&#xff0c;比如说双面打印。为了实现双面打印的功能&#xff0c;需要再进行设置…

Vue83 引入elementUI

笔记 安装插件 安装按需引入插件 代码 ### App.vue <template><div><button>原生的按钮</button><input type"text"><atguigu-row><atguigu-button>默认按钮</atguigu-button><atguigu-button type"pr…

Arduino UNO R3自学笔记22 之 Arduino基础篇学习总结

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;目前将Arduino的大多数基础内容学习了&#xff0c;做个总结。 1.编程语言 学习单片机&#xff0c;在面向单片机编程时&#xff0c;语言是最基础的&#…

localhost与127.0.0.1傻傻分不清楚,区别详解来了

对应程序员来说&#xff0c;localhost和127.0.0.1经常使用&#xff0c;但是却又傻傻分不清楚&#xff01;尽管它们在实际应用中经常互换使用&#xff0c;但它们之间确实存在一些细微的差别。本文我将详细探讨localhost与127.0.0.1的区别。 一、定义与解析方式 localhost 定…

SSM外卖点餐软件APP-计算机毕业设计源码30768

目 录 摘要 1 绪论 1.1 研究背景 1.2研究目的 1.3论文结构与章节安排 2 外卖点餐软件APP系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能…

旅游管理自动化:SpringBoot系统设计与实现

第二章 相关技术简介 2.1 JAVA技术 本次系统开发采用的是面向对象的Java作为软件编程语言&#xff0c;Java表面上很像C&#xff0c;但是Java仅仅是继承了C的某些优点&#xff0c;程序员很少使用的C语言的特征在Java设计中去掉了。Java编程语言并没有什么结构&#xff0c;它把数…

【爬虫】网站反debugger、内存爆破以及网站限制开发者工具

【爬虫】网站反debugger、内存爆破以及网站直接限制开发者工具 声明&#xff1a;本文中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0…

就业市场需求分析:基于前程无忧岗位数据分析

背景介绍&#xff1a;在前程无忧网站&#xff0c;以"数据分析师""武汉"作为搜索关键词&#xff0c;爬取50页岗位数据合计980条。以该数据为基础&#xff0c;从岗位搜索匹配度、HR活跃度、不同区域/行业/企业的岗位数量和薪资分布等角度进行分析。 1、原始数…