Qt学习之路-1(基于tcp实现文件传输)

作者在 2015-08-30 10:51:52 发布以下内容
本文还是参考的《Qt及Qt Quick开发实战精解》一书中的第5个例子,即局域网聊天工具中的UDP聊天和TCP文件传送部分。另外http://www.yafeilinux.com/ 上有其源码和相关教程下载。

         其发送端界面如下:

/root/图片/chat/server.png

server.png (上传于2015-08-30 10:51:52)
server.png

/root/图片/chat/client.png

client.png (上传于2015-08-30 10:51:52)
client.png

流程图如下:

server:

Screenshot.png (上传于2015-08-30 10:51:52)
Screenshot.png

client:

Ccreenshot-1.png (上传于2015-08-30 10:51:52)
Ccreenshot-1.png


TCP部分程序代码和注释如下:

dialog.h:


#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QTime>

class QTcpServer;
class QFile;
class QTcpSocket;
class QUdpSocket;

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();
    void initser();// 初始化服务器。


private:
    Ui::Dialog *ui;
    qint16 tport;
    qint16 uport;
    qint16 u2port;
    qint64 totalBytes;
    qint64 writeBytes;
    qint64 tobeWriteBytes;
    qint64 payloadSize;//被初始化为第一个常量。
    QByteArray outBlock;

    QTcpSocket *clintcon;
    QUdpSocket *udpSocket;
    QTcpServer *tser;
    QString fileName;
    QString theFileName;

    QFile *locFile;
    QTime time;
protected:
    void SendFileName(QString fileName);
    QString getIP();
private slots:
    void sendMsg();
    void updClintProgress(qint64 numBytes);
    void getFileName(QString name);
    void refused();//关闭服务器。
    void on_sCloseBtn_clicked();

    void on_sOpenBtn_clicked();

    void on_sSendBtn_clicked();

signals:
    void sendFileName(QString fileName);
};

#endif // DIALOG_H
dialog.cpp:



#include "dialog.h"
#include "ui_dialog.h"
#include <QFile>
#include <QDebug>
#include <QMessageBox>
#include <QFileDialog>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkInterface>
#include "QObject"
#include <QUdpSocket>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <qtcpserver.h>

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    udpSocket = new QUdpSocket(this);
    uport = 8888;
    u2port = 7777;
    udpSocket->bind(u2port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(refused()));
    //sendMessage(NewParticipant);
    tport = 6666;
    tser = new QTcpServer(this);
    connect(tser,SIGNAL(newConnection()),this,SLOT(sendMsg()));
    connect(this,SIGNAL(sendFileName(QString)),this,SLOT(getFileName(QString)));
    initser();
}

Dialog::~Dialog()
{
    delete ui;
}
void Dialog::initser()//初始化服务器。
{
    totalBytes = 0;
    tobeWriteBytes = 0;
    writeBytes = 0;
    ui->sOpenBtn->setEnabled(true);//open按钮可用
    ui->sSendBtn->setEnabled(false);//发送按钮不可用

    ui->label_2->setText("请选择要发送的文件");
    tser->close();
}
void Dialog::refused()//关闭服务器。
{
    tser->close();
    ui->label_2->setText(tr("对方拒绝接收!"));
}
//发送文件
void Dialog::sendMsg()
{
    qDebug() << "--------sendMsg start-----------" ;
    ui->sSendBtn->setEnabled(false);//设置按钮不可用
    clintcon = tser->nextPendingConnection();//获取一个已经连接tcp套接字。
    connect(clintcon,SIGNAL(bytesWritten(qint64)),this,SLOT(updClintProgress(qint64)));

    ui->label_2->setText(tr("开始传送文件 %1!").arg(theFileName));
    locFile = new QFile(fileName);//只读方式打开文档
    if(!locFile->open((QFile::ReadOnly))){
         QMessageBox::warning(this,tr(""),tr("无法读取文件%1:\n %2").arg(fileName).arg(locFile->errorString()));
         return;
    }
    totalBytes = locFile->size();//文件的总字节数。
    qDebug() << "fileTotalBytes:" << totalBytes;
    QDataStream sendOut(&outBlock,QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_4_7);
    time.start();
    QString curFile = fileName.right(fileName.size() - fileName.lastIndexOf('/') -1 );//待发送文件名。
    qDebug() << "curFile:" << curFile;
    sendOut << qint64(0) << qint64(0) << curFile;
    totalBytes += outBlock.size();//文件名大小信息+实际文件大小。
    sendOut.device()->seek(0);//设置文件指针从0开始
    sendOut << totalBytes <<  qint64((outBlock.size() - sizeof(qint64)*2));
    tobeWriteBytes = totalBytes - clintcon->write(outBlock);//发送缓存区的数据,同时修改待发送的数据长度。
    qDebug() << "tobeWriteBytes:" << tobeWriteBytes;
    qDebug() << "outBlock:" << outBlock;
    outBlock.resize(0);
}

