支持语音与视频即时通讯项目杂记(一)

news2025/7/19 15:46:34

    

第一部分解释服务端的实现。

(服务端结构)

   下面一个用于实现TCP服务器的代码,包括消息服务器(TcpMsgServer)和文件中转服务器(TcpFileServer)。

首先,TcpServer是TcpMsgServer和TcpFileServer的基类,它负责创建QTcpServer对象并监听端口。通过StartListen()函数可以启动监听,传入指定的端口号进行监听。CloseListen()函数用于关闭监听。

TcpMsgServer是消息服务器,继承自TcpServer类。它通过重写SltNewConnection()函数来处理新客户端连接的逻辑。当有新的客户端连接到服务器时,会创建一个ClientSocket对象来管理该客户端连接。在SltConnected()函数中,对连接进行验证后,将客户端对象添加到容器m_clients中,并建立与该客户端的信号与槽连接。在SltDisConnected()函数中,处理客户端下线的情况,从容器中移除对应的客户端对象,并断开相关的信号与槽连接。SltMsgToClient()函数用于消息转发控制,根据收到的消息类型、目标客户端ID和消息内容,找到对应的客户端对象,并调用其SltSendMessage()函数将消息发送给客户端。

TcpFileServer是文件中转服务器,同样继承自TcpServer类。它也重写了SltNewConnection()函数来处理新的客户端连接。在SltConnected()函数中,将连接上的客户端对象添加到容器m_clients中。SltDisConnected()函数处理客户端断连的情况,从容器中移除对应的客户端对象,并断开相关的信号与槽连接。SltClientDownloadFile()函数处理客户端请求下载文件的情况,根据收到的消息中的来源ID和文件名,在容器m_clients中找到对应的客户端对象,调用其StartTransferFile()函数开始文件传输过程。

在代码中,TcpMsgServer和TcpFileServer都采用了容器来管理连接的客户端对象,以便进行消息转发和文件传输等操作。

#include "tcpserver.h"
#include "clientsocket.h"
#include "myapp.h"
#include "databasemagr.h"

#include <QHostAddress>

/
/// 服务器类,是TcpMsgServer和TcpFileServer的基类
TcpServer::TcpServer(QObject *parent) :
    QObject(parent)
{
    m_tcpServer = new QTcpServer(this);

    connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(SltNewConnection()));
}

TcpServer::~TcpServer()
{
    if (m_tcpServer->isListening()) m_tcpServer->close();
}

///启动监听
bool TcpServer::StartListen(int port)
{
    if (m_tcpServer->isListening()) m_tcpServer->close();
    bool bOk = m_tcpServer->listen(QHostAddress::Any, port);
    return bOk;
}

///关闭监听
void TcpServer::CloseListen()
{
    m_tcpServer->close();
}


/
/// 消息服务器
TcpMsgServer::TcpMsgServer(QObject *parent) :
    TcpServer(parent)
{
}

TcpMsgServer::~TcpMsgServer()
{
    qDebug() << "tcp server close";
    foreach (ClientSocket *client, m_clients) {
        m_clients.removeOne(client);
        client->Close();
    }
}

/// 新客户端连接处理
void TcpMsgServer::SltNewConnection()
{
    ClientSocket *client = new ClientSocket(this, m_tcpServer->nextPendingConnection());
    connect(client, SIGNAL(signalConnected()), this, SLOT(SltConnected()));
    connect(client, SIGNAL(signalDisConnected()), this, SLOT(SltDisConnected()));
}

///通过验证后,才可以加入容器进行管理
void TcpMsgServer::SltConnected()
{
    ClientSocket *client = (ClientSocket *)this->sender();
    if (NULL == client) return;

    connect(client, SIGNAL(signalMsgToClient(quint8,int,QJsonValue)),
            this, SLOT(SltMsgToClient(quint8,int,QJsonValue)));
    connect(client, SIGNAL(signalDownloadFile(QJsonValue)), this, SIGNAL(signalDownloadFile(QJsonValue)));
    m_clients.push_back(client);

    qDebug() << "TcpMsgServer::SltConnected. last m_nId=" + QString::number(m_clients[m_clients.size()-1]->GetUserId());
}

