黑马程序员QT学习笔记(第二部分)

1.TCP通信

TCP通信过程

请添加图片描述
tcp通信必须先建立tcp连接,通信端分为客户端和服务器端。服务器端程序必须使用qtcpserver用于端口监听,建立服务器,qtcpsocket用于建立连接后使用套接字(socket)进行通信。
TCP CLIENT:QTcpSocket
TCP SERVER:QTcpServer,QTcpSocket

服务器

新建项目名为Tcp
ui界面完成一下布局,并选择上面的textedit,勾选右下角readonly
请添加图片描述
Tcp.pro添加network,并点击小锤子编译一下

QT  +=  network

serverwidget.h

#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H

#include <QWidget>
#include<QTcpServer> //监听套接字
#include<QTcpSocket> //通信套接字

QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACE

class ServerWidget : public QWidget
{
    Q_OBJECT

public:
    ServerWidget(QWidget *parent = nullptr);
    ~ServerWidget();

private:
    Ui::ServerWidget *ui;
    
    QTcpServer *tcpServer; //监听套接字
    QTcpSocket *tcpSocket; //通信套接字
};
#endif // SERVERWIDGET_H

serverwidget.cpp

#include "serverwidget.h"
#include "ui_serverwidget.h"


ServerWidget::ServerWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::ServerWidget)
{
    ui->setupUi(this);

    tcpServer = NULL;
    tcpSocket = NULL;

    //监听套接字 指定父对象让其自动回收空间
    tcpServer = new QTcpServer(this);
    //监听当前网卡的所有ip地址,端口号8888
    tcpServer->listen(QHostAddress::Any,8888);

    setWindowTitle("服务器:8888");

    connect(tcpServer,&QTcpServer::newConnection,
            [=]()
            {
                //取出建立好连接的套接字
                tcpSocket = tcpServer->nextPendingConnection();
                //获取对方的ip和端口
                QString ip = tcpSocket->peerAddress().toString();
                qint16 port = tcpSocket->peerPort();
                QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);

                ui->textEditRead->setText(temp);

                connect(tcpSocket,&QTcpSocket::readyRead,
                        [=]()
                        {
                            //从通信套接字中取出内容
                            QByteArray array = tcpSocket->readAll();
                            ui->textEditRead->append(array);
                        }

                        );

            }
            );

}

ServerWidget::~ServerWidget()
{
    delete ui;
}


void ServerWidget::on_ButtonSend_clicked()
{
    if(NULL == tcpSocket)
    {
        return;
    }

    //获取编辑区的内容
    QString str = ui->textEditWrite->toPlainText();
    //给对方发送数据,使用TCPsocket套接字
    tcpSocket -> write(str.toUtf8().data());
}

void ServerWidget::on_ButtonClose_clicked()
{
    if(NULL == tcpSocket)
    {
        return;
    }

    //主动和客户端断开连接
    tcpSocket->disconnectFromHost();
    tcpSocket->close();
    tcpSocket = NULL;
}

客户端

项目右键,添加文件,选择QT设计师界面类
请添加图片描述
选择widget
请添加图片描述
设计ui界面,上面的texteidt选择readonly
请添加图片描述
clientwidget.h

#include<QTcpSocket>
private:
    Ui::ClientWidget *ui;
    QTcpSocket *tcpSocket; //指针
};

main.cpp

#include "serverwidget.h"

#include <QApplication>
#include"clientwidget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ServerWidget w;
    w.show();
    
    ClientWidget w2;
    w2.show();
    
    return a.exec();
}

clientwidget.cpp

#include "serverwidget.h"
#include "ui_serverwidget.h"


ServerWidget::ServerWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::ServerWidget)
{
    ui->setupUi(this);

    tcpServer = NULL;
    tcpSocket = NULL;

    //监听套接字 指定父对象让其自动回收空间
    tcpServer = new QTcpServer(this);
    //监听当前网卡的所有ip地址,端口号8888
    tcpServer->listen(QHostAddress::Any,8888);

    setWindowTitle("服务器:8888");

    connect(tcpServer,&QTcpServer::newConnection,
            [=]()
            {
                //取出建立好连接的套接字
                tcpSocket = tcpServer->nextPendingConnection();
                //获取对方的ip和端口
                QString ip = tcpSocket->peerAddress().toString();
                qint16 port = tcpSocket->peerPort();
                QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);

                ui->textEditRead->setText(temp);

                connect(tcpSocket,&QTcpSocket::readyRead,
                        [=]()
                        {
                            //从通信套接字中取出内容
                            QByteArray array = tcpSocket->readAll();
                            ui->textEditRead->append(array);
                        }

                        );

            }
            );

}

