QT聊天室阶段性记录(完善中:注册功能,数据库存储)

news2025/2/23 19:53:06

server.h

#ifndef SERVERDEMO_H
#define SERVERDEMO_H

#include <QObject>
#include <QTcpServer>
#include <QMap>
#include <QSqlDatabase> //数据库管理类
#include <QSqlQuery>    //执行sql语句的类
#include <QSqlRecord>   //数据库记录类

#include "TextMessage.h"
#include "txtmsgassembler.h"
#include "TxtMsgHandler.h"

class ServerDemo : public QObject
{
    Q_OBJECT

    QTcpServer m_server;   //服务端类对象
    QMap<QTcpSocket*, TxtMsgAssembler*> m_map;
    TxtMsgHandler* m_handler;
    QSqlDatabase db;
public:
    ServerDemo(QObject* parent = NULL);
    bool start(int port);   //启动服务端
    void stop();            //停止服务端
    void setHandler(TxtMsgHandler* handler);//设置接口
    ~ServerDemo();

protected slots:
    void onNewConnection(); //新客户端连接槽函数
    void onConnected();     //连接槽函数
    void onDisconnected();  //断连槽函数
    void onDataReady();     //数据接收槽函数
    void onBytesWritten(qint64 bytes);
};

#endif // SERVERDEMO_H

server.cpp

#include "ServerDemo.h"
#include <QHostAddress>
#include <QTcpSocket>
#include <QObjectList>
#include <QDebug>

ServerDemo::ServerDemo(QObject* parent) : QObject(parent), m_handler(nullptr)
{
    //判断字节的数据库对象中是否包含了要处理的数据库,如果没有包含则添加一个数据库,否则直接打开
    if( !db.contains("user.db") )
    {
        //添加一个数据库,调用该类中的静态成员函数
        db = QSqlDatabase::addDatabase("QSQLITE");//sqlite3

        //设置数据库名字
        db.setDatabaseName("user.db");
    }

    //打开数据库
    if( !db.open() )
    {
        qDebug() << "数据库打开失败" << endl;
        return;
    }

    //使用sql语句进行表的创建
    QString sql = "create table if not exists user_info("
                  "username varchar(16) primary key,"
                  "password varchar(16),"
                  "status varchar(10),"
                  "level varchar(10))";
    //执行语句
    QSqlQuery querry;

    if( !querry.exec(sql) )
    {
        qDebug() << "表创建失败失败" << endl;
        return;
    }

    connect(&m_server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
}

void ServerDemo::onNewConnection()
{
    QTcpSocket* tcp = m_server.nextPendingConnection();
    TxtMsgAssembler* assembler = new TxtMsgAssembler();

    m_map.insert(tcp, assembler);//用字典容器,为每一个客户端分配一个装配器类对象

    connect(tcp, SIGNAL(connected()), this, SLOT(onConnected()));
    connect(tcp, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
    connect(tcp, SIGNAL(readyRead()), this, SLOT(onDataReady()));
    connect(tcp, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64)));

    if( m_handler != nullptr )
    {
        TextMessage msg("CONN", tcp->peerAddress().toString() + ":" + QString::number(tcp->peerPort()));

        m_handler->handle(*tcp, msg);
    }
}

void ServerDemo::onConnected()
{

}

void ServerDemo::onDisconnected()
{
    QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender());

    if( tcp != nullptr )
    {
        delete m_map.take(tcp);

        if( m_handler != nullptr )
        {
            TextMessage msg("DSCN", "");

            m_handler->handle(*tcp, msg);
        }
    }
}

//循环从缓存区读取数据
void ServerDemo::onDataReady()
{
    QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(sender());
    char buf[256] =  {0};
    int len = 0;

    if( tcp != NULL )
    {
        //通过字典容器找到发送信息的客户端,
         TxtMsgAssembler* assembler = m_map.value(tcp);

        while( (len = tcp->read(buf, sizeof(buf))) > 0 )
        {
            if( assembler != nullptr )
            {
                QSharedPointer<TextMessage> ptm = nullptr;

                assembler->prepare(buf, len);

                while( (ptm = assembler->assemble()) != nullptr )
                {
                    if( m_handler != nullptr )
                    {
                        m_handler->handle(*tcp, *ptm);
                    }
                }
            }
         }
    }
}

