0


QT-通过tcp传输文件和文本消息

1.概述

在建立连接的基础上增加了发送文件的功能,在接收端和发送端定义了一个枚举类型,用于判别发送的是文件还是文本消息

enum MSG{
        messAge,
        fiLe
    };

2.客户端

客户端ui

主要函数

1.获取端口号和ip地址,进行连接,再次点击即可断开连接

void Widget::on_btn_listen_clicked()
{
    QString address=ui->lineEdit_address->text();
    qint16 port=ui->lineEdit_port->text().toInt();
    QHostAddress ip=QHostAddress(address);
    if(!conState)
    {
        my_client->connectToHost(ip,port);
        if(!my_client->waitForConnected(5000))
        {
            QMessageBox::critical(this,"错误",my_client->errorString());
            return ;
        }
        else
        {
            ui->btn_listen->setText("断开连接");
            connect(my_client,&QTcpSocket::readyRead,this,&Widget::readyReadClientSlot);
            conState=true;
        }
    }
    else
    {
        conState=false;
        my_client->close();
        qDebug()<<"断开连接"<<endl;
        ui->btn_listen->setText("开始连接");
    }
}

2.点击发送消息按钮,获取文本框内容,将消息发送出去,其中type为文本消息类型

void Widget::on_btn_send_clicked()
{
    QString str=ui->textEdit->toPlainText();
    QString res;
    res+="客户端:"+str;
    QByteArray arr;
    QDataStream data(&arr,QIODevice::WriteOnly);
    MSG type=messAge;
    data<<type<<res.toLocal8Bit();
    my_client->write(arr);
    ui->textBrowser->append(res);
    ui->textEdit->clear();
    ui->textEdit->setFocus();
}

3.接收客户端发来的文本消息

void Widget::readyReadClientSlot()
{
    int len=my_client->bytesAvailable();
    QByteArray arr=my_client->read(len>65536?65536:len);
    QString res=QString::fromLocal8Bit(arr);
    ui->textBrowser->append(res);
}

4.浏览文件

void Widget::on_btn_openfile_clicked()
{
    QString path=QFileDialog::getOpenFileName(this,"选择文件","E:/");
    ui->lineEdit_path->setText(path);

}

5.点击发送文件按钮,首先会将文件名和文件大小传输过去,以作判断传输是否完成依据

void Widget::on_btn_sendfile_clicked()
{
    if(!conState)//如果没有连接客户端弹出警告
    {
        QMessageBox::warning(this,"错误","没有连接服务端");
        return;
    }
    QString path=ui->lineEdit_path->text();//得到文件路径
    QFileInfo info(path);
    //获取文件信息
    filename=info.fileName();
    filesize=info.size();
    sendsize=0;//初始化发送的文件大小为0
    QByteArray arr;
    MSG type=fiLe;//发送的内容为文件类型
    QDataStream stream(&arr,QIODevice::WriteOnly);
    stream<<type<<filename<<filesize;//写入文件名和文件大小
    qDebug()<<filename<<" "<<filesize<<endl;
    //打开文件
    file.setFileName(path);
    file.open(QIODevice::ReadOnly);

    ui->progressBar->setMaximum(filesize);
    my_client->write(arr);//发送内容到服务端
    connect(my_client, &QTcpSocket::bytesWritten, this, &Widget::sendtxtSLot);
    //resarr=arr;
    //emit this->sendFileToYes();//发送完后 发送已经发完文件信息的信号
}

6.当文件信息传输完成后,会发送bytesWritten信号,后继每次传输完成也会发送这个信号,接着传输文件内容,当内容传输完成后,关闭文件,将filesize(发送的文件大小)和sendsize(已发送的文件大小)初始化为0,断开信号与槽,免得与文本消息冲突

