首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Qt项目网络聊天室设计

Qt项目网络聊天室设计

作者头像
DeROy
修改于 2020-08-25 15:54:50
修改于 2020-08-25 15:54:50
2.6K01
举报
文章被收录于专栏:编程学习基地编程学习基地
运行总次数:1

网络聊天室

Qt网络聊天室服务端

网络聊天室程序

  1. 基于TCP的可靠连接(QTcpServer、QTcpSocket)
  2. 一个服务器,多个客户端

3. 服务器接收到某个客户端的请求以及发送信息,经服务器发给其它客户端 最终实现一个共享聊天内容的聊天室!

QTcpServer

提供一个TCP基础服务类 继承自QObject,这个类用来接收到来的TCP连接,可以指定TCP端口或者用QTcpServer自己挑选一个端口,可以监听一个指定的地址或者所有的机器地址。

QTcpServer的信号:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
newConnection()//有新连接连接时触发该信号

配置

pro文件添加

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
QT += network

获取当前设备所有ip地址

枚举设备所有ip地址

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
QList<QHostAddress> ipList = QNetworkInterface::allAddresses();
QStringList addressStrList;
addressStrList.clear();

for(int index = 0;index<ipList.size();index++)
{
    if(ipList.at(index).isNull())   continue;         //如果地址为空,则去掉
    QAbstractSocket::NetworkLayerProtocol protocol = ipList.at(index).protocol();
    if(protocol != QAbstractSocket::IPv4Protocol)   continue;           //只取IPV4的地址
    addressStrList.append(ipList.at(index).toString());
}
ui->comboBox_Address->addItems(addressStrList);

listen() close()

调用listen()来监听所有的连接 调用close()来关闭套接字,停止对连接的监听。

如果监听有错误,serverError()返回错误的类型。调用errorString()来把错误打印出来

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool isListening()	const

当服务端正在监听连接时候返回真,否则返回假

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
QString serverAddressStr = ui->comboBox_Address->currentText();     //获取服务器ip地址
quint16 port = ui->lineEdit_port->text().toInt();                   //获取服务器端口
QHostAddress serverAddress = QHostAddress(serverAddressStr);        //初始化协议族

if(mServer->isListening())
{
    //在监听状态 取消监听
    mServer->close();
}
else
{
    //不在监听状态 开始监听
    if(mServer->listen(serverAddress, port))
    {
        //监听成功
        qDebug() << "Listen Ok!!";
    }
    else
    {
        //监听失败
        QMessageBox::warning(this, "Tcp Server Listen Error", mServer->errorString());
    }
}

当监听连接时候,可以调用serverAddress()serverPort()来返回服务端的地址和端口。

newConnection()

每当一个newConnection新的客户端连接到服务端就会发射信号newConnection()

调用nextPendingConnection()来接受待处理的连接。返回一个连接的QTcpSocket(),我们可以用这个返回的套接字和客户端进行连接

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private slots:
    void newConnectionSlot();           //新连接
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//处理新连接客户端
connect(mServer, SIGNAL(newConnection()),this, SLOT(newConnectionSlot()));
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void Widget::newConnectionSlot()
{
    mClient = mServer->nextPendingConnection();
    connect(mClient, SIGNAL(readyRead()),this, SLOT(readyReadSlot()));//接收消息
    connect(mClient, SIGNAL(disconnected()),this, SLOT(disconnectedSlot()));//断开连接
}

SINGL

readyRead()

客户端有数据发送至服务端时触发该信号

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
connect(mClient, SIGNAL(readyRead()),this, SLOT(readyReadSlot()));          //接收消息
isReadable

是否可读

readAll

读取客户端发送过来的全部信息

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if(mClient->isReadable())
{
	QByteArray recvArray = mClient->readAll();
}
disconnected()

当客户端断开连接时触发该信号

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
connect(mClient, SIGNAL(disconnected()),this, SLOT(disconnectedSlot()));    //断开连接

UI设计

服务端UI设计

TcpServer项目训练

widget.h

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QNetworkInterface>
#include <QAbstractSocket>
#include <QTcpServer>
#include <QTcpSocket>
#include <QMessageBox>
#include <QDateTime>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();
private slots:
    void on_pushButton_listen_clicked();//开始监听
    void newConnectionSlot();           //新连接
    void disconnectedSlot();            //断开连接
    void readyReadSlot();               //接收消息的槽函数
    void on_pushButton_send_clicked();	//发送消息

private:
    void enumIpAddress();				//枚举当前设备所有端口
private:
    Ui::Widget *ui;

    QTcpServer *mServer;				//服务器
    QTcpSocket *mClient = nullptr;		//客户端
    QList<QTcpSocket *>mClientList;		//客户端链表
};

#endif // WIDGET_H

