利用Qt写的局域网聊天工具(服务端)

一、局域网聊天工具

有FeiQ大佬坐镇,也只是写着玩玩

软件运行截图:

利用Qt写的局域网聊天工具(服务端)_第1张图片 利用Qt写的局域网聊天工具(服务端)_第2张图片

文件:服务端运行时会在D盘产生一个数据库文件:D:\\user_info_server.db

二、源码:

sqlite部分源码:

cpp文件内容:

/*--------------------------------------------------------
* Author      : Firdin
* E-mail      : [email protected]
* File Name   : server_sqlite.c
* Created Time: 2017年10月12日 星期四 15时27分11秒
* Introduction:
* 
*-------------------------------------------------------*/

#include "server_sqlite.h"
#include
#include

bool server_sqlite::sqlite_init()
{
    if(QSqlDatabase::contains("qt_sql_default_connection"))
        my_db=QSqlDatabase::database("qt_sql_default_connection");
    else
        my_db=QSqlDatabase::addDatabase("QSQLITE");//创建sqlite数据库文件
    if(!my_db.isValid())
    {
        qDebug()<<"数据库创建失败"<

H文件内容如下:


#ifndef server_sqlite_H
#define server_sqlite_H

#include 
#include 
#include 
#include 
#include 

class server_sqlite //无继承的类(独立数据库操作类)
{

private:
    QSqlDatabase my_db;//用户数据表单建立
    int count;//用户在线人数
    QString temp;//用于拼接字符用的临时变量
    QString output;//用于输出用户信息
public:
    explicit server_sqlite()
    {
        count=0;
        temp.clear();
    }
    bool sqlite_init();//初始化数据库用成员函数
    bool sqlite_insert(int id_s,QString name_t,QString  IP_t);//插入数据
    bool sqlite_delete(QString name_m);//删除一条数据
    bool sqlite_delete_all();//删除所有的数据
    QString* sqlite_search(QString name_s);//查找指定用户数据
    bool sqlite_update(QString name_u,QString state_u);//修改指定用户的在线与否信息
    QString* sqlite_online_server();//输出在线用户信息,通过判断state是否是online
    QString* sqlite_offline_server();//输出历史用户(不在线但是有其信息)
    bool sqlite_delete_table();//删除表单(表单为临时配置文件,因此可以删除)
    int sqlite_search_id(QString name_src);//依据名字查询ID号,ID号在转发数据时需要用于查找QList中的socket对象,以达到准确转发消息
    bool sqlite_is_Empty();//判断当前数据库是否为空
    bool sqlite_is_Exist(QString name_se, QString IP_se);//判断当前用户是否已经存在,存在则仅修改其在线状态,否则将插入到数据库中,作为新加成员
     ~server_sqlite()
    {
        my_db.close();
    }
};
#endif

图形界面代码实现:

cpp代码:

#include "widget.h"
#include "ui_widget.h"
#include"server_sqlite.h"//自定义sqlite服务

#include 
#include 

#include//时间
#include//日期
#include//数据流
#include//字节数组

#include//将字符串和整型匹配

server_sqlite server;//定义一个服务数据库对象(主函数中也用这个对象)

Widget::Widget(QWidget *parent) :QWidget(parent),
ui(new Ui::Widget)
{
    ui->setupUi(this);
//显示时间:
    QString temp1=QTime::currentTime().toString();
    QString temp2=QDate::currentDate().toString("yyyy年MM月dd日  ");
    QString show=temp2+" "+temp1;
    ui->local_Time->setText(show);
//定时器
    this->startTimer(1000);//用于刷新时间的定时器,ID=1
    this->startTimer(3000);//间隔3秒刷新用户在线信息,ID=2
//固定窗体大小
    this->setFixedSize(523,454);//窗口大小固定
    state=true;//标识服务状态,true表明服务已打开
    countor=0;//数据库用户数量计数
    ui->textEdit_showuseringo->setReadOnly(true);//设为只读
    ui->textEdit_showuser_cominication->setReadOnly(true);//设为只读
    connect(ui->pushButton,SIGNAL(clicked(bool)),this,SLOT(slot_net_server()));//打开、关闭按钮动作连接
}

Widget::~Widget()
{
    server.sqlite_delete_all();
    server.sqlite_delete_table();//退出时删除所有数据库文件
    qDebug()<<"退出服务器程序!!";
    delete ui;
}
//设置背景图案
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter (this);
    painter.drawPixmap(0,0,this->width(), this->height(),QPixmap(":/blue.jpg"));
}

void Widget::slot_net_server()//按钮响应函数,用于打开网络或关闭网络
{
    if(state)//按钮切换及功能实现
    {
        state=false;
        ui->pushButton->setText("关闭服务");
        get_IP_Address();//获取本机IP地址并显示在label控件中
        Server_t=new QTcpServer(this);//创建服务器
        Socket_t=new QTcpSocket(this);//创建socket
        Socket_List=new QList;//创建socket文件描述符集合
        Server_t->listen(QHostAddress::Any,8080);//接收来自任意网卡的信息
        connect(Server_t,SIGNAL(newConnection()),this,SLOT(slot_new_connect()));//将新链接连接到槽函数响应
        ui->label_show_message->setText("服务已打开!");
    }
    else
    {
        state=true;
        ui->pushButton->setText("打开服务");

        Server_t->close();
        for(int i=0;ilength();i++)//关闭服务,关闭所有Socket
        {
            Socket_List->at(i)->close();
        }
        ui->label_show_message->setText("服务已关闭!!");
        ui->local_IP_2->clear();
        ui->textEdit_showuser_cominication->clear();
        ui->textEdit_showuseringo->clear();
    }
}

void Widget::get_IP_Address()//获取本机IP地址并解析IPV4地址(源自网络)
{
    QString temp;
    QHostInfo info = QHostInfo::fromName(QHostInfo::localHostName());//首先获取本机名称,其次依据本机名称获的本机IP地址
    info.addresses();//QHostInfo的address函数获取本机ip地址
    //存在多条ip地址ipv4和ipv6:
    foreach(QHostAddress address,info.addresses())//该语句为从集合中每个成员中进行筛选:for each member进行遍历
    {
            if(address.protocol()==QAbstractSocket::IPv4Protocol)//只取ipv4协议的地址
            {
                ui->local_IP_2->setText("IP:"+address.toString()+"  Port: 8080");
                temp=address.toString();
            }
    }
    qDebug()<<"IPv4地址:"<timerId();
    if(1 == event->timerId())//定时器ID为1时(1s溢出),刷新时间
    {
        QString temp1=QTime::currentTime().toString();
        QString temp2=QDate::currentDate().toString("yyyy年MM月dd日  ");
        QString show=temp2+" "+temp1;
        ui->local_Time->setText(show);
        display_userinfo();
    }
    else if(2 == event->timerId() && 0 != countor)//如果刷新用户列表的定时器溢出,则发送用户列表给所有的用户
    {
        ui->label_show_message->clear();
        if(0 == Socket_List->length())
        {
            qDebug()<<"当前没有用户,不需要刷新用户列表";
        }
        else
        {
            QString user_info;
            QString user_name;
            QString user_IP;
            user_info=*server.sqlite_online_server();//获取在线用户信息
            if(""!=user_info)//有在线用户信息则进行分发
            {
                QStringList analysis=user_info.split("\n");//分解字符串
                for(int i=0;ilength();i++)//遍历发送数据(每个用户都要刷新在线列表)
                {
                    QByteArray message;
                    QDataStream out(&message,QIODevice::WriteOnly);
                    out.setVersion(QDataStream::Qt_5_7);
                    out<at(i)->write(message);//从网络发送用户列表(所有在线用户都会接收到消息)
                }
            }
        }
    }
    else
         ui->label_show_message->setText("等待用户连接!");
}

void Widget::slot_new_connect()
{
    Socket_t=Server_t->nextPendingConnection();//建立套接字
    Socket_List->append(Socket_t);//加入到描述符集合
    id_src=Socket_List->length();//取得当前描述符集合最大数值,作为数据库插入的ID (最大数值依据连接用户改变)

    connect(Socket_t,SIGNAL(readyRead()),this,SLOT(slot_readMesssage()));//将所有的socket都连接到slot_readMessage(),而后再遍历取出数据
}

void Widget::slot_readMesssage()//该函数在任意一个描述符出现readyread信号后均能进入,因此需要遍历描述符集合
{
    QString recv;
    qDebug()<<"接收到信息,正在遍历服务器文件描述符集合查找信息";
    for(int i=0;ilength();i++)//遍历Socket_List中所有的描述符,若其没有数据,则跳过,直到找到数据
    {
        QDataStream in(Socket_List->at(i));
        in.setVersion(QDataStream::Qt_5_7);//定义版本,以免出错
        in>>recv;
        if(!(recv.isEmpty()))//若取得的数据不为空,说明是该描述符发出的readyread信号
        {
            String_Splitor(recv);//将接收到的字符串进行解析并进入下一步操作
        }
    }
}

 void Widget::send_Message(QString user_src,QString message_src)//该成员函数仅用于发送消息
 {
     QByteArray message;//临时byte数组
     QDataStream out(&message,QIODevice::WriteOnly);//数据流向
     out.setVersion(QDataStream::Qt_5_7);//指定QT版本,以免网络传输出错
     out<at(des-1)->write(message);//发送信息,Socket_List列表中位置与其长度相差1,从0开始,des为数据库查询得到的ID号,其为Socket_List的length()返回值
 }

void Widget::String_Splitor(QString src)
{
    QStringList analysis=src.split(":");
    qDebug()<<"字符解析结果:"<发送消息,user->注册服务,file->发送文件,video->发送视频(switch不支持字符分支)
    QMap map;//利用Map容器将字符串与整形数字对应,可以高效使用switch语句
//匹配字符与整型,等价于 if("user"==analysis.at(0))
                                                                 //int index=0;
    map.insert("user",0);
    map.insert("message",1);//接收到消息<互相发送消息>

    map.insert("file",2);//接收到除音乐、图片、视频外的文件

//由于多媒体文件可以预览,因此将其与其他文件分开,同时压缩文件也应该与其他的分开,以下功能以后再实现
    map.insert("video",3);//接收到视频文件
    map.insert("picture",4);//接收到图片文件
    map.insert("music",5);//接收到音乐文件
    map.insert("zip",6);//接收到压缩包文件
    map.insert("dir",7);//接收到带有目录的文件

    map.insert("exit",666);//退出客户端发送的下线消息

    map.insert("",888);

    switch(map[analysis.at(0)])//查找键值取出对应的整型数
    {
    case 0://user:username:192.168.1.168
      {
        qDebug()<<"进入分支user";
        QString name_m=analysis.at(1);
        QString IP_m=analysis.at(2);//取出新加入用户的名字和IP地址

        QString show_in;//用于在UI上显示某人加入聊天队列的信息
        show_in.sprintf("用户%s加入聊天队列,IP为:%s",name_m.toStdString().data(),IP_m.toStdString().data());
        ui->textEdit_showuser_cominication->append(show_in);//将新连接的用户追加显示在左侧textEdit上

        ui->label_show_message->setText("有新用户连接!");//提示有新用户连接进来
        qDebug()<<"正在插入数据库";
        server.sqlite_insert(id_src,name_m,IP_m);//新用户的信息插入到数据库文件中
        qDebug()<<"插入数据库完成";
        countor++;
        qDebug()<textEdit_showuser_cominication->append(name_m+message_m);
        break;
     }
    case 2://file:user1|user2:filename.size
        break;
    case 3://video:user1|user2:filename.size
        break;
    case 4://picture:user1|user2:filename.size
        break;
    case 5://music:user1|user2:filename.size
        break;
    case 6://zip:user1|user2:filename.size
        break;
    case 7://dir:user1|user2:filename.size
        break;
    case 666://exit:username:192.168.1.168
    {
        QString name_m=analysis.at(1);//取出退出用户名
        server.sqlite_update(name_m,"offline");//更新在线状态
        QString show_in;//用于在UI上显示某人加入聊天队列的信息
        show_in.sprintf("用户%s下线",name_m.toStdString().data());//拼接字符串
        ui->textEdit_showuser_cominication->append(show_in);//将下线的用户追加显示在左侧textEdit上
    }
        break;
    case 888://内部错误
        qDebug()<<"字符串解析出错!!";
        break;
    default:
        qDebug()<<"指令解析出错,检查网络接收数据!!";
        break;
    }

}

void Widget::display_userinfo()
{
    QString *user_online=server.sqlite_online_server();
    QString *user_offline=server.sqlite_offline_server();
    if(0!=countor&&state)
    {
        ui->textEdit_showuseringo->clear();

        ui->textEdit_showuseringo->append("当前在线用户:\n");
        ui->textEdit_showuseringo->append(*user_online);
        ui->textEdit_showuseringo->append("离线用户:\n");
        ui->textEdit_showuseringo->append(*user_offline);
    }
}

H文件:

#ifndef WIDGET_H
#define WIDGET_H

#include 
//添加网络类
#include 
#include 
#include 
#include 
#include 
#include
#include
#include

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    void timerEvent(QTimerEvent *event);//重写定时器虚函数,依据ID号区分功能
    void get_IP_Address();//获取本机IP地址
    void send_Message(QString user_src,QString message_src);//发送消息到客户端
    void String_Splitor(QString src);//分解字符串实现不同的功能
    void paintEvent(QPaintEvent *event);
    void display_userinfo();
    ~Widget();

private slots:
    void slot_new_connect();//新的连接
    void slot_readMesssage();//接收消息
    void slot_net_server();//网络服务函数

private:
    Ui::Widget *ui;
    QTcpSocket *Socket_t;//用于发送接收数据
    QTcpServer *Server_t;//用于连接
    QList *Socket_List;//用于将socket集合
    bool state;//当前状态
    int id_src;//数据库ID号索引(有bug)
    int countor;//记录用户数量
};

