一、TCP传输文件

服务器端:

需要两个套接字:QTcpserver(连接套接字)和QTcpsocket(通信套接字)

和传输文本信息一样(​​https://blog.51cto.com/u_15515702/5592539​​),增加了选择文件后获取文件的大小以及文件名,方便客户端接收。

进行组包,先发送一个头部信息,包含文件名和大小

发送完头部之后再发送文件

1.读文件

2.发送数据

3.发送文件大小和获取到的文件的大小相等时,说明发送完毕

客户端

只需要一个套接字QTcpsocket,接收分析字符串中的文件大小和文件名

本地创建一个文件,对方发多少接收多少,把接收到的内容写到文件里

1.TcpServerWidget.h

#ifndef TCPSERVERWIDGET_H
#define TCPSERVERWIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QFile>
#include <QTimer>

namespace Ui {
class TcpServerWidget;
}

class TcpServerWidget : public QWidget
{
Q_OBJECT

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

private slots:
void on_buttonFile_clicked();

void on_buttonSend_clicked();

private:
Ui::TcpServerWidget *ui;

QTcpServer *tcpServer; //监听套接字
QTcpSocket *tcpSocket; //通信套接字

QString fileName;//文件名字
qint64 fileSize;//文件大小
qint64 sendSize;//已经发了多少数据

//文件对象
QFile file;

//发送数据的函数
void sendDate();

QTimer *myTimer;
};

#endif // TCPSERVERWIDGET_H

2.TcpServerWidget.cpp

#include "tcpserverwidget.h"
#include "ui_tcpserverwidget.h"
#include <QFileInfo>
#include <QFileDialog>
#include <QFile>

#define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]"

TcpServerWidget::TcpServerWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::TcpServerWidget)
{
ui->setupUi(this);
setWindowTitle("服务器端口:8888");

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

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

//监听
tcpServer->listen(QHostAddress::Any,8888);

connect(tcpServer,&QTcpServer::newConnection,
[=](){
//取出建立好连接的套接字
tcpSocket=tcpServer->nextPendingConnection();

//获取对方的IP和端口
QString ip=tcpSocket->peerAddress().toString();
qint16 port =tcpSocket->peerPort();
QString ipDate=QString("[ip=%1 port=%2] 建立好连接了!!").arg(ip).arg(port);

ui->textEdit->append(ipDate);
ui->buttonFile->setEnabled(true);
}

);

myTimer=new QTimer(this);
connect(myTimer,&QTimer::timeout,
[=](){
//关闭定时器
myTimer->stop();

//发送文件
sendDate();
}

);
}

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

//打开文件 获取文件的基本信息
void TcpServerWidget::on_buttonFile_clicked()
{
QString filePath = QFileDialog::getOpenFileName(
this,tr("Open File"),"../"
);
//cout<<filePath;

fileName.clear();
fileSize=0;

//获取文件的基本信息
//QFileInfo获取文件信息
QFileInfo FileDate(filePath);
//qDebug()<<FileDate.exists();
if(FileDate.exists()){
qDebug() << "文件名字:" <<FileDate.fileName();
qDebug() << "文件大小:" << FileDate.size()<<"bit";
fileName=FileDate.fileName();
//cout<<fileName;
fileSize=FileDate.size();
//cout<<fileSize;
}

//打开文件
if(!filePath.isEmpty()){
//只读方式打开文件
//指定文件的名字
file.setFileName(filePath);

//打开文件
bool openOk=file.open(QIODevice::ReadOnly);
if(openOk){
//提示打开文件的路径
ui->textEdit->append("文件打开成功了。");
}
else{
cout<<"文件打开失败了。";
}
}

ui->buttonFile->setEnabled(false);
ui->buttonSend->setEnabled(true);

}

//先发送文件信息 再发送数据
void TcpServerWidget::on_buttonSend_clicked()
{
//文件信息先发送 “fileName_fileSize”
QString head=QString("%1**%2").arg(fileName).arg(fileSize);
//给对方发送数据, 使用套接字是tcpSocket
//cout<<head;
qint64 len;
len=tcpSocket->write(head.toUtf8().data());
//发送成功了
if(len>0){
//再发送数据
//发送真正的文件信息
//防止TCP黏包
//需要通过定时器延时 20 ms
myTimer->start(20);
}
else{
//如果发送失败
file.close();
ui->buttonFile->setEnabled(true);
ui->buttonSend->setEnabled(false);
}
}

