完成客户端首页的所有功能
完成客户端加入直播和创建直播的界面设计
增加json打包函数和json解析函数
增加服务器的开播消息和关闭直播的消息
对于创建直播来说,只需要相应的客户发送开播消息给服务器,然后服务器转发给其他客户端
关闭直播也是如此
另外,在客户端连接之后,要发送当前开播的房间给客户端
对于客户端界面,开播和观看直播的界面不同
开播界面如下(并非最终版本)
观看直播的画面如下
开播只需要之间点击开播即可,开播后发送相应的房间号给其他客户端
点击观看时,需要选择房间号进行观看进一步的内容还没有实现
新增json消息打包和解析
服务器要解析下面的json,打包上面的json
#ifndef MYJSON_H
#define MYJSON_H
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QDebug>
#include <QJsonArray>
#include <QByteArray>
class MyJson
{
public:
MyJson();
//解析用户名和密码
QString name_pswd_info(QString message,QString &pswd,QString &name);
//打包直播刷新消息
QString pack_live_flush(QString action,QStringList name_list);
};
#endif // MYJSON_H
#include "myjson.h"
MyJson::MyJson()
{
}
QString MyJson::name_pswd_info(QString message, QString &pswd, QString &name)
{
QByteArray bytes = message.toUtf8();
QJsonParseError jsonError;
QJsonDocument doucment = QJsonDocument::fromJson(bytes, &jsonError);
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未发生错误
if (doucment.isObject()){
QJsonObject object = doucment.object(); // 转化为对象
if (object.contains("Name")) {
QJsonValue value = object.value("Name");
if (value.isString()) {
name = value.toString();
qDebug() << "Name : " << name;
}
}else{
return "json_error_no_name";
}
if (object.contains("password")) {
QJsonValue value = object.value("password");
if (value.isString()) {
pswd = value.toString();
qDebug() << "pswd : " << pswd;
}
}else{
return "json_error_no_pswd";
}
}else{
return "json is no object";
}
}else{
return "json error";
}
return "success";
}
QString MyJson::pack_live_flush(QString action, QStringList name_list)
{
QJsonObject json;
json.insert("action",action);
QJsonArray json_array;
for (int i = 0; i < name_list.size(); ++i){
json_array.insert(i,name_list.at(i));
}
json.insert("name",json_array);
QJsonDocument document;
document.setObject(json);
QByteArray byteArray = document.toJson(QJsonDocument::Compact);
QString strJson(byteArray);
return strJson;
}
精简了解析函数,新增了对接收到消息的处理
#include "tcpsocket.h"
TcpSocket::TcpSocket(QObject *parent):
QTcpSocket(parent)
{
mydb = dbhelper::getIntance(NULL);
mylog = MyLog::getInstance(NULL);
//本来想要在这里发送连接的信号给服务器的,结果发现在他状态改变的时候还读取不到端口号和ip地址,所以就放在房间socket的地方去发送消息了
// connect(this,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(dataconnected()));
//连接断线函数/读取函数和自定义的槽函数
connect(this,SIGNAL(disconnected()),this,SLOT(datadisconnected()));
connect(this,SIGNAL(readyRead()),this,SLOT(dataReceived()));
}
QString TcpSocket::getName()
{
return name;
}
int TcpSocket::getlive_state()
{
return live_state;
}
void TcpSocket::setlive_state(int state)
{
live_state = state;
}
void TcpSocket::msgPrase(QString msg_buf)
{
int item;
QString buf;
buf.clear();
item = msg_buf.section('|',0,0).trimmed().toInt();
int item_len = QString::number(item).length();
// qDebug()<<"len"<
buf = msg_buf.right(msg_buf.length()-item_len-1);
// qDebug()<<"item"<-
//把它转换成消息发送给server
MyMessage message((MsgId)item,buf,buf.length());
if(message.isInEnum()){
if(message.getmsgid() == MSG_CLIENT_CLOSE || message.getmsgid() == MSG_CLITEN_CONNECT){
emit messageToServer(message); //发射消息信号
}else if(message.getmsgid() == MSG_LOGIC){
QString ret = prase_MSG_LOGIC(message.getmsgbuf());
message.setmsgbuf(ret);
qDebug()<<message.toString();
this->write(message.toString().toLatin1(),message.toString().length()+1);
this->flush();
if(ret == "success"){
emit logicToServer(this->peerName()); //发射消息信号
qDebug()<<" emit logicToServer(this->peerName())"<<this->peerName();
}
}else if(message.getmsgid() == MSG_REGISTER){
message.setmsgbuf(prase_MSG_REG(message.getmsgbuf()));
this->write(message.toString().toLatin1(),message.toString().length()+1);
this->flush();
}else if(message.getmsgid() == MSG_SHAKE_HAND){//握手信号
message.setmsgbuf(" ");
this->write(message.toString().toLatin1(),message.toString().length()+1);
this->flush();
}else if(message.getmsgid() == MSG_CREATE_LIVE){//创建直播
live_state = 1;
emit messageToServer(message);
}else if(message.getmsgid() == MSG_CLOSE_LIVE){//关闭直播
live_state = 0;
emit messageToServer(message);
}
}else{
qDebug()<<"无效数据";
}
}
QString TcpSocket::prase_MSG_LOGIC(QString message_buf)
{
QString name;
QString pswd;
QString parse_ret;
parse_ret = myjson.name_pswd_info(message_buf,pswd,name);
if(parse_ret != "success"){
return parse_ret;
}
QString ret = mydb->logic_judge(name,pswd);
if(ret == "success") {
this->setPeerName(name);
MyMessage message(MSG_CLITEN_CONNECT,name,name.length());
QString connectbuf = this->peerName() +" "+this->peerAddress().toString()+" "+QString::number(this->peerPort()) + " connected!!";
mylog->LogWrite(connectbuf);
emit messageToServer(message);
}
return ret;
}
QString TcpSocket::prase_MSG_REG(QString message_buf)
{
QString name;
QString pswd;
QString parse_ret;
parse_ret = myjson.name_pswd_info(message_buf,pswd,name);
if(parse_ret != "success"){
return parse_ret;
}
QString ret;
ret = mydb->user_reg(name,pswd);
return ret;
}
void TcpSocket::datadisconnected()
{
//断开之后会发送-1
qDebug()<<"emit disconnected("<<this->peerName()<<")";
emit disconnected(this->peerName());
}
void TcpSocket::dataReceived()
{
//处理督导的信号
while(this->bytesAvailable() > 0){
char buf[1024] = {'\0'};
int length = bytesAvailable();
this->read(buf,length); //读取接收
msgPrase(buf); //解析收到的数据
}
}
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <QObject>
#include <QtNetwork>
#include <QTcpServer>
#include "tcpsocket.h"
#include "mylog.h"
#include "myjson.h"
class TcpServer : public QTcpServer
{
Q_OBJECT
public:
explicit TcpServer(QObject* parent = 0,int port = 0);
QList<TcpSocket*> tcpSocketList;//用来存储客户端指针的list容器
MyLog *mylog;
MyJson myjson;
protected:
void incomingConnection(int socketDescriptor);//重写了连接函数
signals:
void msgToServer(MyMessage);//用来给main发送消息的信号
public slots:
void msgFromSocket(MyMessage);//用来处理从socket接收到信号的槽函数
void tcpDisconnected(QString);//用来处理客户端断开的槽函数
void tcpLogicSuccess(QString);//用来处理登录成功的信号
};
#endif // TCPSERVER_H
#include "tcpserver.h"
TcpServer::TcpServer(QObject *parent, int port):
QTcpServer(parent)
{
mylog = MyLog::getInstance(NULL);
listen(QHostAddress::AnyIPv4,port); //用来监听ipv4的客户端,port是传进来的
}
void TcpServer::incomingConnection(int socketDescriptor)
{
TcpSocket *tcpSocket = new TcpSocket(this);
tcpSocket->setSocketDescriptor(socketDescriptor);
connect(tcpSocket, SIGNAL(messageToServer(MyMessage)),
this, SLOT(msgFromSocket(MyMessage)));
connect(tcpSocket,SIGNAL(logicToServer(QString)),
this,SLOT(tcpLogicSuccess(QString)));
connect(tcpSocket, SIGNAL(disconnected(QString)),
this, SLOT(tcpDisconnected(QString)));
//把socket指针放到socketlist中
tcpSocketList.append(tcpSocket);
}
void TcpServer::msgFromSocket(MyMessage message)
{
qDebug()<<message.getmsgbuf();
// (||message.getmsgid() == MSG_CLITEN_CONNECT)
if(message.getmsgid() == MSG_CLIENT_CLOSE){
emit msgToServer(message);
return;
}else if(message.getmsgid() == MSG_CLITEN_CONNECT){
emit msgToServer(message);
//给该客户端发送更新的消息
return;
}else if(message.getmsgid() == MSG_CREATE_LIVE){
emit msgToServer(message);
//给其他客户端发送开播消息
for(int i = 0; i < tcpSocketList.count(); i++)
{
QTcpSocket *temp = tcpSocketList.at(i);
if(temp->peerName() != message.getmsgbuf())
{
QStringList name_list;
name_list<<message.getmsgbuf();
QString buf = myjson.pack_live_flush("on",name_list);
MyMessage live_message(MSG_LIVE_FLUSH,buf,buf.length()); //开播消息
temp->write(live_message.toString().toLatin1(), live_message.toString().length()+1);
temp->flush();
qDebug()<<live_message.toString()<<" "<<live_message.toString().length();
continue;
}
}
return;
}else if(message.getmsgid() == MSG_CLOSE_LIVE){
emit msgToServer(message);
//给其他客户端发送下播消息
for(int i = 0; i < tcpSocketList.count(); i++)
{
QTcpSocket *temp = tcpSocketList.at(i);
if(temp->peerName() != message.getmsgbuf())
{
QStringList name_list;
name_list <<message.getmsgbuf();
QString buf = myjson.pack_live_flush("off", name_list);
MyMessage live_message(MSG_LIVE_FLUSH,buf,buf.length()); //开播消息
temp->write(live_message.toString().toLatin1(), live_message.toString().length()+1);
temp->flush();
continue;
}
}
return;
}
}
void TcpServer::tcpDisconnected(QString name)
{
QString disconnectbuf;
//关闭
for(int i = 0; i < tcpSocketList.count(); i++)
{
TcpSocket *temp = tcpSocketList.at(i);
if(temp->peerName() == name)
{
disconnectbuf = temp->peerAddress().toString() + " " + QString::number(temp->peerPort()) + " disconnected!!";
temp->destroyed();
tcpSocketList.removeAt(i);
break;
}
}
//发送该客户端被关闭的消息
QString msgbuf = name;
MyMessage message(MSG_CLIENT_CLOSE,msgbuf,msgbuf.length());
emit msgToServer(message);
disconnectbuf = name+" "+ disconnectbuf;
mylog->LogWrite(disconnectbuf);
return;
}
void TcpServer::tcpLogicSuccess(QString name)
{
QStringList name_list;
for(int i = 0; i < tcpSocketList.count(); i++)
{
QTcpSocket *temp = tcpSocketList.at(i);
if(temp->peerName() == name)
{
for(int j = 0;j<tcpSocketList.count();j++){
TcpSocket *other_temp = tcpSocketList.at(j);
if(other_temp->getlive_state() == 1){
name_list<<other_temp->peerName();
// QString buf = myjson.pack_live_flush("on",other_temp->peerName());
// MyMessage live_message(MSG_LIVE_FLUSH,buf,buf.length()); //开播消息
// temp->write(live_message.toString().toLatin1(), live_message.toString().length()+1);
// temp->waitForBytesWritten(100);
// temp->flush();
// qDebug()<<"to "<peerName()<
}
}
QString buf = myjson.pack_live_flush("on",name_list);
MyMessage live_message(MSG_LIVE_FLUSH,buf,buf.length()); //开播消息
temp->write(live_message.toString().toLatin1(), live_message.toString().length()+1);
temp->flush();
qDebug()<<"to "<<temp->peerName()<<live_message.toString()<<" "<<live_message.toString().length();
return;
}
}
}
enum MsgId{
MSG_START = 0,
MSG_CLITEN_CONNECT, //连接消息
MSG_READ_BYTES, //读取接收到的消息
MSG_CLIENT_CLOSE, //客户端关闭的消息
MSG_LOGIC, //客户端登录消息
MSG_REGISTER,//客户端注册消息
MSG_SHAKE_HAND, //握手
MSG_CREATE_LIVE,//创建直播
MSG_LIVE_FLUSH,//直播列表刷新
MSG_CLOSE_LIVE,//直播间关闭
MSG_END
};
客户端主要是消息的发送,消息的解析 和界面的跳转
#ifndef CLIENTJSON_H
#define CLIENTJSON_H
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonParseError>
#include <QDebug>
#include <QByteArray>
typedef struct live_flush{
QString action;
QStringList name;
}Live_Flush_S;
class ClientJson
{
public:
ClientJson();
//打包账号密码
QString pack_name_pswd(QString name,QString pswd);
//解析直播间刷新消息
QString live_flush_info(QString message_buf,Live_Flush_S &live_msg);
};
#endif // CLIENTJSON_H
#include "clientjson.h"
ClientJson::ClientJson()
{
}
QString ClientJson::pack_name_pswd(QString name, QString pswd)
{
QJsonObject json;
json.insert("Name",name);
json.insert("password",pswd);
QJsonDocument document;
document.setObject(json);
QByteArray byteArray = document.toJson(QJsonDocument::Compact);
QString strJson(byteArray);
return strJson;
}
QString ClientJson::live_flush_info(QString message_buf, Live_Flush_S &live_msg)
{
QByteArray bytes = message_buf.toUtf8();
QJsonParseError jsonError;
QJsonDocument doucment = QJsonDocument::fromJson(bytes, &jsonError);
if (!doucment.isNull() && (jsonError.error == QJsonParseError::NoError)) { // 解析未发生错误
if (doucment.isObject()){
QJsonObject object = doucment.object(); // 转化为对象
if (object.contains("action")) {
QJsonValue value = object.value("action");
if (value.isString()) {
live_msg.action = value.toString();
qDebug() << "action : " << live_msg.action;
}
}else{
return "json_error_no_name";
}
if (object.contains("name")) {
QJsonValue value = object.value("name");
if (value.isArray()) {
QJsonArray array = value.toArray();
int size = array.size();
for (int i = 0; i < size; ++i) {
QJsonValue value = array.at(i);
if (value.isString()) {
live_msg.name<<value.toString();
}
}
qDebug() << "name : " << live_msg.name;
}else{
return "name_json_error";
}
}else{
return "json_error_no_action";
}
}else{
return "json is no object";
}
}else{
return "json error";
}
return "success";
}
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QMessageBox>
#include <QDebug>
#include "tcpthread.h"
#include "mymessage.h"
#include "clientjson.h"
#include "room.h"
#include "liveroom.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void Logic_Init();//登录初始化
void User_Init();//使用界面初始化
void connectToServer(); //用连接服务器
void deleteListRoom(QString);
public slots:
void connect_state(QString); //用来控制连接状态
void connect_success(); //用来判断连接状态
void logic_msg_recv(QString); //用来处理登录消息
void reg_msg_recv(QString); //用来处理注册消息
void liveroom_msg_recv(QString);
void live_flush_recv(QString action,QStringList namelist);//用来处理直播间刷新
void room_msg_recv(QString); //用来观看直播的消息
private slots:
void on_radioshow_clicked(bool checked); //用来显示密码和隐藏密码
void on_pushlogic_clicked();//用来登录
void on_pushquit_clicked();//用来退出
void on_pushregister_clicked();
void on_pushCreate_clicked();
void on_pushAdd_clicked();
signals:
void connect_enable(); //连接
private:
Ui::Widget *ui;
TcpThread *tcpThread;//用来启动socket线程
// bool status;
QString userName; //账号
QString passWord; //密码
int Logic_Flag; //用来判断是登录还是注册0注册1登录
LiveRoom *liveroom;
ClientJson myjson;
Room *room;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QThread>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
tcpThread = nullptr;
room = nullptr;
liveroom = nullptr;
// status = false;
Logic_Init();
QObject::connect(this,SIGNAL(connect_enable()),this,SLOT(connect_success()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::Logic_Init()
{
this->setWindowTitle("登录/注册");
ui->linepswd->show();
ui->lineUser->show();
ui->radioshow->show();
ui->pushlogic->show();
ui->pushregister->show();
ui->linepswd->setEchoMode(QLineEdit::Password);
ui->pushAdd->hide();
ui->pushCreate->hide();
ui->listRoom->hide();
ui->pushquit->hide();
}
void Widget::User_Init()
{
this->setWindowTitle("云木直播平台");
ui->linepswd->hide();
ui->lineUser->hide();
ui->radioshow->hide();
ui->pushlogic->hide();
ui->pushregister->hide();
ui->label->hide();
ui->label_2->hide();
ui->label_3->hide();
ui->pushAdd->show();
ui->pushCreate->show();
ui->listRoom->show();
ui->pushquit->show();
}
void Widget::connectToServer()
{
QString Address_Ip = "127.0.0.1";
int port = 8010;
if(!tcpThread){
tcpThread = new TcpThread;
QObject::connect(tcpThread,SIGNAL(send_tcpmsg(QString)),this,SLOT(connect_state(QString)));
QObject::connect(tcpThread,SIGNAL(send_logic_msg(QString)),this,SLOT(logic_msg_recv(QString)));
QObject::connect(tcpThread,SIGNAL(send_reg_msg(QString)),this,SLOT(reg_msg_recv(QString)));
QObject::connect(tcpThread,SIGNAL(send_live_flush_msg(QString,QStringList)),this,SLOT(live_flush_recv(QString,QStringList)));
}
tcpThread->startThread(Address_Ip,port);
}
void Widget::deleteListRoom(QString name)
{
int row = 0;
QString line;
while(row < ui->listRoom->count()){
line=ui->listRoom->item(row)->text();
if(name==line)
{
//qDebug()<<"删除成功";
ui->listRoom->takeItem(row);
break;
}
row++;
}
}
void Widget::connect_state(QString message)
{
qDebug()<<message;
if(message == "connecting") {
ui->label_connect_state->setText("连接中...");
}else if(message == "connect"){
ui->label_connect_state->clear();
emit connect_enable();
return;
}else if(message == "disconnect"){
tcpThread->stopThread();
return;
}else if(message == "overtime"){
QMessageBox::warning(NULL,tr("无法连接"),tr("无法连接请稍后再试"));
ui->label_connect_state->clear();
tcpThread->stopThread();
return;
}else if(message.left(9) == "reconnect"){
QString text = "第"+message.right(1)+"次重连中";
ui->label_connect_state->setText(text);
}
}
void Widget::connect_success()
{
QString strJson = myjson.pack_name_pswd(userName,passWord);
MyMessage mymsg((Logic_Flag?MSG_LOGIC:MSG_REGISTER),strJson,strJson.length());
qDebug()<<mymsg.toString();
tcpThread->onSendTcp(mymsg.toString());
}
void Widget::logic_msg_recv(QString message)
{
if(message == "success"){
QMessageBox::about(NULL,"登录","登录成功");
User_Init();
return;
}else if(message == "pswd_error"){
QMessageBox::warning(NULL," 密码错误","密码输入错误请重试");
tcpThread->stopThread();
return;
}
else if(message == "no_name"){
QMessageBox::warning(NULL,"未注册","请先注册账号");
tcpThread->stopThread();
return;
}
}
void Widget::reg_msg_recv(QString message)
{
if(message == "success"){
QMessageBox::about(NULL,"注册","注册成功");
tcpThread->stopThread();
return;
}else if(message == "rename"){
QMessageBox::warning(NULL,"用户名重复","请输入其他用户名");
tcpThread->stopThread();
}else{
tcpThread->stopThread();
}
}
void Widget::liveroom_msg_recv(QString liveroommsg)
{
if(liveroommsg == "close"){
this->show();
liveroom = nullptr;
MyMessage live_close(MSG_CLOSE_LIVE,userName,userName.length());
qDebug()<<live_close.toString();
tcpThread->onSendTcp(live_close.toString());
}
}
void Widget::live_flush_recv(QString action, QStringList namelist)
{
if(action == "on"){
for(int i = 0;i<namelist.count();i++){
QString name = namelist.at(i);
ui->listRoom->addItem(name);
}
}else if(action == "off"){
for(int i = 0;i<namelist.count();i++){
QString name = namelist.at(i);
deleteListRoom(name);
}
}
}
void Widget::room_msg_recv(QString roommsg)
{
if(roommsg == "close"){
this->show();
room = nullptr;
}
}
void Widget::on_radioshow_clicked(bool checked)
{
if(checked){
ui->linepswd->setEchoMode(QLineEdit::Normal);
}else{
ui->linepswd->setEchoMode(QLineEdit::Password);
}
}
void Widget::on_pushlogic_clicked()
{
if(ui->lineUser->text().isEmpty()){
QMessageBox::warning(NULL,tr("输入错误"),tr("账号不能为空"));
return;
}
if(ui->linepswd->text().isEmpty()){
QMessageBox::warning(NULL,tr("输入错误"),tr("密码不能为空"));
return;
}
userName = ui->lineUser->text();
passWord = ui->linepswd->text();
Logic_Flag = 1;
//连接
connectToServer();
}
void Widget::on_pushquit_clicked()
{
tcpThread->stopThread();
QMessageBox::about(NULL,tr("退出"),tr("点击退出"));
this->close();
}
void Widget::on_pushregister_clicked()
{
if(ui->lineUser->text().isEmpty()){
QMessageBox::warning(NULL,tr("输入错误"),tr("账号不能为空"));
return;
}
if(ui->linepswd->text().isEmpty()){
QMessageBox::warning(NULL,tr("输入错误"),tr("密码不能为空"));
return;
}
userName = ui->lineUser->text();
passWord = ui->linepswd->text();
Logic_Flag = 0;
//连接
connectToServer();
}
void Widget::on_pushCreate_clicked()
{
MyMessage message(MSG_CREATE_LIVE,userName,userName.length());
tcpThread->onSendTcp(message.toString());
if(liveroom == NULL){
qDebug()<<"new";
liveroom = new LiveRoom;
connect(liveroom,SIGNAL(sendlivemsg(QString)),this,SLOT(liveroom_msg_recv(QString)));
liveroom->show();
QString room_name = userName + "的直播间";
liveroom->setWindowTitle(room_name);
this->hide();
}else{
liveroom->show();
}
return;
}
void Widget::on_pushAdd_clicked()
{
if(ui->listRoom->count() == 0){
QMessageBox::warning(NULL,tr("无法加入"),tr("没有主播"));
return;
}
if(ui->listRoom->currentItem() == NULL){
QMessageBox::warning(NULL,tr("没有选择"),tr("请选择要加入的直播间"));
return;
}else{
if(room == NULL){
qDebug()<<"new";
room = new Room();
connect(room,SIGNAL(roommsg(QString)),this,SLOT(room_msg_recv(QString)));
room->show();
QString liver = ui->listRoom->currentItem()->text();
QString room_name = liver + "的直播间";
room->setWindowTitle(room_name);
this->hide();
}else{
room->show();
}
}
}
源代码.
1、测试开播下播时服务器的反应
2、测试开播时其他客户端能否正常刷新
3、测试其他客户端已经开播,用户登录后直播间信息是否正常刷新
4、测试没有人直播时无法加入直播间,已经已经有人直播之后加入直播间
今天对界面的跳转,界面的自动释放,以及QWidgetList的使用有了一定的了解,并且使用了一下
日日行,不怕千万里;常常做,不怕千万事。 !!!!!!