void ServerDemo::onBytesWritten(qint64 bytes)
{
    (void)bytes;
}

//开启服务端
bool ServerDemo::start(int port)
{
    bool ret = true;

    if( !m_server.isListening() )
    {
        ret = m_server.listen(QHostAddress("127.0.0.1"), port);
    }

    return ret;
}

//停止服务端
void ServerDemo::stop()
{
    if( m_server.isListening() )
    {
        m_server.close();
    }
}

//设置接口
void ServerDemo::setHandler(TxtMsgHandler *handler)
{
    m_handler = handler;
}

//服务端析构
ServerDemo::~ServerDemo()
{
    const QObjectList& list = m_server.children();

    for(int i=0; i<list.length(); i++)
    {
        QTcpSocket* tcp = dynamic_cast<QTcpSocket*>(list[i]);

        if( tcp != NULL )
        {
            tcp->close();
        }
    }

    const QList<TxtMsgAssembler*>& al = m_map.values();

    for(int i = 0; i < al.length(); i++)
    {
        delete al.at(i);
    }
}

server_main.cpp

#include <QCoreApplication>
#include "serverhandler.h"
#include "ServerDemo.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ServerHandler handler;
    ServerDemo server;

    server.setHandler(&handler);
    server.start(8888);

    return a.exec();
}

client.h

#ifndef CLIENTDEMO_H
#define CLIENTDEMO_H

#include <QObject>
#include <QTcpSocket>
#include "TextMessage.h"
#include "txtmsgassembler.h"
#include "TxtMsgHandler.h"

class ClientDemo : public QObject
{
    Q_OBJECT

    QTcpSocket m_client;            //客户端对象
    TxtMsgAssembler m_assembler;    //协议消息装配器
    TxtMsgHandler* m_handler;       //接口函数
protected slots:
    void onConnected();             //连接事件槽函数
    void onDisconnected();          //断连事件槽函数
    void onDataReady();             //消息事件槽函数
    void onBytesWritten(qint64 bytes);

public:
    ClientDemo(QObject* parent = NULL);
    bool connectTo(QString ip, int port);   //连接服务器
    qint64 send(TextMessage& message);      //发送协议消息
    qint64 available();                     //查看缓冲区数据个数
    void setHandler(TxtMsgHandler* handler);
    bool isValid();                         //判断客户端状态
    void close();                           //关闭客户端
};

#endif // CLIENTDEMO_H

client.cpp

#include "ClientDemo.h"
#include <QHostAddress>
#include <QDebug>

//信号与槽的映射
ClientDemo::ClientDemo(QObject* parent) : QObject(parent), m_handler(nullptr)
{
    connect(&m_client, SIGNAL(connected()), this, SLOT(onConnected()));
    connect(&m_client, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
    connect(&m_client, SIGNAL(readyRead()), this, SLOT(onDataReady()));
    connect(&m_client, SIGNAL(bytesWritten(qint64)), this, SLOT(onBytesWritten(qint64)));
}

//连接信号的槽函数
void ClientDemo::onConnected()
{
    if( m_handler != nullptr )
    {
        TextMessage conn("CONN", m_client.peerAddress().toString() + ":" + QString::number(m_client.peerPort()));

        m_handler->handle(m_client, conn);
    }
}

//断开连接的槽函数
void ClientDemo::onDisconnected()
{
    m_assembler.reset();    //重置装配类对象

    if( m_handler != nullptr )
    {
        TextMessage dscn("DSCN", "");

        m_handler->handle(m_client, dscn);
    }
}

//接收数据的槽函数
void ClientDemo::onDataReady()
{
    char buf[256] = {0};
    int len = 0;

    //循环读取缓冲区中的字节流数据
    while( (len = m_client.read(buf, sizeof(buf))) > 0 )
    {

        QSharedPointer<TextMessage> ptm = nullptr;

        m_assembler.prepare(buf, len);  //加入存储容器

        while( (ptm = m_assembler.assemble()) != nullptr )//循环装配协议文本,直到没有文本可以装配
        {
            if( m_handler != nullptr )
            {
                m_handler->handle(m_client, *ptm);
            }
        }
    }
}

void ClientDemo::onBytesWritten(qint64 bytes)
{
    (void)bytes;
}

//同步的方式连接服务端
bool ClientDemo::connectTo(QString ip, int port)
{
    m_client.connectToHost(ip, port);
    return m_client.waitForConnected();
}

//以协议消息为单位发送数据
qint64 ClientDemo::send(TextMessage& message)
{
    QByteArray ba = message.serialize();

    return m_client.write(ba.data(), ba.length());
}

//查看缓冲区的数据长度
qint64 ClientDemo::available()
{
    return m_client.bytesAvailable();
}

void ClientDemo::setHandler(TxtMsgHandler *handler)
{
    m_handler = handler;
}

//查看客户端状态
bool ClientDemo::isValid()
{
    return m_client.isValid();
}

//关闭客户端
void ClientDemo::close()
{
    m_client.close();
}

client_main.cpp

#include "MainwinUI.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWin w;
    w.show();
    return a.exec();
}