void Dialog::on_sCloseBtn_clicked()
{
    if(tser->isListening()){
        tser->close();//当tcp正在监听时,关闭tcp服务器端应用,即按下close键时就不监听tcp请求了
        if(locFile->isOpen()){
            locFile->close();
        }
        clintcon->abort();//Socket::终止当前的连接,禁止写入缓存区。
    }
    close();
}

void Dialog::on_sOpenBtn_clicked()
{
    fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
                 "/home"
                 );//tr("Images (*.png *.xpm *.jpg *.txt *.gz)")
    //fileName = QFileDialog::getOpenFileName(this);
    if(theFileName.isEmpty()){
        theFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/') - 1);
        ui->label_2->setText(tr("要发送的文件为:%1").arg(theFileName));
        qDebug() << "thefilename:" << theFileName;
    }
    qDebug() << "fileName:" << fileName;
    ui->sSendBtn->setEnabled(true);
}

void Dialog::on_sSendBtn_clicked()
{
        qint8 a;
        a = tser->listen(QHostAddress::Any,tport);
    if(!a){
        qDebug() << tser->errorString();
        close();
    }
    qDebug() << a;
    ui->label_2->setText(tr("等待对方接收...."));
    emit sendFileName(theFileName);
    qDebug() << "-----emit- success!------";
    ui->sSendBtn->setEnabled(false);
}
//进度条修改进度。
void Dialog::updClintProgress(qint64 numBytes)
{
    qDebug() << "udpClintProgress  start....";
    qApp->processEvents();
    //qDebug() << "writeBytes1:"<< writeBytes;
    //writeBytes += (int)numBytes;
    //qDebug() << "writeBytes2:" << writeBytes;
    if(tobeWriteBytes > 0){//没发送完。
        outBlock = locFile->read(qMin(tobeWriteBytes,payloadSize));//每次最多发送64k大小。
        tobeWriteBytes -= (int)clintcon->write(outBlock);
        writeBytes += (totalBytes - tobeWriteBytes);//已经发送了的。
        outBlock.resize(0);
    }else {
        locFile->close();
    }
    ui->progressBar->setMaximum(totalBytes);
    ui->progressBar->setValue(writeBytes);
    float useTime = time.elapsed();
    double speed = writeBytes/useTime*1000000;

    ui->label_2->setText(tr("(%1 m/s)").arg(speed));
    qDebug() << "writeBytes:"<< writeBytes;
    qDebug() << "totalBytes:" << totalBytes;

    if(writeBytes == totalBytes){
        locFile->close();
        tser->close();
        ui->label_2->setText(tr("文件:%1发送成功!").arg(theFileName));
    }

}
//获取文件名
void Dialog::getFileName(QString name)
{
        qDebug() << "------1getFileName--------" << name;
        theFileName = name;
        //qDebug() << "fileNamesize" << strlen(fileName);
        SendFileName(theFileName);
}
//发送文件名
void Dialog::SendFileName(QString fileName)
{
        QByteArray data;
        QDataStream out(&data, QIODevice::WriteOnly);

        QString address,clientaddr;
        address  = getIP();
        //clientaddr = getIP();
        //QString clientAddress = ui->userTableWidget->item(row, 2)->text();//(row,,2)为ip地址
        out << address << fileName;//发送本地ip,所发送的文件名
        qDebug() << "SendFileName :" << fileName;
        qDebug() << "server IP:" << address;
        int a= udpSocket->writeDatagram(data,data.length(),QHostAddress::Broadcast, uport);//向8888端口广播
        qDebug() << "a" << a;
}
//获取IP
QString Dialog::getIP()
{
    QList<QHostAddress> list = QNetworkInterface::allAddresses();
    foreach (QHostAddress address, list) {
        if(address.protocol() == QAbstractSocket::IPv4Protocol){
            if(address.toString().contains("192.168.1")||address.toString().contains("127.0"))
                continue;
            return address.toString();
        }
    }
    return 0;
}