///有客户端下线
void TcpMsgServer::SltDisConnected()
{
    //找到断连的socket
    ClientSocket *client = (ClientSocket *)this->sender();
    if (NULL == client) return;

    //移除对应socket
    for (int i = 0; i < m_clients.size(); i++) {
        if (client == m_clients.at(i))
        {
            m_clients.remove(i);
            return;
        }
    }

    disconnect(client, SIGNAL(signalConnected()), this, SLOT(SltConnected()));
    disconnect(client, SIGNAL(signalDisConnected()), this, SLOT(SltDisConnected()));
    disconnect(client, SIGNAL(signalMsgToClient(quint8,int,QJsonValue)),
               this, SLOT(SltMsgToClient(quint8,int,QJsonValue)));
    disconnect(client, SIGNAL(signalDownloadFile(QJsonValue)), this, SIGNAL(signalDownloadFile(QJsonValue)));
}

///消息转发控制
void TcpMsgServer::SltMsgToClient(const quint8 &type, const int &id, const QJsonValue &json)
{
    // 查找要发送过去的id
    for (int i = 0; i < m_clients.size(); i++) {
        if (id == m_clients.at(i)->GetUserId())
        {
            qDebug()<<"TcpMsgServer::SltMsgToClient. send to:"+QString::number(id);

            m_clients.at(i)->SltSendMessage(type, json);
            return;
        }
    }
}

///传送文件到指定ID的客户端
void TcpMsgServer::SltTransFileToClient(const int &userId, const QJsonValue &json)
{
    // 查找要发送过去的id
    for (int i = 0; i < m_clients.size(); i++) {
        if (userId == m_clients.at(i)->GetUserId())
        {
            m_clients.at(i)->SltSendMessage(SendFile, json);
            return;
        }
    }
}

//
/// 文件中转服务器,客户端先把待转发的文件保存在服务器
/// 服务器接受完成后,通知其他客户端来下载
TcpFileServer::TcpFileServer(QObject *parent) :
    TcpServer(parent)
{
}

TcpFileServer::~TcpFileServer()
{
    qDebug() << "tcp server close";
    foreach (ClientFileSocket *client, m_clients) {
        m_clients.removeOne(client);
        client->Close();
    }
}

///客户端与文件服务器新建连接
void TcpFileServer::SltNewConnection()
{
    //新建槽函数与socket
    ClientFileSocket *client = new ClientFileSocket(this, m_tcpServer->nextPendingConnection());
    connect(client, SIGNAL(signalConnected()), this, SLOT(SltConnected()));
    connect(client, SIGNAL(signalDisConnected()), this, SLOT(SltDisConnected()));
}

/// socket管理
void TcpFileServer::SltConnected()
{
    //连接时将Client放入vector m_clients
    ClientFileSocket *client = (ClientFileSocket *)this->sender();
    if (NULL == client) return;

    m_clients.push_back(client);
}

/// 客户端断连
void TcpFileServer::SltDisConnected()
{
    ClientFileSocket *client = (ClientFileSocket *)this->sender();
    if (NULL == client) return;

    for (int i = 0; i < m_clients.size(); i++) {
        if (client == m_clients.at(i))
        {
            m_clients.remove(i);
            return;
        }
    }

    disconnect(client, SIGNAL(signalConnected()), this, SLOT(SltConnected()));
    disconnect(client, SIGNAL(signalDisConnected()), this, SLOT(SltDisConnected()));
}

/// 客户端请求下载文件
void TcpFileServer::SltClientDownloadFile(const QJsonValue &json)
{
    // 根据ID寻找连接的socket
    if (json.isObject()) {
        QJsonObject jsonObj = json.toObject();
        qint32 nId = jsonObj.value("from").toInt();//
        qint32 nWid = jsonObj.value("id").toInt();;//
        QString fileName = jsonObj.value("msg").toString();
        qDebug() << "get file" << jsonObj << m_clients.size();
        for (int i = 0; i < m_clients.size(); i++) {
            if (m_clients.at(i)->CheckUserId(nId, nWid))
            {
                m_clients.at(i)->StartTransferFile(fileName);
                return;
            }
        }
    }
}

       当服务端端通过accpt收到一个请求后,创建一个ClientSocket,处理客户端消息。
        下面是一个Qt中的客户端socket管理类,用于与服务端进行通信。其中包含两个类,一个是ClientSocket,用于处理普通消息,另一个是ClientFileSocket,用于处理文件传输。

在ClientSocket中,包含了一些信号和槽函数,用于处理连接、数据接收、关闭等操作。同时还有一些私有函数,用于解析不同类型的消息,并且把解析后的数据发送到前台界面进行展示。