阶段性效果图

在这里插入图片描述

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

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

相关文章

最全MacBook选购指南 | 看完你就知道怎么买

最全MacBook选购指南 | 看完你就知道怎么买 作为MacBook的老用户大大小小的型号也都用了不少 那这么多台MacBook到底怎么选呢&#x1f4a1; . ☑️M1和Intel的MacBook有什么差别呢&#xff1f; 下半年苹果发布的两款MacBook都是苹果自研的芯片M1。在此之前苹果一直用的都是Inte…

解决报错:模块“react-redux“没有导出的成员“TypedUseSelectorHook”

在react整合typescript,redux时&#xff0c;写hook.ts时报这个错&#xff1a;模块"react-redux"没有导出的成员“TypedUseSelectorHook” 现象如下&#xff1a; 原因&#xff1a;react-redux版本太低&#xff0c;至少要升级到7.2.3以后才能包含TypedUseSelectorHook…

Clion中使用C/C++开发stm32程序

前言 从刚开始学习阶段&#xff0c;一直是用的keil5开发stm32程序&#xff0c;自从看到稚晖君推荐的CLion开发嵌入式程序后&#xff0c;这次尝试在CLion上开发stm32程序。 1、配置CLion用于STM32开发的环境 这里我就不详细写了&#xff0c;没必要重新写&#xff0c;网上教程很多…

【Java项目推荐之黑马头条】自媒体文章实现异步上下架(使用Kafka中间件实现)

自媒体文章上下架功能完成 需求分析 流程说明 接口定义 说明接口路径/api/v1/news/down_or_up请求方式POST参数DTO响应结果ResponseResult DTO Data public class WmNewsDto {private Integer id;/*** 是否上架 0 下架 1 上架*/private Short enable;}ResponseResult 自媒…

哈希/散列--哈希表[思想到结构][==完结版==]

