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

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();
}

阶段性效果图

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值