widget.cpp

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    mServer = new QTcpServer;
    enumIpAddress();

    //处理新连接客户端
    connect(mServer, SIGNAL(newConnection()),this, SLOT(newConnectionSlot()));
}

Widget::~Widget()
{
    delete ui;
}
void Widget::enumIpAddress()
{
    QList<QHostAddress> ipList = QNetworkInterface::allAddresses();
    QStringList addressStrList;
    addressStrList.clear();

    for(int index = 0;index<ipList.size();index++)
    {
        if(ipList.at(index).isNull())   continue;         //如果地址为空,则去掉
        QAbstractSocket::NetworkLayerProtocol protocol = ipList.at(index).protocol();
        if(protocol != QAbstractSocket::IPv4Protocol)   continue;           //只取IPV4的地址
        addressStrList.append(ipList.at(index).toString());
    }
    ui->comboBox_Address->addItems(addressStrList);
}

void Widget::on_pushButton_listen_clicked()
{
    QString serverAddressStr = ui->comboBox_Address->currentText();     //获取服务器ip地址
    quint16 port = ui->lineEdit_port->text().toInt();                   //获取服务器端口
    QHostAddress serverAddress = QHostAddress(serverAddressStr);        //初始化协议族

    if(mServer->isListening())
    {
        //在监听状态 取消监听
        mServer->close();
        ui->pushButton_listen->setText("监听");
    }
    else
    {
        //不在监听状态 开始监听
        if(mServer->listen(serverAddress, port))
        {
            //监听成功
            qDebug() << "Listen Ok!!";
            ui->pushButton_listen->setText("停止监听");
        }
        else
        {
            //监听失败
            QMessageBox::warning(this, "Tcp Server Listen Error", mServer->errorString());
        }
    }
}

void Widget::newConnectionSlot()
{
    QString clientInfo;

    mClient = mServer->nextPendingConnection();
    mClientList.append(mClient);
    //窥视Client 信息
    clientInfo = mClient->peerAddress().toString() + ":"+  QString::number(mClient->peerPort());
    ui->listWidget_client->addItem(clientInfo);

    connect(mClient, SIGNAL(readyRead()),this, SLOT(readyReadSlot()));          //接收消息

    connect(mClient, SIGNAL(disconnected()),this, SLOT(disconnectedSlot()));    //断开连接
}