void TcpServerWidget::sendDate(){
ui->textEdit->append("正在发送数据");
qint64 len=0;
sendSize=0;

do{
//每次发送数据的大小
char buf[4*1024] = {0};
len = 0;

//往文件中读数据
len = file.read(buf, sizeof(buf));
//cout<<len;
//发送数据,读多少,发多少
len=tcpSocket->write(buf,len);
//cout<<len;

//发送的数据需要累积
sendSize += len;
cout<<"sendSize="<<sendSize;
}while(len>0);
//cout<<"是否发送文件完毕";

//cout<<"sendSize="<<sendSize;
//cout<<"fileSize"<<fileSize;
//是否发送文件完毕
if(sendSize==fileSize){
ui->textEdit->append("数据发送完毕");
file.close();
//主动和客户端端口连接
tcpSocket->disconnectFromHost();
tcpSocket->close();
tcpSocket = NULL;
}
}

3.TcpClientWidget.h

#ifndef TCPCLIENTWIDGET_H
#define TCPCLIENTWIDGET_H

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

namespace Ui {
class TcpClientWidget;
}

class TcpClientWidget : public QWidget
{
Q_OBJECT

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

private slots:
void on_buttonConnect_clicked();

private:
Ui::TcpClientWidget *ui;

QTcpSocket *tcpSocket; //通信套接字

QString fileName;//文件名字
qint64 fileSize;//文件大小
qint64 receSize;//已经收了多少数据

bool startInfo;//判断是否是接收到文件信息了

//文件对象
QFile file;
};

#endif // TCPCLIENTWIDGET_H

4.TcpClientWidget.cpp

#include "tcpclientwidget.h"
#include "ui_tcpclientwidget.h"
#include <QMessageBox>
#include <QHostAddress>

#define cout qDebug() << "[" << __FILE__ <<":" << __LINE__ << "]"

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

tcpSocket=new QTcpSocket(this);

ui->progressBar->setValue(0); //当前值

connect(tcpSocket,&QTcpSocket::connected,
[=](){
ui->textEdit->setText("与服务端已经成功连接!");
}

);

//从通信套接字里面取内容
connect(tcpSocket,&QTcpSocket::readyRead,
[=](){
//获取对方发送的内容
QByteArray array = tcpSocket->readAll();

if(startInfo){
startInfo=false;
receSize=0;
//字符串切割“fileName_fileSize”
QString temp=QString(array);
// fileName=temp.section("_",0,0);
// //cout<<fileName;
// fileSize=temp.section("_",1,1).toInt();

//文件名
fileName = QString(array).section("**", 0, 0);
//文件大小
fileSize = QString(array).section("**", 1, 1).toInt();
//cout<<"fileSize="<<fileSize;

//指定文件的名字
file.setFileName(fileName);

//打开文件
bool openOk=file.open(QIODevice::WriteOnly);
if(!openOk){
cout<<"文件打开失败";
tcpSocket->disconnectFromHost(); //断开连接
tcpSocket->close(); //关闭套接字

return; //如果打开文件失败,中断函数
}

ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(fileSize/1024);
ui->progressBar->setValue(0); //当前值

}
else{
//是文件内容

qint64 len = file.write(array);
if(len>0){
receSize+=len;
}

//更新进度条
ui->progressBar->setValue(receSize/1024);

if(receSize==fileSize){
ui->textEdit->append("文件接收完成");
QMessageBox::information(this, "完成", "文件接收完成");

tcpSocket->disconnectFromHost(); //断开连接
tcpSocket->close(); //关闭套接字
file.close(); //关闭文件
}
}

}

);
}

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

//与服务器建立连接
void TcpClientWidget::on_buttonConnect_clicked()
{
//获取服务器ip和端口
QString ip=ui->lineEditIP->text();
qint16 port=ui->lineEditPort->text().toInt();

//主动和服务器建立连接
tcpSocket->connectToHost(QHostAddress(ip),port);
}

5.结果显示

Qt学习第五天_传输文件

二、TCP传输过程中的粘包问题

1.发送文件选择TCP传输,因为UDP传输会发生丢包问题。

2.TCP传输虽然不会发生丢包,但是存在粘包问题,上面通过定时器解决此问题,先发送头部信息,使用定时器等待一段时间后再发送文件内容。

3.目前还在学习阶段,如果您还有其他方法,欢迎评论。