基于TCP/IP和UDP组播的Qt网络直播间项目

文章目录

项目源码 -> 点这里

项目演示视频 ->

一、项目概述

  1. 项目名称:语音直播聊天室
  2. 核心定位:基于 C/S 架构的实时互动直播平台
  3. 技术架构:TCP 协议(客户端 - 服务器通信)+ UDP 协议(音视频传输)+ 数据库存储
  4. 核心价值:支持多用户实时直播、互动交流,满足直播观看与社交需求

二、核心功能模块

  1. 用户管理模块:注册(查重防重复)、登录(防重复登录)、注销、充值
  2. 直播房间模块:创建直播间(主播权限)、加入直播间(游客权限)、房间列表刷新
  3. 音视频传输模块:主播摄像头 / 麦克风采集、UDP 组播传输、游客实时接收
  4. 互动功能模块:文字群聊、弹幕发送、礼物赠送(积分 / 金钱联动)
  5. 列表展示模块:直播房间列表、直播间游客列表实时刷新

三、技术实现亮点

  1. 协议设计:自定义协议包封装,实现数据统一传输与解析
  2. 多线程支持:多客户端并发连接,独立线程处理通信逻辑
  3. 数据存储:DAO 层封装,用户信息、积分等数据安全存储于数据库
  4. 音视频优化:UDP 组播避免串房,视频压缩适配网络传输,音频 G.711 编码保障音质
  5. 设计模式:采用抽象工厂、策略模式等,实现功能模块解耦

四、 架构框图

五、 音视频模块

音频、视频、弹幕都采用udp组播 因udp组播可分组通信,且可以在广域网上传输,而udp广播只适用于局域网

可直接移植音视频模块:请参考附件《C++项目Qt视频、音频部分整理》

六、 应用层协议

理解JSON,JS对象,QJSON的区别和联系

JSON和JS对象的概念:

为什么用JSON:

JSON和QJSON的概念?为什么在Qt中要转成QBtyeArray?

场景 数据(要寄的东西) 工具(用来写 / 读的) 发送工具(邮局 / 快递员) 必须做的事
Web 开发 用户名、年龄 JSON.stringify/parse AJAX(邮局) 直接把 JSON 文字交给邮局,邮局收
Qt 开发 用户名、年龄 QJSON 工具包 Qt 网络模块(挑剔快递员) 先把 JSON 文字装进 QByteArray 快递袋,再给快递员

自定义协议的代码实现:

cpp 复制代码
class Protocol{
public:
    enum Type{
        none,

        rgst,               //账户注册
        login,              //账户登录
        ClientExit,         //账户退出
        ClientDelete,       //账户注销
        showUserInfo,       //显示信息
        chargeMoney,        //账户充值

        RoomCreate,         //创建房间
        JoinRoom,           //加入房间
        refreshRoomList,    //房间列表
        refreshAudienceList,//游客列表
        closeRoom,          //关闭房间
        sendMsg,            //用户群聊
        backToHall,         //退出直播
        AudienceBackToHall, //退出直播
        sendGift            //送出礼物
    };

    Protocol(Type type = none):type(type){}

    void setType(Type type){this->type = type;}
    QJsonValueRef operator[](const QString& key){return m_body[key];}   //set值时返回值必须是引用或指针,否则操作的只是副本,值无法改变

    Type getType()const{return type;}
    QJsonValue operator[](const QString& key)const{return m_body[key];}

    //打包
    QByteArray pack(){
        //数据包 = head + body(可变长数据部分)
        QByteArray body = QJsonDocument(m_body).toJson();

        QByteArray head(8,0);
        //head的前四个字节为可变长数据部分的大小
        *(int*)head.data() = body.size();
        //head的后四个字节为数据包的类型(固定数据部分的大小)
        *(Type*)(head.data()+4) = type; //??

        return head+body;
    }
    //拆包
    int unpack(const QByteArray& dataPack){
        if(dataPack.isEmpty()){return 0;}

        //可边长数据部分的长度
        int len = *(int*)dataPack.data();
        if(len<=0){return 0;}

        //取出类型
        this->type = *(Type*)(dataPack.data()+4);

        //取出可边长数据部分
        QByteArray body = dataPack.mid(8,len);
        //保存到json类里
        m_body = QJsonDocument::fromJson(body).object();

        return 8+len;
    }

private:
   Type type;
   QJsonObject m_body;
};