void Widget::readyReadSlot()
{
    QByteArray recvArray;
    QTcpSocket* current = nullptr;
    if(!mClientList.isEmpty())
    {
        //接收客户端数据
        for(int index = 0;index < mClientList.count();index ++)
        {
            current = mClientList.at(index);

            if(current->isReadable())
            {
                recvArray = current->readAll();
                if(recvArray.isEmpty()) continue;
                QString str = QString(QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss ddd")) +
                        ":Recv\n" + str.fromLocal8Bit(recvArray.data());    //本地GBK转Unicode 解决乱码
                ui->textBrowser_recv->append(str);
                break;
            }
        }
        //转发给其他客户端
        for(int index = 0;index < mClientList.count();index ++)
        {
            QTcpSocket* temp = mClientList.at(index);
            if(current == temp) continue;
            if(temp->isWritable())
            {
                temp->write(recvArray);
            }
        }
    }
}

void Widget::disconnectedSlot()
{
    QMessageBox::information(this, "client close Signal", "client over");
}

void Widget::on_pushButton_send_clicked()
{
    QString sendString = ui->plainTextEdit_send->toPlainText();
    QByteArray sendArr = sendString.toLocal8Bit();

    //群发给所有客户端连接
    if(!mClientList.isEmpty())
    {
        for(int index = 0;index < mClientList.count();index ++)
        {
            QTcpSocket* temp = mClientList.at(index);
            if(temp->isWritable())
            {
                temp->write(sendArr);
            }
        }
    }
    QString str = QString(QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss ddd"))
            + ":Send\n" + sendString;
    ui->textBrowser_recv->append(str);          //本地GBK转Unicode 解决乱码
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程学习基地 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
「Android音视频编码那点破事」第四章,使用MediaCodec实现H264编码
  说到Android的视频硬编码,很多新人首先会想到MediaRecorder,这可以说是Android早期版本视频硬编码的唯一选择。这个类的使用很简单,只需要给定一个Surface(输入)和一个File(输出),它就给你生成一个标准的mp4文件。   但越是简单的东西便意味着越难以控制,MediaRecorder的缺点很明显。相信很多人在接触到断点视频录制这个需求的时候,首先会想到使用MediaRecorder,很遗憾,这个东西并不能给你很多期待,就像一开始的我一样。   首先,MediaRecorder并没有断点录制的API,当然你可以使用一些“小技巧”,每次录制的时候,都把MediaRecorder stop掉,然后再次初始化,这样就会生成一系列的视频,最后把它们拼接起来。然而问题在于,每次初始化MediaRecorder都需要消耗很长时间,这意味着,当用户快速点击录制按钮的时候可能会出现问题。对于这个问题,你可以等到MediaRecorder初始化完成才让用户点击开始录制,但是这样往往会因为等待时间过长,导致用户体验极差。   这种情况下,一个可控的视频编码器是必须的。虽然在Android 4.4以前我们没得选择,但是在Android 4.4之后,我们有了MediaCodec,一个完全可控的视频编码器,虽然无法直接输出mp4(需要配合MediaMuxer来对音视频进行混合,最终输出mp4,或者其它封装格式)。如今的Android生态,大部分手机都已经是Android 5.0系统,完全可以使用MediaCodec来进行音视频编码的开发,而MediaRecorder则降级作为一个提高兼容性的备选方案。   废话不多说,我们直接步入正题。要想正确的使用MediaCodec,我们首先得先了解它的工作流程,关于这个,强烈大家去看一下Android文档。呃呃,相信在这个快速开发为王道的环境,没几个人会去看,所以还是在这里简单介绍一下。
阿利民
2022/05/16
1K0
「Android音视频编码那点破事」第四章,使用MediaCodec实现H264编码
Android原生编解码接口 MediaCodec 之——踩坑
MediaCodec 有两种方式触发输出关键帧,一是由配置时设置的 KEY_FRAME_RATE 和KEY_I_FRAME_INTERVAL参数自动触发,二是运行过程中通过 setParameters 手动触发输出关键帧。
全栈程序员站长
2022/11/01
7.1K1
Android原生编解码接口 MediaCodec 之——踩坑
一文掌握直播技术:实时音视频采集、编码、传输与播放
从游戏、教育、电商到娱乐,直播技术的应用场景无处不在。随着移动端的网速越来越快,直播技术的普及和发展将更加迅速。
陆业聪
2024/08/19
1.8K0
一文掌握直播技术:实时音视频采集、编码、传输与播放
Android PC投屏简单尝试(录屏直播)2—硬解章(MediaCodec+RMTP)
代码地址 :https://github.com/deepsadness/MediaProjectionDemo
deep_sadness
2018/12/10
3.1K0
Android PC投屏简单尝试(录屏直播)2—硬解章(MediaCodec+RMTP)
Android MediaCodec 硬编码 H264 文件
在 Android 4.1 版本提供了 MediaCodec 接口来访问设备的编解码器,不同于 FFmpeg 的软件编解码,它采用的是硬件编解码能力,因此在速度上会比软解更具有优势,但是由于 Android 的碎片化问题,机型众多,版本各异,导致 MediaCodec 在机型兼容性上需要花精力去适配,并且编解码流程不可控,全交由厂商的底层硬件去实现,最终得到的视频质量不一定很理想。
音视频开发进阶
2019/11/18
3.7K0
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】六、Android音视频硬编码:生成一个MP4
在【音视频硬解码流程:封装基础解码框架】这篇文章中,介绍了如何使用Android原生提供的硬编解码工具MediaCodec,对视频进行解码。同时,MediaCodec也可以实现对音视频的硬编码。
开发的猫
2020/04/02
2.4K1
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】六、Android音视频硬编码:生成一个MP4
干货 | 移动端使用OpenGL转场特效的音视频合成应用
近年来短视频的火爆,让内容创作类的APP获得了巨大的流量。用户通过这类工具编辑自己的短视频,添加各式各样的炫酷特效,从而呈现出更加丰富多彩的视频内容。本文将会介绍如何使用移动端原生API,将图片添加转场特效并且最终合成为视频的基本流程。
携程技术
2023/11/11
8660
干货 | 移动端使用OpenGL转场特效的音视频合成应用
Android AVDemo(8):视频编码,H.264 和 H.265 都支持丨音视频工程示例
iOS/Android 客户端开发同学如果想要开始学习音视频开发,最丝滑的方式是对音视频基础概念知识有一定了解后,再借助 iOS/Android 平台的音视频能力上手去实践音视频的采集 → 编码 → 封装 → 解封装 → 解码 → 渲染过程,并借助音视频工具来分析和理解对应的音视频数据。
关键帧
2022/06/13
1.3K0
Android AVDemo(8):视频编码,H.264 和 H.265 都支持丨音视频工程示例
短视频源码开发,Android端短视频功能的快速实现
一、短视频内容生产 优质短视频内容的产生依赖于短视频的采集和特效编辑,这就要求在进行短视频源码开发时,用到基础的美颜、混音、滤镜、变速、图片视频混剪、字幕等功能,在这些功能基础上,进行预处理,结合OpenGL、AI、AR技术,产生很多有趣的动态贴纸玩法,使得短视频内容更具创意。
云豹科技程序员
2021/06/01
1.3K0
手写 Android 录屏直播
观看手游直播时,我们观众端看到的是选手的屏幕上的内容,这是如何实现的呢?这篇博客将手写一个录屏直播 Demo,实现类似手游直播的效果。
字节流动
2021/06/09
2.2K0
手写 Android 录屏直播
Android平台硬件编码总结一
MediaCodec编码的套路 状态 来自官网的状态图 codec_status.png 创建 find Support Codec 通过这种方式可以得到当前设备所有的MedaCodecInfo
deep_sadness
2018/08/30
2.8K0
Android平台硬件编码总结一
刷抖音上瘾后,决定探究如果做一款类似抖音短视频app
优质短视频内容的产生依赖于短视频的采集和特效编辑,这就要求在进行抖音APP开发时,用到基础的美颜、混音、滤镜、变速、图片视频混剪、字幕等功能,在这些功能基础上,进行预处理,结合OpenGL、AI、AR技术,产生很多有趣的动态贴纸玩法,使得短视频内容更具创意。
Android技术干货分享
2019/06/17
2.7K2
Android AVDemo(9):视频封装,采集编码 H.264/H.265 并封装 MP4丨音视频工程示例
iOS/Android 客户端开发同学如果想要开始学习音视频开发,最丝滑的方式是对音视频基础概念知识有一定了解后,再借助 iOS/Android 平台的音视频能力上手去实践音视频的采集 → 编码 → 封装 → 解封装 → 解码 → 渲染过程,并借助音视频工具来分析和理解对应的音视频数据。
关键帧
2022/06/13
7060
Android AVDemo(9):视频封装,采集编码 H.264/H.265 并封装 MP4丨音视频工程示例
Android AVDemo(2):音频编码,采集 PCM 数据编码为 AAC丨音视频工程示例
iOS/Android 客户端开发同学如果想要开始学习音视频开发,最丝滑的方式是对音视频基础概念知识有一定了解后,再借助 iOS/Android 平台的音视频能力上手去实践音视频的采集 → 编码 → 封装 → 解封装 → 解码 → 渲染过程,并借助音视频工具来分析和理解对应的音视频数据。
关键帧
2022/06/13
1.2K0
Android AVDemo(2):音频编码,采集 PCM 数据编码为 AAC丨音视频工程示例
MediaCodec录制音视频并将合成为一个文件
音频录制 相关参考 MediaCodec硬编码pcm2aac 主要分为以下几步骤:
曾大稳
2020/01/20
2.4K0
MediaCodec录制音视频并将合成为一个文件
Android音视频硬编码与混合(三)
硬编码:使用非CPU进行编码,如显卡GPU、专用的DSP、FPGA、ASIC芯片等
PengJie
2021/01/03
2.7K0
「Android音视频编码那点破事」第五章,使用MediaCodec编码AAC音频数据
  在上一章我们讲到了MediaCodec的工作流程,以及如何利用MediaCodec进行H264编码。这一章的内容同样是MediaCodec,只不过是编码音频为AAC,整个流程大同小异。   上一章我们利用MediaCodec编码视频时,使用了Surface,所以可以不直接操作输入缓冲区队列。但是编码音频的时候,由于无法使用Surface,所以需要直接操作输入缓冲区队列。   这里我们需要通过AudioRecord采集PCM数据,然后把采集到的数据送进编码器进行编码。所以首先我们要初始化一个AudioRecord对象。   要使用录音,需要申请录音权限。
阿利民
2022/05/16
6250
Android端的短视频开发,我们该如何快速实现移动端短视频功能?
优质短视频内容的产生依赖于短视频的采集和特效编辑,这就要求在进行抖音APP开发时,用到基础的美颜、混音、滤镜、变速、图片视频混剪、字幕等功能,在这些功能基础上,进行预处理,结合OpenGL、AI、AR技术,产生很多有趣的动态贴纸玩法,使得短视频内容更具创意。
Android技术干货分享
2019/03/27
2.4K0
Android端的短视频开发,我们该如何快速实现移动端短视频功能?
使用MediaCodeC将图片集编码为视频
这是MediaCodeC系列的第三章,主题是如何使用MediaCodeC将图片集编码为视频文件。在Android多媒体的处理上,MediaCodeC是一套非常有用的API。此次实验中,所使用的图片集正是MediaCodeC硬解码视频,并将视频帧存储为图片文件文章中,对视频解码出来的图片文件集,总共332张图片帧。 若是对MediaCodeC视频解码感兴趣的话,也可以浏览之前的文章:MediaCodeC解码视频指定帧,迅捷、精确
AiLo
2019/11/22
2.8K0
视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术
从游戏、教育、电商到娱乐,直播技术的应用场景无处不在。随着移动端的网速越来越快,直播技术的普及和发展将更加迅速。
JackJiang
2024/10/17
5610
视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术
推荐阅读
相关推荐
「Android音视频编码那点破事」第四章,使用MediaCodec实现H264编码
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档