文章目录 1.何为哈希?1.1百度搜索1.2自身理解1.3哈希方法/散列方法1.4哈希冲突/哈希碰撞1.5如何解决?哈希函数的设计 2.闭散列和开散列2.1闭散列/开放定址法2.2开散列/链地址法/开链法1.概念2.容量问题3.字符串问题4.开散列性能测试5.开散列与闭散列比较 3.代码实现[配备详细…

【Luckfox pico入门记录(一)】开发环境与工具链

写在前面 最近刷bilibili发现微雪电子关于luckyfox pico的介绍视频&#xff0c;感叹linux开发板居然可以把价格缩到100RMB以内&#xff0c;也正巧结束了复旦微比赛&#xff0c;受够了FM33LC046N的低性能&#xff0c;来玩点便宜又高性能的板子。   开发板型号&#xff1a;luck…

【JavaEE】_构造HTTP请求与HTTPS

目录 1. 构造HTTP请求 1.1 form标签构造HTTP请求 1.1.1 form标签构造GET请求 1.1.2 form标签构造POST请求 1.2 通过ajax构造HTTP请求 1.3 form与ajax 1.4 使用ajax构造HTTP请求 2.HTTPS 2.1 对称加密 2.2 非对称加密 2.3 证书 1. 构造HTTP请求 1.1 form标签构造HTT…

软考高项-第五章:信息系统工程

重点知识点&#xff1a; 以上总结&#xff0c;仅供参考。

MacBook内存空间不够该怎么办?

随着使用时间的增长&#xff0c;我们会发现Macbook电脑的存储空间越来越少&#xff0c;这时候我们就需要对Mac电脑进行清理&#xff0c;以释放更多的存储空间。那么&#xff0c;Mac空间不足怎么解决呢&#xff1f; 1.清理垃圾文件 Mac空间不足怎么解决&#xff1f;首先要做的就…

C#和Excel文件的读写交互

C#和Excel文件的读写交互是一项重要的技术&#xff0c;在许多应用程序开发中起着关键作用。C#作为一种现代的面向编程语言&#xff0c;提供了丰富的库和功能&#xff0c;使开发人员能够轻松地处理Excel文件&#xff0c;并进行数据的读取和写入。 首先&#xff0c;让我们了解一下…

matlab矩阵计算

1.a&#xff08;10,6&#xff09;&#xff0c;b&#xff08;6,5&#xff09;&#xff0c;那么a*b得到的是10行5列。第一个的数组的行数等于第二个数组的列数。得到的矩阵是第一个矩阵的行数和第二个矩阵的列数。 2.矩阵的除法&#xff0c;符号为“\”&#xff0c;这时就需要前一…

单调栈介绍和使用

前言&#xff1a; 今天来讲一下单调栈&#xff0c;它定义是非常简单的&#xff0c;首先栈是一种先进后出、后进先出的数据结构。而单调栈&#xff0c;就是说栈中的元素是严格单调递增或者递减的。它主要用来解决的问题&#xff1a;找到前一个或者后一个的最大或者最小元素。属…

力扣 -- 446. 等差数列划分 II - 子序列

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int numberOfArithmeticSlices(vector<int>& nums) {int nnums.size();//把元素和它对应的所有下标绑定存放到哈希表中unordered_map<double,vector<int>> hash;for(int i0;i<n;…

Day-08 基于 Docker安装 Nginx 镜像-反向代理

此时静态页面网站已经部署上了&#xff0c;但是还是会显示一个端口8080出来&#xff0c;就十分不美观&#xff01; ps: 怎么把端口干掉呢&#xff1f;换成XXXXX.com/demo1 或者 XXXXX.com/demo2这种效果呢&#xff1f; eg: 可以使用 Nginx的反向代理实现&#xff01; 1.开始反…

【算法速查】万字图解带你快速入门八大排序(下)

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;首先在这里祝大家中秋国庆双节同乐&#xff01;&#xff01;抓住假期的小尾巴&#xff0c;今天来把算法速查的八大排序的后续写完&#xff0c;当…

Go 复合数据类型之结构体与自定义类型

Go 复合数据类型之结构体与自定义类型 文章目录 Go 复合数据类型之结构体与自定义类型一、类型别名和自定义类型1.1 类型定义&#xff08;Type Definition&#xff09;简单示例 1.2 类型别名简单示例 1.3 类型定义和类型别名的区别 二、结构体2.1 结构体介绍2.2 结构体的定义2.…

MySQL视图、用户管理

目录 视图概念和操作 视图基本操作 视图规则和限制 用户管理 用户 视图概念和操作 什么是视图&#xff1f; 视图是一个虚拟表&#xff0c;由一个或多个基本表的查询结果组成&#xff08;视图是存储在数据库中的查询的SQL 语句&#xff0c;不在数据库中以存储的数据值集形式…

VC6 MFC Dialog as apllication 编程

MFC框架虽然古老&#xff0c;但编程还是方便多了&#xff0c;在操控界面控件的基础上&#xff0c;平时在Console模式习练的类可以融入到MFC中&#xff0c;开发应用程序还是比较方便的。下图是习练程序的界面。 说明&#xff1a; 一个EDIT框&#xff0c;一个Label框&#xff1b…

Linux进程相关管理(ps、top、kill)

目录 一、概念 二、查看进程 1、ps命令查看进程 1&#xff09;ps显示某个时间点的程序运行情况 2&#xff09;查看指定的进程信息 2、top命令查看进程 1&#xff09;信息统计区&#xff1a; 2&#xff09;进程信息区 3&#xff09;交互式命令 三、信号控制进程 四、…