void Widget::sendtxtSLot()
{
    //timer->start(5000);
    qDebug()<<"准备开始发送文件"<<endl;
    qDebug()<<"文件信息:"<<filename<<filesize<<endl<<"已经发送的大小:"<<sendsize<<endl;
    if(sendsize<filesize)//当发送大小小于文件大小执行 文件比较小就换成了if 一次能读完 换成while现在还有bug
    {
        //connect(timer,&QTimer::timeout,this,&Widget::sendfiletxt);

        qDebug()<<"开始发送文件"<<endl;
        QByteArray arr=file.read(1024*10);//一次读取的内容
        QByteArray arrsend;
        QDataStream data(&arrsend,QIODevice::WriteOnly);
        MSG type=fiLe;//发送的内容为文件类型
        data<<type<<arr;//读入文件类型和文件内容
        my_client->write(arrsend);//发送数据内容
        sendsize+=arr.size();
        QString strfile=QString::fromUtf8(arr);
        ui->textBrowser->append(strfile);
        qDebug()<<"更新发送的大小"<<sendsize<<endl;
        ui->progressBar->setValue(sendsize);

    }
    if(filesize==sendsize)
    {
        qDebug()<<"文件发送完成"<<endl;
        file.close();
        filesize=0;
        sendsize=0;
        //timer->stop();
       // disconnect(timer,&QTimer::timeout,this,&Widget::sendfiletxt);
        disconnect(my_client, &QTcpSocket::bytesWritten, this, &Widget::sendtxtSLot);
    }

}

完整代码-客户端

头文件:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QTcpSocket>
#include<QDebug>
#include<QMessageBox>
#include<QFile>
#include<QTimer>
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
    enum MSG{
        messAge,
        fiLe
    };
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
private:

signals:
    void sendFileToYes();//测试bug用的信号,已经废弃
private slots:
    void on_btn_listen_clicked();//开始/断开连接

    void on_btn_send_clicked();//发送文本消息
    void readyReadClientSlot();//接收服务端文本消息

    void on_btn_openfile_clicked();//浏览文件

    void on_btn_sendfile_clicked();//发送文件,首先发送文件信息过去
    void sendtxtSLot();//发送文件内容

private:
    Ui::Widget *ui;
    QTcpSocket *my_client;
    bool conState;

    //文件
    QString filename;
    int filesize;
    int sendsize;
    QFile file;
    QByteArray resarr;
    QTimer *timer;//测试bug用,已舍弃
};

#endif // WIDGET_H

cpp:

#include "widget.h"
#include "ui_widget.h"
#include<QHostAddress>
#include<QFileDialog>
#include<QFileInfo>
#include<QDataStream>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle("客户端");
    timer =new QTimer(this);

    ui->progressBar->setValue(0);
    my_client=new QTcpSocket();
    conState=false;
    connect(this,&Widget::sendFileToYes,this,&Widget::sendtxtSLot);

}

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

void Widget::on_btn_listen_clicked()
{
    QString address=ui->lineEdit_address->text();
    qint16 port=ui->lineEdit_port->text().toInt();
    QHostAddress ip=QHostAddress(address);
    if(!conState)
    {
        my_client->connectToHost(ip,port);
        if(!my_client->waitForConnected(5000))
        {
            QMessageBox::critical(this,"错误",my_client->errorString());
            return ;
        }
        else
        {
            ui->btn_listen->setText("断开连接");
            connect(my_client,&QTcpSocket::readyRead,this,&Widget::readyReadClientSlot);
            conState=true;
        }
    }
    else
    {
        conState=false;
        my_client->close();
        qDebug()<<"断开连接"<<endl;
        ui->btn_listen->setText("开始连接");
    }
}

void Widget::on_btn_send_clicked()
{
    QString str=ui->textEdit->toPlainText();
    QString res;
    res+="客户端:"+str;
    QByteArray arr;
    QDataStream data(&arr,QIODevice::WriteOnly);
    MSG type=messAge;
    data<<type<<res.toLocal8Bit();
    my_client->write(arr);
    ui->textBrowser->append(res);
    ui->textEdit->clear();
    ui->textEdit->setFocus();
}