#endif // WIDGET_H

main.c文件

#include "widget.h"
#include 

#include "server_sqlite.h" //自定义数据库操作文件


extern server_sqlite server;

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;

    if(!server.sqlite_init())//初始化一个数据对象
    {
        qDebug()<<"数据表单已存在,请检查!!"<

UI界面的xml代码如下:



 Widget
 
  
   
    0
    0
    523
    454
   
  
  
   Widget
  
  
   
    
     270
     410
     241
     31
    
   
   
    
     楷体
     13
    
   
   
    QFrame::Panel
   
   
    QFrame::Raised
   
   
    
   
  
  
   
    
     270
     60
     241
     31
    
   
   
    
     楷体
     12
    
   
   
    QFrame::Panel
   
   
    QFrame::Raised
   
   
    
   
  
  
   
    
     410
     10
     101
     41
    
   
   
    
     楷体
     13
    
   
   
    打开服务
   
  
  
   
    
     50
     30
     171
     21
    
   
   
    
     楷体
     13
    
   
   
    用户交互信息详情
   
  
  
   
    
     320
     120
     171
     21
    
   
   
    
     楷体
     13
    
   
   
    用户连接情况
   
  
  
   
    
     270
     30
     131
     21
    
   
   
    
     楷体
     13
    
   
   
    服务IP地址
   
  
  
   
    
     10
     420
     251
     21
    
   
   
    
     楷体
     11
    
   
   
    
   
  
  
   
    
     270
     150
     241
     251
    
   
   
    
     楷体
     11
    
   
   
    background-color: qlineargradient(spread:pad, x1:0.523, y1:0.00586364, x2:0.471, y2:1, stop:0 rgba(155, 250, 205, 75), stop:1 rgba(255, 255, 255, 255));
   
  
  
   
    
     10
     60
     241
     341
    
   
   
    
     楷体
     11
    
   
   
    background-color: qlineargradient(spread:pad, x1:0.523, y1:0.00586364, x2:0.471, y2:1, stop:0 rgba(155, 250, 205, 75), stop:1 rgba(255, 255, 255, 255));
   
  
 
 
 
 

       以上代码没有完成文件传输等功能,由于涉及到压缩算法和网络优化,暂时没有算法能力实现,故搁置了,后面算法能力提升后将对该部分进行补充升级。

      不足之处多多指点,多谢。

 

你可能感兴趣的:(Linux学习&开发)