client.h



#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QHostAddress>
#include <QFile>
#include <QTime>
#include <QDebug>
#include <QMessageBox>
#include"thread.h"
enum MessageType{FileName, refuse};
class QTcpSocket;
class QUdpSocket;
namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

    void setHostaddr(QHostAddress addr);                         //获取主机ip
    void setFileName(QString FileName);                         //获取文件保存路径
    void reciveFileNmae();
private slots:

    void on_cCancelBtn_clicked();

    void on_cClosebtn_clicked();

protected:
    void SendRefuse(MessageType type);
    void closeEvent(QCloseEvent *);
    void hansPendingFile(QString serAddress,QString fileName);
    QString getIP();
private:
    Thread *b;
    Ui::Dialog *ui;
    QUdpSocket *udpSocket;
    QTcpSocket *cClint;

    QHostAddress hostAddr;
    qint16 tport;
    qint16 uport;
    qint16 u2port;
    quint16 blockSize;
    qint64 totalBytes;
    qint64 recevBytes;
    qint64 fileNameSize;

    QString FileName;
    QString *SerAdress;
    QFile *locFile;                          //要接收的文件

    QByteArray inBlock;                     //缓存一次接收的数据
    QTime Time;
private slots:
    void processPendingDatarams();
    void newConn();
    void readMsg();
    void displayError(QAbstractSocket::SocketError);

signals:

};

#endif // DIALOG_H
client.cpp



#include "dialog.h"
#include "ui_dialog.h"

#include <QTcpSocket>
#include <QUdpSocket>
#include <QTime>
#include <QDebug>
#include <QMessageBox>
#include <QNetworkInterface>
#include <QFile>
#include <QFileDialog>
#include <QDateTime>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <qiodevice.h>


Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    setWindowTitle("client");
    totalBytes = 0;
    recevBytes = 0;
    fileNameSize = 0;
    udpSocket = new QUdpSocket(this);
    uport = 8888;
    u2port = 7777;
    udpSocket->bind(QHostAddress::Broadcast, uport, QUdpSocket::ShareAddress| QUdpSocket::ReuseAddressHint);
    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(processPendingDatarams()));
    tport = 6666;
    cClint = new QTcpSocket(this);
    connect(cClint,SIGNAL(readyRead()),this,SLOT(readMsg()));
    connect(cClint, SIGNAL(error(QAbstractSocket::SocketError)), this,
                SLOT(displayError(QAbstractSocket::SocketError)));
}
Dialog::~Dialog()
{
    delete ui;
}
// 设置地址
void Dialog::setHostaddr(QHostAddress addr)
{
    qDebug() << "--------setHostaddr---------";
    hostAddr = addr;
    qDebug() << "hostAddr:" << hostAddr;
    newConn();
}
//设置文件名
void Dialog::setFileName(QString FileName)
{
    locFile = new QFile(FileName);
}
void Dialog::newConn()                   // 设置与服务器建立连接的。
{
    qDebug() << "---------newConn start---------";
    blockSize = 0;
    cClint->abort();
    cClint->connectToHost(hostAddr,tport);
    Time.start();
}
//接收文件
void Dialog::readMsg(){
    qDebug() << "-----------readMsg start-----------";
    QDataStream in(cClint);
    in.setVersion(QDataStream::Qt_4_7);

    float useTime = Time.elapsed();
    if(recevBytes <= sizeof(qint64)*2){
        if((cClint->bytesAvailable() >= sizeof(qint64)*2)&& fileNameSize == 0){
            //接收数据总大小信息和数据文件名大小。
            in >> totalBytes >> fileNameSize;
            qDebug() << "totalBytes:" << totalBytes;
            qDebug() << "fileNameSize:" << fileNameSize;
            recevBytes += sizeof(qint64)*2;
            qDebug() << "1recevBytes:" << recevBytes;
        }
        if((cClint->bytesAvailable() >= fileNameSize) && (fileNameSize != 0)){
            //开始接收文件,并建立文件。
            in >> FileName;
            qDebug() << "FileName:" << FileName;
            recevBytes += fileNameSize;
            qDebug() << "2recevBytes:" << recevBytes;
            if(!locFile->open(QFile::WriteOnly)){
                QMessageBox::warning(this,tr("应用程序"),tr("无法读取文件%1: \n%2").arg(FileName).arg(locFile->errorString()));
                return;
            }
        }else{
            return;
        }
    } 
    qDebug() << "-----------BytesArray write newFile of start-----------";
    if(recevBytes < totalBytes){
        recevBytes += cClint->bytesAvailable();        //把缓存区的内容写入文件。
        inBlock = cClint->readAll();
        locFile->write(inBlock);
        inBlock.resize(0);                             //清空缓存区
    }
    ui->progressBar->setMaximum(totalBytes);
    ui->progressBar->setValue(recevBytes);//设置进度条的进度
    double speed = recevBytes/useTime;
    ui->label->setText(tr("(%1 m/s)").arg(speed));
    qDebug() << "recevBytes:" << recevBytes;
    qDebug() << "totalBytes:" << totalBytes;
    if(recevBytes == totalBytes){
        locFile->close();
        cClint->close();
        ui->label->setText(tr("接收文件 %1完毕 ").arg(FileName));
    }
}

