TCP文件传输

服务端

界面:

在pro文件中写上

QT       += core gui network
CONFIG+=c++11

这是一些需要的头文件:

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

定义一些变量

private:
    Ui::ServerWidget *ui;
    QTcpServer *tcpServer;
    QTcpSocket *tcpSocket;
    QFile file;
    QString fileName;//文件名字
    qint64 filesize;//文件大小
    qint64 sendSize;//已经发送文件的大小
    QTimer timer;//设置一个定时器

选择文件按钮的槽函数:

void ServerWidget::on_Choosebutton_clicked()
{
    //获取打开文件的路径
    QString path=QFileDialog::getOpenFileName(this,"open","../");
    if(path.isEmpty()==false)
    {
        //选中文件以后准备打开文件
        //获取文件信息
        //初始化两个变量
        fileName.clear();
        filesize=0;
        QFileInfo info(path);
        fileName=info.fileName();//获取文件名字
        filesize=info.size();//获取文件大小
        sendSize=0;
        //只读方式打开
        //指定文件的名字
        file.setFileName(path);
        bool isopen=file.open(QIODevice::ReadOnly);
        if(!isopen)
        {
            qDebug()<<"Open File Error!";
        }
        //提示打开什么文件
        ui->textEdit->append(path);
        ui->Choosebutton->setEnabled(false);//锁定选择文件按钮
        ui->Sendbutton->setEnabled(true);//开启发送按钮
    }
    else
    {
        qDebug()<<"Wrong FilePath";
    }
}

其中主要的内部方法就是:

1.QFileDialog::getOpenFileName(this,"open","../");用来打开文件,并且获取路径,这个是只能获取单个文件的路径

2.QFileInfo info(path);  创建这个对象来获取文件的信息

3.bool isopen=file.open(QIODevice::ReadOnly);   判断文件是不是打开成功

4.setEnabled( )可以开启按钮,也可以锁定按钮

发送文件按钮的函数:

void ServerWidget::on_Sendbutton_clicked()
{
    //先发送文件头信息,格式:文件名##文件大小
    QString head=QString("%1##%2").arg(fileName) .arg(filesize);
    //发送头部信息
    qint64 len = tcpSocket->write(head.toUtf8());
    if(len>0)//头部信息发送成功
    {
       //发送真正文件信息
        //为了放置TCP文件黏包
        //定时器
        timer.start(20);
    }
    else
    {
        qDebug()<<"The head of the file open wrong";
        file.close();
        ui->Choosebutton->setEnabled(true);
        ui->Sendbutton->setEnabled(false);
    }
}

主要函数:

1. tcpSocket->write(head.toUtf8());    write函数用来发送信息

构造函数:

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->Sendbutton->setEnabled(false);
    ui->Choosebutton->setEnabled(false);
    //如果客户端成功链接服务器
    //tcpServer会自动触发newConnect
    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->Sendbutton->setEnabled(true);

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

主要函数:

1.tcpServer->listen(QHostAddress::Any,8888);   用来绑定套接字与端口和IP

2.QTcpServer::newConnection    链接成功会自动触发这个信号、

3. tcpSocket=tcpServer->nextPendingConnection();     取出套接字

4.tcpSocket->peerAddress().toString();    取出ip

5.tcpSocket->peerPort();    取出端口

6.QTimer::timeout    开启定时器后自动触发

发送信息函数SendDate():

void ServerWidget::SendDate()
{
    qint64 len=0;
    do
    {
        //每次发4k大小
        char buffer[4*1024]={0};
        len=0;
        //往文件中读数据
        len=tcpSocket->read(buffer,sizeof(buffer));
        //往文件中写数据
        len=tcpSocket->write(buffer,len);
        //发送的数据需要累加
        sendSize+=len;
    }while(len>0);
    //是否发送文件完毕
    if(sendSize==filesize)
    {
        ui->textEdit->setText("文件发送完毕");
        file.close();//关闭文件
        //把客户端断开
        tcpSocket->disconnectFromHost();
        tcpSocket->close();
    }
}

主要函数:

1.tcpSocket->read(buffer,sizeof(buffer));   读文件

2.tcpSocket->write(buffer,len);   写文件

3.tcpSocket->disconnect();    断开链接

4.tcpSocket->close();    关闭套接字

客户端:

界面:

Connect按钮函数:

void ClienWidget::on_connectbutton_clicked()
{
    //获取服务器的IP和端口
    QString ip=ui->ipEdit->text();
    quint16 port=ui->portEdit->text().toInt();
    //开始链接
    tcpSocket->connectToHost(QHostAddress(ip),port);
}

主要函数:

1.tcpSocket->connectToHost(QHostAddress(ip),port);      根据设定的IP和端口主动链接

构造函数:

ClienWidget::ClienWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClienWidget)
{
    ui->setupUi(this);
    tcpSocket=new QTcpSocket(this);
    isStart=true;
    connect(tcpSocket,&QTcpSocket::readyRead,
            [=]()
    {
        //取出接受的内容
        QByteArray arr=tcpSocket->readAll();
        //要区分是头还是真的内容
        if(isStart)
        {
            //接收头
            isStart=false;
            //解析头部信息buf="文件名##文件大小"
            fileName=QString(arr).section("##",0,0);
            filesize=QString(arr).section("##",1,1).toInt();
            receSize=0;
            //打开文件
            file.setFileName(fileName);
            bool isok=file.open(QIODevice::WriteOnly);
            if(isok==false)
            {
                qDebug()<<"Open File Fail";
            }
        }
        else//接受文件信息
        {
            qint64 len= file.write(arr);
            receSize+=len;
            if(receSize==filesize)
            {
                file.close();
                QMessageBox::information(this,"完成","文件接受完成");
                tcpSocket->disconnectFromHost();//断开链接
                tcpSocket->close();
            }
        }
    });
}

主要函数:

1.QTcpSocket::readyRead    一旦传输成功就会自动触发的信号

2.tcpSocket->readAll()     读取文件中的所有东西

3.  section("##",0,0)   这个是用来拆解字符串信息的

    因为我们的头信息是以“文件名字##文件大小”来显示的,所以这里用section("##",0,0)  表示取出以“##”分隔方式的第一段(0,0)表示第一段,也就是(1,1)表示第二段

4.QIODevice::WriteOnly      以只读的方式打开一个文件

5. QMessageBox::information(this,"完成","文件接受完成")      是弹出一个提醒框,具体参数参考手册

猜你喜欢

转载自blog.csdn.net/scwMason/article/details/81204089
今日推荐