在ClientFileSocket中,主要有两个功能:文件接收和文件发送。对于文件接收,分别记录了已经接收到的数据大小、文件名大小、要接收的文件等信息;对于文件发送,记录了文件大小、已经发送的数据大小、剩余数据大小、要发送的文件等信息。同时还有一些私有函数,用于初始化socket、处理接收到的数据、更新发送进度等操作。

总的来说,这个类是一个很重要的网络通信模块,可以实现与服务端的双向交互,包括文字、图片、文件等。

#ifndef CLIENTSOCKET_H
#define CLIENTSOCKET_H

#include <QObject>
#include <QTcpSocket>
#include <QFile>
#include <QApplication>

/// 服务端socket管理类
class ClientSocket : public QObject
{
    Q_OBJECT
public:
    explicit ClientSocket(QObject *parent = 0, QTcpSocket *tcpSocket = NULL);
    ~ClientSocket();

    int GetUserId() const;
    void Close();
signals:
    void signalConnected();
    void signalDisConnected();
    void signalDownloadFile(const QJsonValue &json);
    void signalMsgToClient(const quint8 &type, const int &id, const QJsonValue &dataVal);
public slots:

private:
    QTcpSocket *m_tcpSocket;
    int         m_nId;

public slots:
    // 消息回发
    void SltSendMessage(const quint8 &type, const QJsonValue &json);

private slots:
    void SltConnected();
    void SltDisconnected();
    void SltReadyRead();

private:
    // 消息解析和抓转发处理
    void ParseLogin(const QJsonValue &dataVal);
    void ParseUserOnline(const QJsonValue &dataVal);
    void ParseLogout(const QJsonValue &dataVal);
    void ParseUpdateUserHead(const QJsonValue &dataVal);

    void ParseReister(const QJsonValue &dataVal);
    void ParseAddFriend(const QJsonValue &dataVal);
    void ParseAddGroup(const QJsonValue &dataVal);
    void ParseCreateGroup(const QJsonValue &dataVal);

    void ParseGetMyFriend(const QJsonValue &dataVal);
    void ParseGetMyGroups(const QJsonValue &dataVal);

    void ParseRefreshFriend(const QJsonValue &dataVal);
    void ParseRefreshGroups(const QJsonValue &dataVal);

    void ParseFriendMessages(const QByteArray &reply);
    void ParseGroupMessages(const QByteArray &reply);
};

ClientSocket::ClientSocket(QObject *parent, QTcpSocket *tcpSocket) :
    QObject(parent)
{
    qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");


    m_nId = -1;

    if (tcpSocket == NULL) m_tcpSocket = new QTcpSocket(this);
    m_tcpSocket = tcpSocket;

    connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(SltReadyRead()));//处理客户端信息
    connect(m_tcpSocket, SIGNAL(connected()), this, SLOT(SltConnected()));//处理登录成功信号
    connect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(SltDisconnected()));//处理登出信号
}

处理客户端消息,根据消息类型进行不同的处理:

void ClientSocket::SltReadyRead()
{
    // 读取socket数据
    QByteArray reply = m_tcpSocket->readAll();
    QJsonParseError jsonError;
    // 转化为 JSON 文档
    QJsonDocument doucment = QJsonDocument::fromJson(reply, &jsonError);
    // 解析未发生错误
    if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) {
        // JSON 文档为对象
        if (doucment.isObject()) {
            // 转化为对象
            QJsonObject jsonObj = doucment.object();
            int nType = jsonObj.value("type").toInt();
            QJsonValue dataVal = jsonObj.value("data");

            switch (nType) {
            case Register:
            {
                ParseReister(dataVal);
            }
                break;
            case Login:
            {
                ParseLogin(dataVal);
            }
                break;
            case UserOnLine:
            {
                ParseUserOnline(dataVal);
            }
                break;
            case Logout:
            {
                ParseLogout(dataVal);
                Q_EMIT signalDisConnected();
                m_tcpSocket->abort();
            }
                break;
            case UpdateHeadPic:
            {
                ParseUpdateUserHead(dataVal);
            }
                break;
            case AddFriend:
            {
                ParseAddFriend(dataVal);
            }
                break;
            case AddGroup:
            {
                ParseAddGroup(dataVal);
            }
                break;
            case CreateGroup:
            {
                ParseCreateGroup(dataVal);
            }
                break;
            case GetMyFriends:
            {
                ParseGetMyFriend(dataVal);
            }
                break;
            case GetMyGroups:
            {
                ParseGetMyGroups(dataVal);
            }
                break;

            case RefreshFriends:
            {
                ParseRefreshFriend(dataVal);
            }
                break;
            case RefreshGroups:
            {
                ParseRefreshGroups(dataVal);
            }
                break;
            case SendMsg:
            case SendFile:
            case SendPicture:
            {
                ParseFriendMessages(reply);
            }
                break;
            case SendGroupMsg:
            {
                ParseGroupMessages(reply);
            }
                break;
            case SendFace:
            {
                ParseGroupMessages(reply);
            }
                break;
            case SendFileOk:
            {

            }
                break;
            case GetFile:
            {
                Q_EMIT signalDownloadFile(dataVal);
            }
                break;
            default:
                break;
            }
        }
    }
}