ServerWidget::~ServerWidget()
{
    delete ui;
}


void ServerWidget::on_ButtonSend_clicked()
{
    if(NULL == tcpSocket)
    {
        return;
    }

    //获取编辑区的内容
    QString str = ui->textEditWrite->toPlainText();
    //给对方发送数据,使用TCPsocket套接字
    tcpSocket -> write(str.toUtf8().data());
}

void ServerWidget::on_ButtonClose_clicked()
{
    if(NULL == tcpSocket)
    {
        return;
    }

    //主动和客户端断开连接
    tcpSocket->disconnectFromHost();
    tcpSocket->close();
    tcpSocket = NULL;
}

运行结果

请添加图片描述
点击客户端connect,客户端和服务器显示连接成功,可以通信
请添加图片描述
客户端输入hello whisper,点击send按钮,服务器接收
请添加图片描述
服务器输入hi evil,点击send按钮,客户端接收
请添加图片描述
点击close,断开连接

2.UDP通信

UDP通信过程

请添加图片描述
与tcp通信不同,两个程序之间进行udp通信无需预先建立持久的socket连接,udp每次发送数据报都需要指定目标地址和端口。

UDP

Udp.pro添加network,并点击小锤子编译一下

QT  +=  network

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QUdpSocket>//udp套接字

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    
    void dealMsg(); //槽函数,处理对方发过来的数据

private:
    Ui::Widget *ui;
    QUdpSocket *udpSocket; //指针
};
#endif // WIDGET_H


widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QHostAddress>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    setWindowTitle("服务器端口:8888");

    //分配空间,指定父对象
    udpSocket = new QUdpSocket(this);
    //绑定
    udpSocket->bind(8888);
    //当对方成功发送数据过来,自动触发readyRead()
    //这里使用槽函数的方式
    connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);
}

void Widget::dealMsg()
{
    //读取对方发送的内容
    char buf[1024] = {0};
    QHostAddress cliAddr;//对方地址
    quint16 port; //对方端口
    qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
    if (len>0)
    {
        //格式化 [192.68.2.2:8888]aaaa
        QString str = QString("[%1:%2] %3")
                .arg(cliAddr.toString())
                .arg(port)
                .arg(buf);
        //给编辑区设置内容
        ui->textEdit->setText(str);
    }

}
Widget::~Widget()
{
    delete ui;
}

//发送按钮
void Widget::on_buttonSend_clicked()
{
    //获取对方的IP和端口
    QString ip = ui->lineEditIP->text();
    qint16 port = ui->lineEditPort->text().toInt();
    //获取编辑区内容
    QString str = ui->textEdit->toPlainText();
    //给指定的IP发送数据
    udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
}

运行结果

输入对方的IP和端口即可发送信息
请添加图片描述

UDP组播

请添加图片描述

//绑定的地方要指定ipv4
udpSocket->bind(QHostAddress::AnyIPv4,8888);

//加入某个组播
//组播地址是D类地址
udpSocket->joinMulticastGroup(QHostAddress("224.0.0.2"));
//udpSocket->leaveMulticastGroup(); //退出组播

3.定时器

完成ui界面的设计
请添加图片描述
widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QTime> //定时器对象头文件

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    
    QTimer *myTimer;//创建定时器指针
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QTimer>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myTimer = new QTimer(this);
    connect(myTimer,&QTimer::timeout,
            [=]()
            {
                 static int i;
                 i++;
                 ui->lcdNumber->display(i);
            }
            );
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_buttonStart_clicked()
{
    //启动定时器,时间间隔为100ms
    //每隔100ms,定时器myTimer自动触发timeout()
    //如果定时器不是活动的时候才启动
    if(myTimer->isActive()==false)
    {
         myTimer->start(100);
    }

}

void Widget::on_pushButton_2_clicked()
{
    //如果定时器活动时,才停止
    if(myTimer->isActive()==true)
    {
         myTimer->stop();     
    }
   
}

运行结果
点击start按钮,开始计时,点击end按钮,停止计时
请添加图片描述

4.TCP传文件

TCP流程图

请添加图片描述

服务器

TCP_file.pro

QT       += core gui network