void Dialog::closeEvent(QCloseEvent *)
{
    on_cClosebtn_clicked();
}

void Dialog::on_cCancelBtn_clicked()
{
    cClint->abort();
    if(locFile->isOpen()){
        locFile->close();
    }
}

void Dialog::on_cClosebtn_clicked()       //关闭服务
{
    cClint->abort();
    if(locFile->isOpen()){
        locFile->close();
    }
    close();
}
//处理接收到的文件名
void Dialog::hansPendingFile(QString serAddress,QString fileName)
{
    QString cIpAddress = getIP(); //"192.168.31.119";
    qDebug() << "cIpAddress:" << cIpAddress;
    int btn = QMessageBox::information(this,tr("接收文件"),
                                       tr("文件%1是否接收?").arg(fileName),
                                       QMessageBox::Yes,QMessageBox::No);
    if(btn == QMessageBox::Yes){
        QString name = QFileDialog::getSaveFileName(0,tr("保存文件"),fileName);
        if(!name.isEmpty()){
            Dialog *clint = new Dialog(this);
            setFileName(name);
            setHostaddr(QHostAddress(serAddress));//默认一台电脑用。
        }
    }
    else{
       SendRefuse(refuse);
        sleep(2);
        on_cClosebtn_clicked();
    }
}
QString Dialog::getIP()
{
    QList<QHostAddress> list = QNetworkInterface::allAddresses();
    foreach (QHostAddress address, list) {
        if(address.protocol() == QAbstractSocket::IPv4Protocol){
            if(address.toString().contains("192.168.1")||address.toString().contains("127.0"))
                continue;
            return address.toString();
           }
    }
    return 0;
}
//处理udp发送名字信号时函数
void Dialog::processPendingDatarams()
{
    qDebug() << "pending Datarams......." ;
    while(udpSocket->hasPendingDatagrams()){
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(datagram.data(), datagram.size());
        QDataStream in(&datagram,QIODevice::ReadOnly);
        QString time = QDateTime::currentDateTime()
                .toString("yyyy-MM-dd hh:mm:ss");

        //QString clientAddress, fileName;
        QString fileName,serAddress;
        in >> serAddress >>  fileName;
        qDebug() << "pending fileName" << fileName;
        hansPendingFile(serAddress,fileName);
    }
}
void Dialog::SendRefuse(MessageType refuse)
{
        QByteArray data;
        QDataStream out(&data, QIODevice::WriteOnly);
        out << refuse;
        qDebug() << "data" << data;
        int a= udpSocket->writeDatagram(data,data.length(),QHostAddress::LocalHost, u2port);
        qDebug() << "------------writeDatagram a:" << a;        
}
void Dialog::displayError(QAbstractSocket::SocketError socketError)
{
    switch(socketError)
        {
        //RemoteHostClosedError为远处主机关闭了连接时发出的错误信号
        case QAbstractSocket::RemoteHostClosedError : break;
        default : qDebug() << cClint->errorString();
        }
}





默认分类 | 阅读 30886 次
文章评论,共0条
游客请输入验证码
浏览107798次
文章分类
最新评论