void Widget::readyReadClientSlot()
{
    int len=my_client->bytesAvailable();
    QByteArray arr=my_client->read(len>65536?65536:len);
    QString res=QString::fromLocal8Bit(arr);
    ui->textBrowser->append(res);
}

void Widget::on_btn_openfile_clicked()
{
    QString path=QFileDialog::getOpenFileName(this,"选择文件","E:/");
    ui->lineEdit_path->setText(path);

}

void Widget::on_btn_sendfile_clicked()
{
    if(!conState)//如果没有连接客户端弹出警告
    {
        QMessageBox::warning(this,"错误","没有连接服务端");
        return;
    }
    QString path=ui->lineEdit_path->text();//得到文件路径
    QFileInfo info(path);
    //获取文件信息
    filename=info.fileName();
    filesize=info.size();
    sendsize=0;//初始化发送的文件大小为0
    QByteArray arr;
    MSG type=fiLe;//发送的内容为文件类型
    QDataStream stream(&arr,QIODevice::WriteOnly);
    stream<<type<<filename<<filesize;//写入文件名和文件大小
    qDebug()<<filename<<" "<<filesize<<endl;
    //打开文件
    file.setFileName(path);
    file.open(QIODevice::ReadOnly);

    ui->progressBar->setMaximum(filesize);
    my_client->write(arr);//发送内容到服务端
    connect(my_client, &QTcpSocket::bytesWritten, this, &Widget::sendtxtSLot);
    //resarr=arr;
    //emit this->sendFileToYes();//发送完后 发送已经发完文件信息的信号
}

void Widget::sendtxtSLot()
{
    //timer->start(5000);
    qDebug()<<"准备开始发送文件"<<endl;
    qDebug()<<"文件信息:"<<filename<<filesize<<endl<<"已经发送的大小:"<<sendsize<<endl;
    if(sendsize<filesize)//当发送大小小于文件大小执行 文件比较小就换成了if 一次能读完 换成while现在还有bug
    {
        //connect(timer,&QTimer::timeout,this,&Widget::sendfiletxt);

        qDebug()<<"开始发送文件"<<endl;
        QByteArray arr=file.read(1024*10);//一次读取的内容
        QByteArray arrsend;
        QDataStream data(&arrsend,QIODevice::WriteOnly);
        MSG type=fiLe;//发送的内容为文件类型
        data<<type<<arr;//读入文件类型和文件内容
        my_client->write(arrsend);//发送数据内容
        sendsize+=arr.size();
        QString strfile=QString::fromUtf8(arr);
        ui->textBrowser->append(strfile);
        qDebug()<<"更新发送的大小"<<sendsize<<endl;
        ui->progressBar->setValue(sendsize);

    }
    if(filesize==sendsize)
    {
        qDebug()<<"文件发送完成"<<endl;
        file.close();
        filesize=0;
        sendsize=0;
        //timer->stop();
       // disconnect(timer,&QTimer::timeout,this,&Widget::sendfiletxt);
        disconnect(my_client, &QTcpSocket::bytesWritten, this, &Widget::sendtxtSLot);
    }

}

服务端:

服务端ui

主要函数

1.获得端口号和ip地址进行监听,再次点击即可断开监听

void Widget::on_btn_listen_clicked()
{
    QString ip=ui->lineEdit_address->text();
    qint16 port=ui->lineEdit_port->text().toInt();
    QHostAddress address=QHostAddress(ip);
    if(!my_sever->isListening())
    {
        if(!my_sever->listen(address,port))
        {
            QMessageBox::critical(this,"错误",my_sever->errorString());
            return ;
        }
        else
        {
            ui->btn_listen->setText("停止监听");
            connect(my_sever,&QTcpServer::newConnection,this,&Widget::newConnectionSlot);
        }
    }
    else
    {
        ui->btn_listen->setText("开始监听");
        my_sever->close();
    }

}

2.将文本消息发送到每个客户端