ui界面
请添加图片描述
serverwidget.h

#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H

#include <QWidget>
#include<QTcpServer>//监听套接字
#include<QTcpSocket>//通信套接字
#include<QFile>
#include<QTimer>

QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACE

class ServerWidget : public QWidget
{
    Q_OBJECT

public:
    ServerWidget(QWidget *parent = nullptr);
    ~ServerWidget();

    void sendData();//发送文件数据

private slots:
    void on_buttonFile_clicked();

    void on_buttonSend_clicked();

private:
    Ui::ServerWidget *ui;

    QTcpServer *tcpServer;
    QTcpSocket *tcpSocket;
    QFile file;//文件对象
    QString fileName;//文件名字
    qint64 fileSize;//文件大小
    qint64 sendSize;//已经发送文件大小

    QTimer timer;//定时器

};
#endif // SERVERWIDGET_H

serverwidget.cpp

#include "serverwidget.h"
#include "ui_serverwidget.h"
#include<QFileDialog>
#include<QDebug>
#include<QFileInfo>

ServerWidget::ServerWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::ServerWidget)
{
    ui->setupUi(this);

    //监听套接字
    tcpServer = new QTcpServer(this);

    //监听
    tcpServer->listen(QHostAddress::Any,8888);
    setWindowTitle("服务器端口:8888");

    //两个按钮都不能按
    ui->buttonFile->setEnabled(false);
    ui->buttonSend->setEnabled(false);

    //如果客户端成功和服务器连接
    //tcpServer会自动触发,newConeection()
    connect(tcpServer,&QTcpServer::newConnection,
            [=]()
            {
                //取出建立好连接的套接字
                tcpSocket = tcpServer->nextPendingConnection();
                //获取对方的IP和端口
                QString ip = tcpSocket->peerAddress().toString();
                quint16 port = tcpSocket->peerPort();

                QString str = QString("[%1:%2] 成功连接").arg(ip).arg(port);
                ui->textEdit->setText(str);//显示到编辑区

                //成功连接后,才能按选择文件
                ui->buttonFile->setEnabled(true);

            }
            );


    connect(&timer,&QTimer::timeout,
            [=]()
            {
                //关闭定时器
                timer.stop();
                //发送文件
                sendData();
            }
                );
}

ServerWidget::~ServerWidget()
{
    delete ui;
}

//选择文件按钮
void ServerWidget::on_buttonFile_clicked()
{
    QString filepath = QFileDialog::getOpenFileName(this,"open","../");
    if(filepath.isEmpty() == false)//如果选择文件路径有效
    {
        fileName.clear();
        fileSize = 0;
        //获取文件信息
        QFileInfo info(filepath);
        fileName = info.fileName();//获取文件名字
        fileSize = info.size();//获取文件大小

        sendSize = 0;//发送文件的大小

        //只读方式打开文件
        //指定文件的名字
        file.setFileName(filepath);
        //打开文件
        bool isOk = file.open(QIODevice::ReadOnly);
        if(isOk==false)
        {
            qDebug() << "只读方式发开文件失败";
        }

        //提示打开文件的路径
         ui->textEdit->append(filepath);
         ui->buttonFile->setEnabled(false);
         ui->buttonSend->setEnabled(true);

    }
    else
    {
        qDebug() << "选择文件路径出错";
    }
}

//发送文件按钮
void ServerWidget::on_buttonSend_clicked()
{
    //先发送文件头信息 文件名##文件大小
    QString head = QString("%1##%2").arg(fileName).arg(fileSize);
    //发送头部信息
    qint64 len = tcpSocket->write(head.toUtf8());
    if(len>0)//头部信息发送成功
    {
        //发送真正的文件信息
        //防止tcp粘包问题
        //需要通过定时器延时 20ms
        timer.start(20);


    }
    else
    {
        qDebug() << "头部信息发送失败";
        file.close();
        ui->buttonFile->setEnabled(true);
        ui->buttonSend->setEnabled(false);
    }
}


void ServerWidget::sendData()
{
    qint64 len = 0;
    do
    {
        //每次发送文件的大小
        char buf[4*1024] = {0};
        len = 0;
        //往文件中读数据
        len = file.read(buf,sizeof(buf));
        //发送数据,读多少,发多少
        len = tcpSocket->write(buf,len);
        //发送的数据需要累积
        sendSize += len;
    }while(len>0);

    //是否发送文件完毕
    if(sendSize == fileSize)
    {
        ui->textEdit->append("文件发送完毕");
        file.close();

        //把客户端端口
        tcpSocket->disconnectFromHost();
        tcpSocket->close();

    }
}

