一、客户端涉及到UI界面的跳转和回显
代码实现思路:
1.1、界面跳转通过信号槽实现,登录界面完成后,发送信号给主界面,同时主界面通过信号函数获取服务端信息,从而建立socket,实现网络通信;
1.2、数据库记录现有用户列表,并建立对应的UI索引,因为每次在主界面双击时,就要弹出新的UI以实现多对多聊天;
1.3、主界面控制socket的收发,socket收发后依据字符流控制消息流向哪个打开UI界面,实现多对多聊天消息分发及隔离;
1.4、运行后的效果如图:
图1.登录界面 图2.主界面
图3.聊天界面
二、代码如下:
salite记录数据相关文件:
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);//查找指定用户数据
QString* sqlite_search_IP(QString name_s);//查找制定用户的IP地址
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对象,以达到准确转发消息
~server_sqlite()
{
my_db.close();
}
};
#endif
登录UI的代码实现:
cpp代码实现:
#include "my_login.h"
#include "ui_my_login.h"
#include
My_Login::My_Login(QWidget *parent) :
QDialog(parent),
ui(new Ui::My_Login)
{
ui->setupUi(this);
this->setFixedSize(318,264);//设置窗体大小固定
this->setWindowTitle("登陆到服务器");
Img=QImage(":/bk_login");
//点击响应
connect(ui->pushButton_login,SIGNAL(clicked(bool)),this,SLOT(slot_send()));
}
My_Login::~My_Login()
{
delete ui;
}
void My_Login::slot_send()//发送信息到主窗体(信号槽)
{
if(ui->lineEdit_username->text().isEmpty() || ui->lineEdit_IPAddr->text().isEmpty() || ui->lineEdit_IP_Port->text().isEmpty())
{
QMessageBox::warning(this,"警告","请输入完整登陆信息后再登陆!",QMessageBox::Ok,QMessageBox::Cancel);
}
else
{
QString send_info;
send_info+=ui->lineEdit_username->text()+":"+ui->lineEdit_IPAddr->text()+":"+ui->lineEdit_IP_Port->text();
emit send_data(send_info);
}
}
void My_Login::paintEvent(QPaintEvent *event)//绘制背景
{
QPainter pt(this); //定义一个在当前窗口画画的画家
QImage drawing = Img.scaled(this->width(),this->height());
pt.drawImage(0,0,drawing,0,0,this->width(),this->height());
update();
}
H文件:
#ifndef MY_LOGIN_H
#define MY_LOGIN_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace Ui {
class My_Login;
}
class My_Login : public QDialog
{
Q_OBJECT
public:
explicit My_Login(QWidget *parent = 0);
void paintEvent(QPaintEvent *event);
~My_Login();
private:
Ui::My_Login *ui;
QImage Img;
signals:
void send_data(QString);
private slots:
void slot_send();
};
#endif // MY_LOGIN_H
聊天界面的实现:
cpp文件的实现:
#include "my_chart.h"
#include "ui_my_chart.h"
#include
#include
#include
#include
#include
My_Chart::My_Chart(QWidget *parent) :
QDialog(parent),
ui(new Ui::My_Chart)
{
ui->setupUi(this);
this->setFixedSize(653,441);
ui->textEdit_show_message->setReadOnly(true);
//点击响应
connect(ui->pushButton_send,SIGNAL(clicked(bool)),this,SLOT(slot_send_message()));
}
My_Chart::~My_Chart()
{
delete ui;
}
void My_Chart::setIP_min(QString res)//显示本机IP地址和聊天对象的IP地址
{
QStringList analysis=res.split(";");
ui->label_show_mine->setText(analysis.at(0));//显示本机IP地址
ui->label_show_other_side->setText(analysis.at(1));//显示对方的IP地址
}
void My_Chart::show_message(QString src)//接收主窗体的字符串并显示到聊天界面上去
{
QStringList analysis=src.split(":");//依据符号对原始字符串进行分割
ui->textEdit_show_message->append(analysis.at(0)+":\n"+analysis.at(1));//将对方的消息内容显示在窗体上
}
void My_Chart::set_names_client(QString src)//传送用户姓名和接收方姓名
{
QStringList analysis=src.split(":");
name_local=analysis.at(0);
username=analysis.at(1);//设置用户名
}
void My_Chart::slot_send_message()//发送消息
{
QString mess="message:";//消息拼接
QString temp;//取出聊天内容
temp.clear();
QTextStream out(&temp);//从textEdit中读取内容
out << ui->textEdit_input_message->toPlainText();//保存成为html格式//保存到文件中
//qDebug()<<"对话框中的内容"<textEdit_input_message->clear();//清除输入框中的字符
QByteArray message;
QDataStream out(&message,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_7);
out<write(message);
qDebug()<<"消息已发出!";
ui->textEdit_show_message->append(name_local + ":\n" + temp);
}
}
void My_Chart::set_Socket_local(QTcpSocket *Socket_src)
{
Socket_login=Socket_src;
}
void My_Chart::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawPixmap(0,0,width(), height(),QPixmap(":/10.jpg"));
}
H文件实现:
#ifndef MY_CHART_H
#define MY_CHART_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace Ui {
class My_Chart;
}
class My_Chart : public QDialog
{
Q_OBJECT
public:
explicit My_Chart(QWidget *parent = 0);
void setIP_min(QString res);//显示本地IP地址(测试用)
void show_message(QString src);//接收主窗体的字符串并显示到聊天界面上去
void set_names_client(QString src);//传送聊天双方的名字
void set_Socket_local(QTcpSocket *Socket_src);//将Socket传送过来,在聊天窗口实现发送消息,减轻主窗体的网络负担
void paintEvent(QPaintEvent *event);
~My_Chart();
/*
signals:
void send_message(QString);
*/
private slots:
void slot_send_message();//发送消息到一个指定的用户(指定用户为打开的聊天界面是和谁在聊天)
private:
Ui::My_Chart *ui;
QString username;//聊天对像的名字(用于发送信息)
QString name_local;//本家姓名(发起聊天的我的用户名)
QTcpSocket *Socket_login;//接受主窗体的网络文件描述符
};
#endif // MY_CHART_H
主界面的代码实现:
cpp文件实现:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include
MainWindow::MainWindow(QWidget *parent) :
QWidget(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
user_count=2;
current_user=0;
this->setFixedSize(252,532);//设置窗体大小固定,数值源自UI设计图形界面
this->setWindowTitle("FeiQ低配版");//设置窗体的名称
get_IP_Address();//获取本机IP地址并显示
//初始化数据库
my_client.sqlite_init();//初始化数据库,为聊天用户建立一个在线用户表单
//控件动作响应
connect(Login_u,SIGNAL(send_data(QString)),this,SLOT(slot_recv(QString)));//登陆窗体动作响应
connect(ui->listWidget_onlineuser_list,SIGNAL(itemDoubleClicked(QListWidgetItem*)),this,SLOT(slot_select_user(QListWidgetItem*)));//双击用户列表动作响应
Login_u->show();//显示登陆界面
}
MainWindow::~MainWindow()//析构函数
{
qDebug()<<"退出聊天程序";
QString mess="exit:"+username_local+":"+IP_Addr;
QByteArray message;
QDataStream out(&message,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_7);
out<write(message);
qDebug()<<"消息已发出!";
delete ui;
}
void MainWindow::slot_recv(QString res)//核对登陆信息并控制窗体切换
{
QStringList analysis=res.split(":");
Img=QImage(":/bk_Main.png");
Server_IP=analysis.at(1);
QString Port_server=analysis.at(2);
Socket_login=new QTcpSocket(this);
Socket_login->abort();
//qDebug()<<"服务器IP地址:"<connectToHost(Server_IP,Port_server.toInt());//连接到服务器,connectToHost函数为虚函数,无返回值
connect(Socket_login,SIGNAL(readyRead()),this,SLOT(slot_recv_Message()));//数据读取响应连接到读取函数
if(!Socket_login->waitForConnected(10000))
{
QMessageBox::warning(Login_u,"网络连接错误","未连接到网络,请稍后重试!",QMessageBox::Ok,QMessageBox::Cancel);
}
else
{
this->show();
//设置用户头像(label控件)
Login_u->close();//关闭登陆界面
ui->label_show_username->setText(analysis.at(0));
username_local=analysis.at(0);
QString string="user:"+username_local+":"+IP_Addr;//拼接消息为格式:user:uesrname:IPAddr
QByteArray message;
QDataStream out(&message,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_7);
out<write(message);
}
}
void MainWindow::slot_select_user(QListWidgetItem *src)//双击用户名弹出聊天界面
{
QString temp=src->text();//获取当前双击用户的名字
QString *IP_temp=my_client.sqlite_search_IP(temp);
int ID=my_client.sqlite_search_id(temp);//在数据库中查找对应的ID号
if(-1 != ID)
{
Chart_u[ID].setWindowTitle("和"+temp+"聊天中");//依据ID号建立一个子窗口
Chart_u[ID].setIP_min(IP_Addr+";"+*IP_temp);//显示聊天界面上的IP地址
Chart_u[ID].set_names_client(username_local+":"+temp);//传送聊天双方的名字到聊天窗口
Chart_u[ID].show();//显示子窗体
Chart_u[ID].set_Socket_local(Socket_login);//传送文件描述符,以便窗体发送消息
}
else
{
qDebug()<<"ID号查询失败,请检查数据库文件!!";
}
}
void MainWindow::get_IP_Address()//获取本机IP地址并解析IPV4地址(源自网络)
{
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->label_showIPAddr->setText(address.toString());
IP_Addr=address.toString();
}
}
//qDebug()<<"IPv4地址:"<width(),this->height());
pt.drawImage(0,0,drawing,0,0,this->width(),this->height());
update();
}
void MainWindow::slot_recv_Message()//接受数据响应函数
{
QDataStream in(Socket_login);//定义数据流
in.setVersion(QDataStream::Qt_5_7);//规定QT网络版本
QString recv_Message;
in >> recv_Message;
if(recv_Message.isEmpty())
{
QMessageBox::warning(this,"警告","网络错误,请检查网络连接!",QMessageBox::Ok,QMessageBox::Cancel);//如果,没有接收到信息,则网络出错,没有数据
}
else
network_server(recv_Message);//接受到信息,则进入字符分解程序
}
void MainWindow::network_server(QString src)//网络服务函数
{
QMap map;//以QMap形式将字符串与整型一一对应
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("",888);//无数据时的错误码
QStringList info = src.split(":");//收到的字符串以“:”为分割标志对其进行分解,并存入字符数组(列表)中,以便后期使用
//user:kk;mm;:192.168.132.108;192.168.1.140; 传输用户列表
//message:username:Hello There! 传输消息
//file:username:filename 传输文件
//qDebug() << "类型:" << info[0]<< " 对方姓名" << info[1]<< " 内容:" << info[2];
switch(map[info.at(0)])
{
case 0:
refresh_userinfo(info.at(1)+":"+info.at(2));//刷新用户列表信息(依据服务器发送信息刷新,无需本地定时)
break;
//接收到服务器转发的消息,消息类型:message:username:Hello There!!
case 1:
qDebug()<<"接收到服务器的消息:"<=1&&IP_dis.length()>=1)
{
QString name_client;//临时变量存放用户名
QIcon user_list_logo(":/user_list.png");
ui->listWidget_onlineuser_list->clear();//清除之前的用户列表
for(int i=0;ilistWidget_onlineuser_list->addItem(temp_list);//加入列表显示
}
}
}
}
//qDebug()<<"在线用户信息刷新完成!!";
}
void MainWindow::splite_message(QString src)//将从服务器接受的消息依据接收用户名分发到各自的窗口上
{
qDebug()<<"分发消息服务函数:"<
H文件的实现:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
//主窗体用户列表使用
#include
#include"my_chart.h"//添加聊天窗体类
#include"my_login.h"//添加登陆窗体类
//添加网络类
#include
#include
#include
#include
//创建套接字使用
#include
#include
//解析字符串使用
#include
//绘制背景图片以及头像使用
#include
#include
#include
#include
#include
#include"server_sqlite.h"//自定义sqlite控制类
namespace Ui {
class MainWindow;
}
class MainWindow : public QWidget
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
void get_IP_Address();
void paintEvent(QPaintEvent *event);
void network_server(QString src);//用于分析字符串并作出相应的响应
void refresh_userinfo(QString src);//该函数用于刷新在线用户信息
void splite_message(QString src);//将从服务器接受的消息依据接收用户名分发到各自的窗口上(由于可以同时和多人聊天,故该部分很必要)
~MainWindow();
private slots:
void slot_recv(QString res);
void slot_select_user(QListWidgetItem *src);
void slot_recv_Message();
private:
Ui::MainWindow *ui;
server_sqlite my_client;//定义一个数据库对象
int user_count,current_user;//用于记录用户数量和当前打开窗口数
QTcpSocket *Socket_login;//用于登陆和聊天的文件描述符
My_Login *Login_u=new My_Login;//登录界面对象,仅显示一个
My_Chart Chart_u[10];//聊天界面子窗口(最多打开10个)
QString IP_Addr;//本机IP地址
QString Server_IP;//服务器IP地址
QImage Img;//背景图片
QPicture *pix;//图像图片
QString username_local;
};
#endif // MAINWINDOW_H
main.c实现
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
//w.show();
return a.exec();
}
UI界面过多,参考图片吧!