void Widget::on_btn_send_clicked()
{
    QString str=ui->textEdit->toPlainText();
    ui->textEdit->clear();
    ui->textEdit->setFocus();
    QString res;
    res+="服务端: "+str;
    QByteArray arr=res.toLocal8Bit();
    ui->textBrowser->append(res);
    for(int i=0;i<my_client.size();i++)
    {
        my_client[i]->write(arr);
    }
}

3.当服务端发出newConnection信号时,将客户端加入到my_client的一员中,初始化接收到的文件大小和已接收的文件大小

void Widget::newConnectionSlot()
{
    qDebug()<<"新用户来了"<<endl;
    QTcpSocket* sock=my_sever->nextPendingConnection();
    my_client.append(sock);
    connect(sock,&QTcpSocket::readyRead,this,&Widget::readyReadSlot);
    filesize=0;
    recvsize=0;
}

4.接收来自客户端发送的文件或文本消息,首先获得是哪个客户端发送来的消息,然后判断是文本消息类型还是文件类型

注意:if(recvsize<filesize)和if(filesize==0)顺序不能颠倒,否则就会多执行一次if(recvsize<filesize)分支,在此卡了很久的bug!!!

void Widget::readyReadSlot()
{
    QTcpSocket *sock=qobject_cast<QTcpSocket*>(sender());
    sendnum++;//触发一次槽函数,客户端发来消息的次数加一
    ui->lcdNumber->display(sendnum);
    int type;//判断客户端发送的消息类型 {messAge:普通消息,fiLe:文件}
    int len=sock->bytesAvailable();
    QByteArray arr=sock->read(len>65536?65536:len);
    QByteArray arrtxt;//普通消息内容
    QByteArray arrfile;//文件内容
    QDataStream data(&arr,QIODevice::ReadOnly);
    QString str;
    data>>type;//读入类型

    switch (type) {
    case messAge://普通消息分支
        data>>arrtxt;
        str=QString::fromLocal8Bit(arrtxt);
         ui->textBrowser->append(str);
        break;
    case fiLe://文件分支
        qDebug()<<"准备接收文件"<<endl;

        if(recvsize<filesize)//当接收到的内容不完整时继续读取
        {
            qDebug()<<"开始接收文件内容"<<endl;

           data>>arrfile;//读入文件内容

           file.write(arrfile);
           recvsize+=arrfile.size();
           qDebug()<<"接受的大小:"<<recvsize<<endl;
           ui->progressBar->setValue(recvsize);
        }
        if(filesize==0)//如果文件大小为0,说明才开始传输,首先客户端要传输文件名字,和文件大小
        {

            qDebug()<<"接收文件信息"<<endl;
            data>>filename>>filesize;//读入文件名和文件大小
            qDebug()<<"文件信息:"<<recvsize<<" "<<filesize<<endl;
            ui->progressBar->setMaximum(filesize);//将进度条的最大值设置为客户端传入文件大小
            //打开文件
            file.setFileName(filename);
            file.open(QIODevice::WriteOnly);
        }

       if(recvsize==filesize)
       {
           qDebug()<<"关闭接收文件内容"<<endl;
           file.close();
           filesize=0;
           recvsize=0;
       }
        break;
    default:
        break;
    }
}

完整代码-服务端

头文件:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QTcpServer>
#include<QTcpSocket>
#include<QDebug>
#include<QMessageBox>
#include<QFile>
#include<QFileInfo>
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT
enum MSG{
    messAge,
    fiLe
};

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_btn_listen_clicked();//开启、关闭监听

    void on_btn_send_clicked();//发送文本消息到客户端

    void readyReadSlot();//接收客户端文本消息
    void newConnectionSlot();

private:
    Ui::Widget *ui;
    QTcpServer *my_sever;
    QList<QTcpSocket*> my_client;
    QString filename;
    int filesize;
    int recvsize;
    QFile file;
    int sendnum;//测试用,获得客户端发来了几次消息

};

#endif // WIDGET_H