客户端

新建文件,QT->设计师界面
请添加图片描述
main.cpp

#include "serverwidget.h"

#include <QApplication>

#include"clientwidget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ServerWidget w;
    w.show();
    ClientWidget w2;
    w2.show();

    return a.exec();
}

clientwidget.h

#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H

#include <QWidget>
#include<QTcpSocket>
#include<QFile>

namespace Ui {
class ClientWidget;
}

class ClientWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ClientWidget(QWidget *parent = nullptr);
    ~ClientWidget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::ClientWidget *ui;

    QTcpSocket *tcpSocket;

    QFile file;//文件对象
    QString fileName;//文件名字
    qint64 fileSize;//文件大小
    qint64 recvSize;//已经接收文件大小

    bool isStart;
};

#endif // CLIENTWIDGET_H

clientwidget.cpp

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include<QDebug>
#include<QMessageBox>
#include<QHostAddress>

ClientWidget::ClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientWidget)
{
    ui->setupUi(this);
    setWindowTitle("客户端");

    tcpSocket = new QTcpSocket(this);

    isStart = true;

    connect(tcpSocket,&QTcpSocket::readyRead,
            [=]()
            {
                //取出接收的内容
                QByteArray buf = tcpSocket->readAll();
                if(true == isStart)
                {//接收头
                    isStart = false;
                    //解析头部信息 QString buf ="hello##1024"
                    //字符串拆包
                    //QString str = "hello##1024";
                    //str.section("##",0,0);//从第0段开始到第0段结束,##隔开,hello是第0段,1024是第一段

                    //初始化
                    fileName = QString(buf).section("##",0,0);
                    fileSize = QString(buf).section("##",1,1).toInt();
                    recvSize = 0;

                    //打开文件
                    file.setFileName(fileName);

                    bool isOk = file.open(QIODevice::WriteOnly);
                    if(isOk == false)
                    {
                        qDebug() << "writeonly error";
                    }
                }
                else//文件信息
                {
                    qint64 len = file.write(buf);
                    recvSize += len;

                    if(recvSize == fileSize)
                    {
                        file.close();
                        QMessageBox::information(this,"完成","文件接收完成");
                        tcpSocket->disconnectFromHost();
                        tcpSocket->close();
                    }
                }
            }
            );
}

ClientWidget::~ClientWidget()
{
    delete ui;
}

void ClientWidget::on_pushButton_clicked()
{
    //获取服务器的IP和端口
    QString ip = ui->lineEditIP->text();
    quint16 port = ui->lineEditPort->text().toInt();

    tcpSocket->connectToHost(QHostAddress(ip),port);
}


运行结果

显示客户端和服务器界面,此时选择文件和发送文件按钮都无法选择
请添加图片描述
点击客户端的connect,连接客户端和服务器,连接成功后选择文件按钮可以选择
请添加图片描述
选择想要发送的文件并点击发送文件
请添加图片描述
文件发送成功
请添加图片描述
在build文件夹下可以找到发送的文件
请添加图片描述

5.Thread 线程

简单示例

ui界面
请添加图片描述
widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QTimer>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void dealTimeout(); //定时器槽函数,处理定时器信号

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;

    QTimer *myTimer; //声明指针变量
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QThread> //线程
#include<QDebug>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    myTimer = new QTimer(this);
    //只要定时器启动,自动触发timeout
    connect(myTimer,&QTimer::timeout,this,&Widget::dealTimeout);


}

Widget::~Widget()
{
    delete ui;
}
void Widget::dealTimeout()
{
    static int i = 0;
    i++;
    //设置lcd的值
    ui->lcdNumber->display(i);
}


void Widget::on_pushButton_clicked()
{
    //如果定时器没有工作
    if(myTimer->isActive()==false)
    {
        myTimer->start(100);
    }

    //非常复杂的数据处理,耗时较长
    QThread::sleep(5);

    //处理完数据,关闭定时器
    myTimer->stop();
    qDebug() << "over";
}

运行结果:点击start按钮,界面没有反应,5秒后关闭定时器并打印“over”。把myTimer->stop();注释掉,则5秒后定时器开始运行。

线程

请添加图片描述
新建文件,选择C++,基类选择QObject

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值