Qt视频直播软件--项目实战(Day4)

本文档记录了一位开发者在设计登录界面时遇到的问题及解决方案,采用QThread实现TCP连接以实现实时显示登录状态。在UI设计中,登录成功后界面会切换,同时提供了登录和注册功能的逻辑流程。代码示例展示了如何处理TCP连接的建立、断开、重连和超时情况。此外,还展示了使用信号槽机制来处理UI与后台线程的交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第四天项目日记

1、今日总结

今天开始写客户端
1)先设计登录界面
2)然后用线程开启socket
3)用线程开的话可以实时获取登录的状态
关于TCP我参考了这篇博文

2、设计思路

写的时候试过在连接的时候用信号来处理连接状态,但是发现在等待连接的过程中,ui的控件不会发生改变,说明ui的控件被阻塞了,所以tcp的连接用线程来写,这样就可以实现在界面中实时显示连接状态。

3、代码说明

在这里插入图片描述

widget.ui

在这里插入图片描述
界面如图所示
这里只用了一个界面,然后设计思路是:在登录之前,只显示上面的界面,登录成功之后,只显示下面的界面。
输入用户名密码点击登录,先判断服务器在不在线,如果在线的话就发送用户名密码,然后服务器对客户端发过来的用户名和密码进行判断,如果匹配了就会跳到下面的页面,如果没匹配就返回失败原因

注册的话也是先判断有没有连接上,如果连接上了,就发送注册信息给服务器,如果没连接上就显示连接不到服务器。

两个功能均未完全实现;

tcpthread.h (继承自QThread)

#ifndef TCPTHREAD_H
#define TCPTHREAD_H

#include <QObject>
#include <QTcpSocket>
#include <QThread>
#include <QDebug>

class TcpThread : public QThread
{
    Q_OBJECT
public:
    explicit TcpThread(QObject *parent = 0);
    ~TcpThread();
    void startThread(const QString& ip, int port);
    void stopThread();

signals:
    void send_tcpmsg(QString);

public:
    int m_iSendData;
    int m_iRecv_TimeOut;
    int reconnect_num;

private:
    QTcpSocket* m_TcpSocket;
    bool        m_isThreaStopped;
    bool 		m_isOkConect;
    QString 	m_QStrSocketIp;
    int 		m_nSockPort;
    QByteArray 	m_datagram;

protected:
    virtual void run();

private slots:
    void onConnect();
    void onDisConnect();
    void onReadMsg();
    void onSendTcp(QString);
public slots:
};

#endif // TCPTHREAD_H

tcpthread.cpp

#include "tcpthread.h"

TcpThread::TcpThread(QObject *parent) :
    QThread(parent)
{
    m_TcpSocket = nullptr ;
    m_isThreaStopped = false;
    m_isOkConect = false;
    reconnect_num = 0;
}

TcpThread::~TcpThread()
{
    m_isThreaStopped = true;
    quit();
    wait();
}

void TcpThread::startThread(const QString &ip, int port)
{
    m_QStrSocketIp = ip;
    m_nSockPort = port;
    m_isThreaStopped = false;
    start();
}

void TcpThread::stopThread()
{
    reconnect_num = 0;
    m_isThreaStopped = true;
}