cpp:

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

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    my_sever=new QTcpServer();
    this->setWindowTitle("服务端");
    ui->progressBar->setValue(0);
    sendnum=0;
    ui->lcdNumber->display(sendnum);
    //connect(my_sever,&QTcpServer::newConnection,this,&Widget::readyReadSlot);
}

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

void Widget::on_btn_listen_clicked()
{
    QString ip=ui->lineEdit_address->text();
    qint16 port=ui->lineEdit_port->text().toInt();
    QHostAddress address=QHostAddress(ip);
    if(!my_sever->isListening())
    {
        if(!my_sever->listen(address,port))
        {
            QMessageBox::critical(this,"错误",my_sever->errorString());
            return ;
        }
        else
        {
            ui->btn_listen->setText("停止监听");
            connect(my_sever,&QTcpServer::newConnection,this,&Widget::newConnectionSlot);
        }
    }
    else
    {
        ui->btn_listen->setText("开始监听");
        my_sever->close();
    }

}

void Widget::on_btn_send_clicked()
{
    QString str=ui->textEdit->toPlainText();
    ui->textEdit->clear();
    ui->textEdit->setFocus();
    QString res;
    res+="服务端: "+str;
    QByteArray arr=res.toLocal8Bit();
    ui->textBrowser->append(res);
    for(int i=0;i<my_client.size();i++)
    {
        my_client[i]->write(arr);
    }
}

void Widget::readyReadSlot()
{
    QTcpSocket *sock=qobject_cast<QTcpSocket*>(sender());
    sendnum++;//触发一次槽函数,客户端发来消息的次数加一
    ui->lcdNumber->display(sendnum);
    int type;//判断客户端发送的消息类型 {messAge:普通消息,fiLe:文件}
    int len=sock->bytesAvailable();
    QByteArray arr=sock->read(len>65536?65536:len);
    QByteArray arrtxt;//普通消息内容
    QByteArray arrfile;//文件内容
    QDataStream data(&arr,QIODevice::ReadOnly);
    QString str;
    data>>type;//读入类型

    switch (type) {
    case messAge://普通消息分支
        data>>arrtxt;
        str=QString::fromLocal8Bit(arrtxt);
         ui->textBrowser->append(str);
        break;
    case fiLe://文件分支
        qDebug()<<"准备接收文件"<<endl;

        if(recvsize<filesize)//当接收到的内容不完整时继续读取
        {
            qDebug()<<"开始接收文件内容"<<endl;

           data>>arrfile;//读入文件内容

           file.write(arrfile);
           recvsize+=arrfile.size();
           qDebug()<<"接受的大小:"<<recvsize<<endl;
           ui->progressBar->setValue(recvsize);
        }
        if(filesize==0)//如果文件大小为0,说明才开始传输,首先客户端要传输文件名字,和文件大小
        {

            qDebug()<<"接收文件信息"<<endl;
            data>>filename>>filesize;//读入文件名和文件大小
            qDebug()<<"文件信息:"<<recvsize<<" "<<filesize<<endl;
            ui->progressBar->setMaximum(filesize);//将进度条的最大值设置为客户端传入文件大小
            //打开文件
            file.setFileName(filename);
            file.open(QIODevice::WriteOnly);
        }

       if(recvsize==filesize)
       {
           qDebug()<<"关闭接收文件内容"<<endl;
           file.close();
           filesize=0;
           recvsize=0;
       }
        break;
    default:
        break;
    }
}

void Widget::newConnectionSlot()
{
    qDebug()<<"新用户来了"<<endl;
    QTcpSocket* sock=my_sever->nextPendingConnection();
    my_client.append(sock);
    connect(sock,&QTcpSocket::readyRead,this,&Widget::readyReadSlot);
    filesize=0;
    recvsize=0;
}
标签: qt tcp/ip ui

本文转载自: https://blog.csdn.net/qq_54227351/article/details/127689977
版权归原作者 为阿根廷助威 所有, 如有侵权,请联系我们删除。

“QT-通过tcp传输文件和文本消息”的评论:

还没有评论