Qt写群聊项目(一):服务器

先从头文件中了解整体逻辑

cpp 复制代码
//来自groupchaatserver.h



#ifndef GROUPCHATSERVER_H
#define GROUPCHATSERVER_H

#include <QObject>
#include <QTcpServer>
#include<QTcpSocket>
class GroupChatServer : public QObject
{
    Q_OBJECT
public:
    explicit GroupChatServer(QObject *parent = nullptr);
    //启动服务器
    bool start(const QString& host,quint16 port);
public:
   //服务器
    void slot_acceptError(QAbstractSocket::SocketError socketError);
   void slot_newConnection();
    //客户端
   void slot_disconnected();
   void slot_errorOccurred(QAbstractSocket::SocketError socketError);
   void slot_readyRead();
signals:
private:
    QTcpServer *m_server;//服务器
    QList<QTcpSocket*>m_sockets;//所有客户端,可以与服务器进行连接
};

#endif // GROUPCHATSERVER_H

这段代码定义了一个基于 Qt 框架 (使用 QTcpServer 和 QTcpSocket)的 群聊服务器GroupChatServer

它的核心设计逻辑是:作为一个 TCP 服务器,监听网络连接,维护所有连接进来的客户端列表,并处理数据的收发和连接状态的变化。

cpp 复制代码
private:
    QTcpServer *m_server;        // 服务器
    QList<QTcpSocket*> m_sockets; // 所有客户端

这是整个服务器的骨架:

  • m_server:这是监听者。它负责绑定 IP 和端口,等待外部的连接请求。它本身不传输数据,只负责"接待"。
  • m_sockets :这是一个容器
    • 因为是"群聊"服务器,必须同时和多个客户端保持连接。
    • 每当一个客户端成功连接,服务器就会创建一个对应的 QTcpSocket 对象,并存入这个列表中。
    • 群发逻辑的关键:当服务器收到某个客户端的消息时,会遍历这个列表,把消息转发给列表中的其他所有 Socket。
cpp 复制代码
bool start(const QString& host, quint16 port);
  • 启动服务器:用户调用这个函数并传入 IP 地址和端口号。

整个服务器的运行流程:

A. 连接建立阶段
  • void slot_newConnection()
    • 触发时机 :当 m_server 检测到有新的客户端尝试连接时触发。
B. 数据通信阶段(群聊核心)
  • void slot_readyRead()
    • 触发时机:当某个客户端发送数据到达服务器时触发。
C. 连接断开阶段
  • void slot_disconnected()
    • 触发时机:当某个客户端断开连接时触发。
D. 异常处理
  • void slot_acceptError(...):处理服务器自身的监听错误(例如端口被占用)。
  • void slot_errorOccurred(...):处理具体某个客户端通信过程中的错误(例如网络中断)。

下面是源文件中各函数功能的具体实现:

我们可以按照服务器的生命周期,将这段代码的逻辑分为四个关键部分来讲解:

1. 初始化与启动

cpp 复制代码
GroupChatServer::GroupChatServer(QObject *parent)
    : QObject{parent},
    m_server(new QTcpServer(this))
{
    //连接服务器信号
    connect(m_server,&QTcpServer::acceptError,this,&GroupChatServer::slot_acceptError);
    connect(m_server,&QTcpServer::newConnection,this,&GroupChatServer::slot_newConnection);
}
bool GroupChatServer::start(const QString& host,quint16 port){
    if(m_server->isListening()){
        qDebug()<<"Server already listening";
        return false;
    }
    if(!m_server->listen((QHostAddress)host,port)){
        qDebug()<<"Server listen failed:"<<m_server->errorString();
        return false;
    }
    return true;
}

构造函数中,首先将服务器自身的"监听错误"和"新连接到来"这两个信号连接到了对应的槽函数。

启动start 函数非常直观,就是让服务器开始监听网络接口。如果端口被占用或 IP 无效,会打印错误信息并返回 false。

2. 新用户处理:连接建立 (slot_newConnection)

这是服务器"纳新"的逻辑:

cpp 复制代码
void GroupChatServer::slot_newConnection()
{
    //有未获取的连接
    while(m_server->hasPendingConnections()){
        //获取下一个有效连接
        auto socket=m_server->nextPendingConnection();
        //添加客户端到数组中
        m_sockets.push_back(socket);
        //接收来自客户端的信息
        connect(socket,&QTcpSocket::disconnected,this,&GroupChatServer::slot_disconnected);
        connect(socket,&QTcpSocket::errorOccurred,this,&GroupChatServer::slot_errorOccurred);
        connect(socket,&QTcpSocket::readyRead,this,&GroupChatServer::slot_readyRead);
    }
}
  • 核心点 :这里不仅获取了连接,还建立了二次连接 。服务器本身只负责"接待",一旦接待成功(socket 拿到手),就让这个具体的客户端 socket 连接上服务器信号,去监听客户端的具体行为(比如发消息、掉线)。

3. 消息转发:群聊的核心 (slot_readyRead)

这是实现"群聊"功能的最关键代码:

cpp 复制代码
void GroupChatServer::slot_readyRead()
{
    auto sock=dynamic_cast<QTcpSocket*>(sender());
    //读取数据
    auto msg=sock->readAll();
    //转发数据
    for(auto& socket:m_sockets){
        //不能转发给别人
        if(socket==sock){
            continue;
        }
        //转发给别人
        socket->write(msg);
        socket->flush();
    }
}
  • 逻辑 :当服务器收到任何一方的消息时,它会遍历 m_sockets 列表。
  • 广播机制 :它会把这条消息发送给列表里的每一个人 ,但是通过 if(socket == sock) continue; 这一行,巧妙地排除了发送者本人,实现了群聊体验。

4. 用户离开:连接断开 (slot_disconnected)

cpp 复制代码
void GroupChatServer::slot_disconnected()
{
    //客户端断开连接,从数组中移除
    auto socket=dynamic_cast<QTcpSocket*>(sender());
    m_sockets.removeOne(socket);
}
  • 逻辑 :利用 sender() 函数,动态判断是谁断开了连接,然后直接从 m_sockets 列表中把它删掉。
  • 结果 :下次再有 slot_readyRead 转发消息时,这个已经离开的人就不会收到消息了。
相关推荐
康小庄2 小时前
SpringBoot 拦截器 (Interceptor) 与切面 (AOP):示例、作用、及适用场景
java·数据库·spring boot·后端·mysql·spring·spring cloud
小六花s2 小时前
SQL注入笔记
数据库·笔记·sql
yufuu982 小时前
Python在金融科技(FinTech)中的应用
jvm·数据库·python
OnYoung2 小时前
持续集成/持续部署(CI/CD) for Python
jvm·数据库·python
2301_822377652 小时前
高级爬虫技巧:处理JavaScript渲染(Selenium)
jvm·数据库·python
u0109272712 小时前
用Python和Twilio构建短信通知系统
jvm·数据库·python
闻哥2 小时前
从 SQL 执行到优化器内核:MySQL 性能调优核心知识点解析
java·jvm·数据库·spring boot·sql·mysql·面试
慧一居士2 小时前
pm2使用
运维·服务器
鹿角片ljp2 小时前
动态SQL实现模糊查询
数据库·sql·oracle