七、 多用户并发

7.1 代码中 "多用户并发" 的核心实现逻辑

项目基于 Qt 的事件循环机制(而非多线程),通过 "为每个客户端创建独立套接字 + 独立消息处理对象",实现多用户同时连接与通信,具体流程如下:

1. 服务端:为每个客户端分配独立套接字(关键代码:Server_w1.cpp

当新客户端连接时,服务端通过QTcpServernewConnection()信号触发newConnection_slot(),为每个客户端创建独立的QTcpSocket对象 ,并将其存入SockContainer容器管理:

cpp 复制代码
// Server_w1.cpp 核心代码
void Server_w1::newConnection_slot(){
    // 为新客户端创建独立套接字
    QTcpSocket* tcpSocket = tcpServer->nextPendingConnection();
    // 将套接字存入单例容器(SockContainer),避免内存泄漏
    SockContainer* sockContainer = SockContainer::getInstance();    
    sockContainer->insertInto_SockContainer(tcpSocket); 
    // 为该套接字创建独立的消息处理器(SockHandle)
    new SockHandle(this,tcpSocket); 
}
  • 关键作用:每个客户端的通信通过 "专属套接字" 完成,不同客户端的数据流互不干扰,是并发的基础。

2. 独立消息处理:SockHandle类隔离客户端请求(关键代码:SockHandle.cpp

为每个客户端的QTcpSocket创建独立的SockHandle对象,通过readyRead()信号(Qt 事件驱动)处理该客户端的消息,避免多用户请求相互阻塞:

cpp 复制代码
// SockHandle.cpp 核心代码
SockHandle::SockHandle(QObject* parent,QTcpSocket* tcpSocket)
    :QObject(parent),tcpSocket(tcpSocket)
{
    // 绑定"客户端发消息"信号到专属处理槽函数
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_slot()));
}

void SockHandle::readyRead_slot(){
    QByteArray byte = tcpSocket->readAll(); // 仅读取当前客户端的数据流
    Protocol dataPack;
    if(dataPack.unpack(byte) >0){
        // 为当前客户端的请求分配独立处理器(DataPack_Handle)
        DataPack_Handle* handle = new DataPack_Handle;
        handle->strategy(tcpSocket,dataPack); // 处理当前客户端的业务(注册/登录/进房等)
        delete handle;
    }
}
  • 关键逻辑 :每个SockHandle仅关联一个客户端的套接字,readyRead_slot()只处理该客户端的消息,通过 Qt 的事件循环机制,实现 "多客户端请求的并行处理"(本质是事件的异步调度,而非 CPU 多线程并行)。