登录的处理:

void ClientSocket::ParseLogin(const QJsonValue &dataVal)
{
    // data 的 value 也是JSON对象
    if (dataVal.isObject()) {
        QJsonObject dataObj = dataVal.toObject();
        QString strName = dataObj.value("name").toString();
        QString strPwd = dataObj.value("passwd").toString();
        QJsonObject jsonObj = DataBaseMagr::Instance()->CheckUserLogin(strName, strPwd);

        m_nId = jsonObj.value("id").toInt();
        qDebug() << "login" << jsonObj;
        //验证成功才向server发送信号说明可以将socket加入容器管理
        if (m_nId > 0) Q_EMIT signalConnected();
        // 发送查询结果至客户端
        SltSendMessage(Login, jsonObj);;
    }
}

余略.....

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

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

相关文章

回文子串00

题目链接 回文子串 题目描述 注意点 s 由小写英文字母组成s 由小写英文字母组成1 < s.length < 1000具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符组成&#xff0c;也会被视作不同的子串 解答思路 最初穷举所有的子串判断每个子串是否是回文子串…

C++标准库算法整理

目录 1、数值操作 1.1、std::accumulate 1.2、std::inner_product 1.3、std::partial_sum 1.4、std::exclusive_scan 1.5、std::inclusive_scan 1.6、std::reduce 2、相邻元素 2.1、std::adjacent_difference 2.2、std::adjacent_find 2.3、std::unique 2.4、std::u…

阿里云2023年双十一优惠活动整理

随着双十一的临近&#xff0c;阿里云也为大家准备了一系列优惠活动。作为国内知名的云服务提供商&#xff0c;阿里云在双十一期间推出了多种优惠政策和福利&#xff0c;让用户在享受优质云服务的同时&#xff0c;也能节省一些费用。本文将对阿里云双十一优惠活动进行详细整理&a…

合伙企业的执行事务合伙人委派代表是什么样的存在

当合伙企业的执行事务合伙人为法人或非法人组织时&#xff0c;通常会委派自然人代表其执行合伙事务&#xff0c;特别是各类投资基金、信托、资产证券化等合伙企业类型的SPV中&#xff0c;由法人执行事务合伙人委派代表执行合伙企业事务比较常见&#xff0c;由此可能出现合伙企业…

AFL安全漏洞挖掘

安全之安全(security)博客目录导读 ATF(TF-A)/OPTEE之FUZZ安全漏洞挖掘汇总 目录 一、AFL简介 二、AFL的安装 三、代码示例及种子语料库 四、AFL插桩编译 五、AFL运行及测试 六、AFL结果分析 一、AFL简介 模糊测试&#xff08;Fuzzing&#xff09;技术作为漏洞挖掘最有…

Compose 实战之为下拉刷新添加自定义指示器

前言 在安卓开发中&#xff0c;下拉刷新是一个非常常用的功能&#xff0c;几乎只要是涉及到列表展示数据的界面都会用到它。 而 Compose 却直到 2022年10月份才在 compose.material:1.3.0 中添加了对下拉刷新的支持&#xff1a;Modifier.pullRefresh 。 在此之前&#xff0c…

SpringBoot整合Activiti

SpringBoot集成Activiti7 SpringBoot版本使用2.7.16 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.16</version><relativePath/> <!-- lookup…

高并发场景下常见的限流算法及方案介绍

应用场景 现代互联网很多业务场景&#xff0c;比如秒杀、下单、查询商品详情&#xff0c;最大特点就是高并发&#xff0c;而往往我们的系统不能承受这么大的流量&#xff0c;继而产生了很多的应对措施&#xff1a;CDN、消息队列、多级缓存、异地多活。 但是无论如何优化&…

element ui 中 el-button重新渲染后disabled属性失效