void TcpThread::run()
{
    bool b_recv_flag = false;
    emit send_tcpmsg("connecting");
    if (!m_TcpSocket)
    {
        m_TcpSocket = new QTcpSocket(this);
        connect(m_TcpSocket, SIGNAL(readyRead()), this, SLOT(onReadMsg()),Qt::DirectConnection);//让接受函数在run子线程中执行(发送者执行)
        connect(m_TcpSocket, SIGNAL(connected()), this, SLOT(onConnect()));
        connect(m_TcpSocket, SIGNAL(disconnected()), this, SLOT(onDisConnect()));
    }
    while (!m_isThreaStopped)
    {
        //检测客户端 socket指针是否为空
        if(b_recv_flag)
            msleep(100);
        if (!m_isOkConect)
        {
            // 终止当前连接并重置套接字(立即关闭套接字,丢弃写缓冲区中的任何挂起数据)
            m_TcpSocket->abort();
            m_TcpSocket->connectToHost(m_QStrSocketIp, m_nSockPort);
            //等待连接。。。延时三秒,三秒内连不上返回false
            m_isOkConect = m_TcpSocket->waitForConnected(3000);
            m_iRecv_TimeOut = -1;
            reconnect_num++;
        }
        if (!m_isOkConect)
        {
            msleep(1);
            if(reconnect_num <3){
                QString reconnect = "reconnect_"+QString::number(reconnect_num);
                emit send_tcpmsg(reconnect);
                continue;
            }else{
                emit send_tcpmsg("overtime");
                break;
            }
        }
        else
        {
            qDebug()<<"tcp_flag:"<<m_iRecv_TimeOut;
            onSendTcp("");
        }
        b_recv_flag = m_TcpSocket->waitForReadyRead(100);
        if  (b_recv_flag)
        {
            m_iRecv_TimeOut = 0;
        }
        else
        {
            m_iRecv_TimeOut++;
            if(m_iRecv_TimeOut>150) m_iRecv_TimeOut=150;
        }

        if(m_iRecv_TimeOut >= 150)
        {
            m_iRecv_TimeOut = -1;
            m_TcpSocket->disconnectFromHost();
        }
    }

    qDebug()<<"exit_5";

    m_TcpSocket->disconnectFromHost();
    qDebug()<<"exit_6";

}

void TcpThread::onConnect()
{
    reconnect_num = 0;
    emit send_tcpmsg("connect");
}

void TcpThread::onDisConnect()
{
    //socket一旦断开则自动进入这个槽函数
    //通过把 m_isOkConect 设为false,在socket线程的run函数中将会重新连接主机
    qDebug()<<"socket is disconnect!"<<endl;
    m_isOkConect = false;
    m_iRecv_TimeOut = -1;
    emit send_tcpmsg("disconnect");
}

void TcpThread::onReadMsg()
{
    if(m_TcpSocket->bytesAvailable() <= 0)
    {
        //  判定连接失败
        m_TcpSocket->disconnectFromHost();
    }
    while (m_TcpSocket->bytesAvailable() > 0)
    {
        // 接收数据
        m_datagram.clear();
        m_datagram.resize(m_TcpSocket->bytesAvailable());
        m_TcpSocket->read(m_datagram.data(), m_datagram.size());
        QString str_tcp_receive = QString::fromLocal8Bit(m_datagram);
        qDebug()<<str_tcp_receive;
        msleep(100);
    }
}

void TcpThread::onSendTcp(QString str_info)
{
    if((!m_TcpSocket)||m_TcpSocket->state()!=QAbstractSocket::ConnectedState)
        return;
    m_iSendData = m_TcpSocket->write(str_info.toStdString().c_str(), strlen(str_info.toStdString().c_str()));
    m_TcpSocket->flush();

    if (m_iSendData < 0)
    {
        m_TcpSocket->disconnectFromHost();
        return;
    }
    msleep(1000);
}







widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QMessageBox>
#include <QDebug>
#include "tcpthread.h"
#include "mymessage.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(); //用连接服务器

public slots:
    void connect_state(QString); //用来控制连接状态
    void connect_success(); //用来表示连接成功

private slots:
    void on_radioshow_clicked(bool checked); //用来显示密码和隐藏密码

    void on_pushlogic_clicked();//用来登录

    void on_pushquit_clicked();//用来退出

private:
    Ui::Widget *ui;
    TcpThread *tcpThread;//用来启动socket线程
//    bool status;
    QString userName; //账号
    QString passWard; //密码