3. 广播机制:支持多客户端同步接收消息(关键代码:RoomManage.cpp

当需要向多客户端同步消息(如刷新房间列表、同步聊天记录)时,通过SockContainer遍历所有客户端套接字,逐一发送消息,确保多用户同时接收:

cpp 复制代码
// RoomManage.cpp 刷新房间列表核心代码
void RoomManage::refreshRoomList(){
    RoomContainer* roomSingle = RoomContainer::getInstance();
    SockContainer* sockSingle = SockContainer::getInstance();
    QVector<QTcpSocket*> allSockets = sockSingle->get_allSockets(); // 获取所有在线客户端套接字
    
    Protocol dataPack2(Protocol::refreshRoomList);
    // 打包所有房间名到协议包
    int count = 0;
    for(auto& room : roomSingle->get_allRooms()){
        dataPack2[QString::number(count++)] = room.get_roomName();
    }
    
    // 向所有客户端发送刷新指令(并发同步)
    for(auto& sock : allSockets){
        sock->write(dataPack2.pack());
    }
}
  • 效果:多客户端可同时收到 "房间列表更新""弹幕""礼物提示" 等消息,实现并发场景下的同步交互。

7.2 与 "多线程" 的区别:事件驱动 vs 多线程并行

你的项目确实没有使用多线程,但通过 Qt 的事件循环实现了 "伪并发",二者核心差异如下:

对比维度 你的项目(事件驱动) 多线程实现(如QThread
核心原理 基于 Qt 事件循环,异步调度多个客户端的事件(如readyRead),同一时间仅处理一个事件,但切换速度快,感知不到阻塞 基于 CPU 多线程,为每个客户端 / 任务分配独立线程,真正实现并行处理
资源占用 低(无需创建多线程栈、线程调度开销) 高(每个线程占用独立内存栈,线程切换有 CPU 开销)
代码复杂度 低(无需处理线程同步、互斥锁等问题) 高(需解决线程安全、资源竞争,如数据库连接互斥)
适用场景 轻量级并发(如 10-50 个客户端,无密集计算) 高并发、密集计算场景(如百级以上客户端、音视频编解码)

总结:你的项目通过 "事件驱动 + 独立套接字 + 异步处理",在不使用多线程的情况下,满足了中小型规模的多用户并发需求,且避免了多线程带来的复杂度(如线程安全问题)。

7.3 当前实现的优势与潜在局限

  1. 优势:轻量、稳定、易维护
  • 无需处理多线程同步问题(如数据库连接冲突、容器访问竞争),代码故障率低;
  • 资源占用少,适合轻量级直播场景(如几十人同时在线);
  • 基于 Qt 原生事件机制,兼容性好,跨平台部署简单。
  1. 潜在局限:高并发下的性能瓶颈

当客户端数量大幅增加(如超过 100 个)或存在密集型操作(如高清音视频编码)时,单线程事件循环可能出现瓶颈:

  • 所有客户端的消息处理都在主线程事件循环中调度,若某个请求处理耗时较长(如复杂数据库查询),会阻塞其他客户端的消息;
  • 音视频传输通过 UDP 组播优化,但消息分发(如遍历所有套接字发送刷新指令)的效率会随客户端数量增加而下降。

7.4 若需提升并发能力:可选的优化方向(基于现有代码扩展)

若后续需要支持更多用户,可在现有架构基础上,局部引入多线程(无需重构整体代码),例如:

  1. 业务处理线程池 :将DataPack_Handle::strategy()的业务逻辑(如注册登录、送礼积分计算)放入QThreadPool,避免耗时操作阻塞主线程;
  2. 音视频独立线程:将主播端的音视频采集、编码放入独立线程,避免音视频处理占用主线程资源;
  3. 数据库操作异步化 :使用QtConcurrent::run()将数据库查询 / 更新(如MyDB::createConnection()后的 SQL 执行)放入后台线程,避免 IO 等待阻塞主线程。

这些优化可在不破坏现有 "事件驱动 + 独立套接字" 架构的前提下,提升高并发场景下的稳定性。

八、总结与展望

  1. 项目亮点:跨协议协同、多用户并发支持、完整互动闭环
  2. 优化方向:界面美化、音视频延迟优化、更多互动玩法扩展
  3. 应用场景:在线直播、远程互动、兴趣社群交流
相关推荐
Code Warrior2 小时前
【Linux】Socket 编程预备知识
linux·网络·c++
jenchoi4133 小时前
【2025-11-12】软件供应链安全日报:最新漏洞预警与投毒预警情报汇总
网络·安全·web安全·网络安全·npm
xxtzaaa4 小时前
游戏被IP限制多开,如何在同一网络下用不同IP多开游戏?
网络·tcp/ip·游戏
DY009J4 小时前
如何在Ubuntu虚拟机中设置Samba共享,并在Windows宿主机中挂载为网络驱动器
网络·windows·ubuntu
Empty_7775 小时前
Ansible之Playbook简单应用
网络·ansible
Orlando cron5 小时前
CPU Load(系统平均负载)
运维·服务器·网络
Nimsolax6 小时前
Linux网络数据链路层
linux·网络
小武~6 小时前
嵌入式网络编程实战:从Socket基础到高并发优化
linux·网络
蓝天智能7 小时前
Qt 的字节序转换
开发语言·qt