调试发现:disabled绑定的值和显示没有保持一致&#xff0c;发现是disabled属性失效 解决方式&#xff1a; 给标签添加key 比如&#xff1a;key“isOldVersion” <el-form-item><el-button type"primary" style"margin-left: 100px;" click"…

TX Text Control .NET Server for ASP.NET 32.0 Crack

TX Text Control .NET Server for ASP.NET 是VISUAL STUDIO 2022、ASP.NET CORE .NET 6 和 .NET 7 支持&#xff0c;将文档处理集成到 Web 应用程序中&#xff0c;为您的 ASP.NET Core、ASP.NET 和 Angular 应用程序添加强大的文档处理功能。 客户端用户界面 文档编辑器 将功能…

手撕 视觉slam14讲 ch7 / pose_estimation_3d2d.cpp (2)

上一篇文章中: 手撕ch7/pose_estimation_3d2d&#xff08;1&#xff09;&#xff0c;我们调用了epnp的方法进行位姿估计&#xff0c;这里我们使用非线性优化的方法来求解位姿&#xff0c;使用g2o进行BA优化 首先介绍g2o&#xff1a;可参考&#xff1a;g2o详细介绍 1.构建g2o图…

解决 MyBatis 一对多查询中,出现每组元素只有一个,总组数与元素数总数相等的问题

文章目录 问题简述场景描述问题描述问题原因解决办法 问题简述 笔者在使用 MyBatis 进行一对多查询的时候遇到一个奇怪的问题。对于笔者的一对多的查询结果&#xff0c;出现了这样的一个现象&#xff1a;原来每个组里有多个元素&#xff0c;查询目标是查询所查的组&#xff0c;…

【数据结构】线性表(一)线性表的定义及其基本操作(顺序表插入、删除、查找、修改)

目录 一、线性表 1. 线性表的定义 2. 线性表的要素 二、线性表的基本操作 三、线性表的顺序存储结构 1. 定义 2. 顺序表的操作 a. 插入操作 b. 删除操作 c. 查找操作 d. 修改操作 e. 代码实例 一、线性表 1. 线性表的定义 一个线性表是由零个或多个具有相同…

TCP/IP网络分层模型

TCP/IP当初的设计者真的是非常聪明&#xff0c;创造性地提出了“分层”的概念&#xff0c;把复杂的网络通信划分出多个层次&#xff0c;再给每一个层次分配不同的职责&#xff0c;层次内只专心做自己的事情就好&#xff0c;用“分而治之”的思想把一个“大麻烦”拆分成了数个“…

行业追踪,2023-10-17

自动复盘 2023-10-17 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

华为云云耀云服务器L实例评测|使用sysbench对云耀云服务器mysql的性能测试

目录 引言 1 在centos上安装mysql 1.1 在云服务器上安装 Docker 1.2 在 Docker 中运行 MySQL 容器 2 安装sysbench并进行性能测试 2.1 安装和配置 sysbench 2.2 运行 sysbench 性能测试 3 分析测试结果 3.1 运行结果 3.2 对运行结果进行翻译 3.3 性能分析 4 清理…

AIGC - 入门向量空间模型

文章目录 向量和向量空间向量的运算什么是向量空间&#xff1f;向量空间的几个重要概念向量之间的距离曼哈顿距离&#xff08;Manhattan Distance&#xff09;欧氏距离&#xff08;Euclidean Distance&#xff09;切比雪夫距离&#xff08;Chebyshev Distance&#xff09; 向量…

qml加载ttf字体库

1,下载获取ttf文件 iconfont-阿里巴巴矢量图标库 字体图标下载 - FontAwesome 字体图标中文Icon 2,添加到项目文件 3,项目添加字体库 #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QFontDatabase> #include <QDebug>in…

Error: GlobalConfigUtils setMetaData Fail Cause:java.lang.NullPointerException

文章目录 1、在开发中会出现这样的错误。2、其次&#xff0c;再看其他错误&#xff1a; 1、在开发中会出现这样的错误。 完整错误&#xff1a;Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Error: GlobalConfigUtils setMetaData Fail ! Cause…

白银现货K线走势图分析

K线图又称蜡烛图、阴阳烛&#xff0c;这一理论起源于日本&#xff0c;是最古老的技术分析方法。在众多的现货白银技术分析方法中&#xff0c;K线分析是核心和根本&#xff0c;因为很多的技术分析方法的分析要素和计算方式&#xff0c;都是来自K线中的四个价格。 面对同样一张的…