signals:
    void connect_enable(); //连接

};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    connect(this,SIGNAL(connect_enable()),this,SLOT(connect_success()));
    ui->setupUi(this);
    tcpThread = nullptr;
//    status = false;
    Logic_Init();
}

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->listView->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->listView->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)));
    }
    tcpThread->startThread(Address_Ip,port);
}

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();
    }else if(message == "disconnect"){
         tcpThread->stopThread();
    }else if(message == "overtime"){
        QMessageBox::warning(NULL,tr("无法连接"),tr("无法连接请稍后再试"));
        ui->label_connect_state->clear();
        tcpThread->stopThread();
    }else if(message.left(9) == "reconnect"){
        QString text = "第"+message.right(1)+"次重连中";
        ui->label_connect_state->setText(text);
    }
}

void Widget::connect_success()
{
    QString msgbuf = userName+" "+ passWard;
    MyMessage mymsg(MSG_LOGIC,msgbuf,msgbuf.length());
    qDebug()<<mymsg.toString();
    tcpThread->send_tcpmsg(mymsg.toString());
    User_Init();
}


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();
    passWard = ui->linepswd->text();
    //连接
    connectToServer();
}

void Widget::on_pushquit_clicked()
{
    tcpThread->stopThread();
    QMessageBox::about(NULL,tr("退出"),tr("点击退出"));
    this->close();
}

mymessage.h

#ifndef MYMESSAGE_H
#define MYMESSAGE_H

#include <QString>

enum MsgId{
    MSG_CLITEN_CONNECT = 0, //连接消息
    MSG_READ_BYTES,   //读取接收到的消息
    MSG_CLIENT_CLOSE,   //客户端关闭的消息
    MSG_LOGIC,  //客户端登录消息
};


class MyMessage
{
    enum MsgId msgid;
    QString msgbuf;
    int length;
public:
    MyMessage();
    MyMessage(MsgId msgid,QString msgbuf,int length);
    void setmsgid(MsgId msgid);
    void setmsgbuf(QString msgbuf);
    void setlength(int length);
    int getmsgid();
    QString getmsgbuf();
    int getlength();
    QString toString();
};

#endif // MYMESSAGE_H

mymessage.cpp

#include "mymessage.h"

MyMessage::MyMessage()
{

}

MyMessage::MyMessage(MsgId msgid, QString msgbuf, int length)
{
    this->msgid = msgid;
    this->msgbuf = msgbuf;
    this->length = length;
}

void MyMessage::setmsgid(MsgId msgid)
{
    this->msgid = msgid;
}

void MyMessage::setmsgbuf(QString msgbuf)
{
    this->msgbuf = msgbuf;
}

void MyMessage::setlength(int length)
{
    this->length = length;
}

int MyMessage::getmsgid()
{
    return msgid;
}

QString MyMessage::getmsgbuf()
{
    return msgbuf;
}

int MyMessage::getlength()
{
    return length;
}

QString MyMessage::toString()
{
    QString send_msg = QString::number(msgid)+"|"+msgbuf;
    return send_msg;
}

4、项目文件

源代码链接接.

5、效果展示

测试如下

1、先测试账号密码不输入的情况下,提示信息
然后测试密码显示和隐藏请添加图片描述
2、测试不开启服务器的情况下登录超时的情况
连接次数三次,每次超时时间为3s,三次结束之后提示连接不到服务器
连接过程中有提示,点击确定之后情况提示
请添加图片描述
3、测试连接过程中开启服务器,自动连接成功(密码验证还没有写)
只测试界面跳转,跳转之后点击退出,客户端退出 退出之后服务器显示的在线列表自动会清掉
请添加图片描述

6、每日总结

今日学会使用 QThread里面创建 QTcpSocket
为了让连接过程中显示连接状态,真的是废了很大的功夫。。。
就这样慢慢成长吧。

另外,要考虑在消息传递的过程中使用 json 格式进行封装一下,所以可能要调研一下 QJson

坚持就是胜利!!!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值