前面介绍了TCP和UDP的通信,只是文体通信,只能传送文字。本次介绍文件传输,也就是文件读写和TCP通信的结合。

解析:根据之前的TCP通信,建立彼此的连接。服务器选择文件,首先将文件的基本信息发送给客户端。如:文件名,文件大小(用于进度条使用)。如上显示,“head#hello#1024”,即头部信息,hello就是文件名,1024是文件大小。如此客户端可以在某个磁盘创建好文件,便于后面的写入。服务端每次读取MK大小的数据,每读取一次,传输一次,客户端写入一次但要累计发送文件的大小,便于与原文件大小比较。

黏包问题:
如果头部信息和文件内容重合或追加。比如”head#hello#1024abdjsjdas”,那么“abdjsjdas”本质是内容信息而不是头部。

解决方式:设定一个定时器,在发出头部信息后,间隔N毫秒后再发送内容信息。

服务端

FileServer.h

#ifndef FILESERVER_H

#define FILESERVER_H

#include <QWidget>

#include <QTcpServer>

#include <QTcpSocket>

#include <QFile>

#include <QFileDialog>

#include <QFileInfo>

#include <QTimer>

namespace Ui {

class Widget;

}

class Widget : public QWidget

{

Q_OBJECT

public:

explicit Widget(QWidget *parent = 0);

void SendData();

~Widget();

private slots:

void on_pushButton_clicked();       //选择文件

void on_pushButton_2_clicked();     //发送文件

private:

Ui::Widget *ui;

QTcpServer *m_listenSock;   //监听套接字

QTcpSocket *m_serverSock;   //通信套接字

QFile m_file;           //文件

QString m_strFileName;  //文件名

qint64 m_fileSize;      //文件大小

qint64 m_sendSize;      //已发送的大小

QTimer timer;           //定时器

};

#endif // FILESERVER_H

FileServer.cpp

#include "FileServer.h"

#include "ui_widget.h"

#include <QMessageBox>

Widget::Widget(QWidget *parent) :

QWidget(parent),

ui(new Ui::Widget)

{

ui->setupUi(this);

this->setWindowTitle("服务器端口:888");

//将选择文件和发送文件的灰度

m_listenSock=new QTcpServer(this);

ui->pushButton->setEnabled(false);

ui->pushButton_2->setEnabled(false);

//监听

bool bIsOk=m_listenSock->listen(QHostAddress::Any,888);

if(bIsOk==true)

{

connect(m_listenSock,&QTcpServer::newConnection,

[=]()

{

//获取客户端的通信套接字

m_serverSock=m_listenSock->nextPendingConnection();

QString IP=m_serverSock->peerAddress().toString();

quint16 port=m_serverSock->peerPort();

QString strTemp=QString("[%1:%2]接入成功").arg(IP).arg(port);

ui->textEdit->setText(strTemp);

//激活选择文件按钮

ui->pushButton->setEnabled(true);

}

);

}

connect(&timer,&QTimer::timeout,

[=]()

{

//停止定时器,并开始发送文件内容

timer.stop();

SendData();

}

);

}

Widget::~Widget()

{

delete ui;

delete m_listenSock;

m_listenSock=NULL;

m_serverSock=NULL;

}

void Widget::on_pushButton_clicked()

{

QString strFilePath=QFileDialog::getOpenFileName(this,"Open","../");

if(strFilePath.isEmpty()==false)

{

//文件基本信息

QFileInfo info(strFilePath);

m_strFileName=info.fileName();

m_fileSize=info.size();

m_sendSize=0;

//qDebug()<<m_strFileName<<m_fileSize;

//指定文件名称

m_file.setFileName(strFilePath);

//打开文件

bool bIsOpen=m_file.open(QIODevice::ReadOnly);

if(bIsOpen==true)

{

ui->textEdit->append(strFilePath);

//激活发送文件按钮,灰度选择文件按钮

ui->pushButton->setEnabled(false);

ui->pushButton_2->setEnabled(true);

};

}

else

{

//打开失败

}

}

void Widget::on_pushButton_2_clicked()

{

//发送头部信息

QString strHead=QString("%1##%2")

.arg(m_strFileName)

.arg(m_fileSize);

qint64 len=m_serverSock->write(strHead.toUtf8());

//发送成功

if(len>0)

{

//防止黏包,头部信息和内容信息混淆

timer.start(1000);

}

else

{

//发送失败,重新选择文件

m_file.close();

ui->pushButton->setEnabled(true);

ui->pushButton_2->setEnabled(false);

}

}

void Widget::SendData()

{

qint64 len=0;       //每次读取的大小

do

{

//每次读取、发送4K

char pBuf[4*1024]={0};

len=0;

//读多少,发送多少

len=m_file.read(pBuf,sizeof(pBuf));

m_serverSock->write(pBuf,len);

//累加发送了多少

m_sendSize+=len;

}while(len>0);

//len>0,表明有数据读取。0数据为空,<0读取失败。

if(m_fileSize==m_sendSize)

{

//QMessageBox::information(this,"提示","发送完成!");

ui->textEdit->append("发送完成!");

//激活选择文件按钮,灰度发送文件按钮

ui->pushButton->setEnabled(true);

ui->pushButton_2->setEnabled(false);

m_file.close();

//m_serverSock->disconnectFromHost();

//m_serverSock->close();

}

}

同一程序添加两个窗口,且有设计器。新建一个文件,但不是C++类文件,而是QT且带有界面类的,最后在main中使用show()直接显示。如下图:

client.h

#ifndef CLIENT_H

#define CLIENT_H

#include <QWidget>

#include <QTcpSocket>

#include <QFile>

#include <QProgressBar>

namespace Ui {

class Client;

}

class Client : public QWidget

{

Q_OBJECT

public:

explicit Client(QWidget *parent = 0);

~Client();

private slots:

void on_pushButton_clicked();       //请求连接

void on_pushButton_2_clicked();     //关闭连接

private:

Ui::Client *ui;

QTcpSocket *m_clientSock;   //通信套接字

QProgressBar m_proBar;  //进度条

QFile m_file;           //文件

QString m_strFileName;  //文件名

qint64 m_fileSize;      //文件大小

qint64 m_recvSize;      //已接收的大小

};

#endif // CLIENT_H

client.cpp

#include "client.h"

#include "ui_client.h"

#include <QMessageBox>

#include <QFileDialog>

Client::Client(QWidget *parent) :

QWidget(parent),

ui(new Ui::Client)

{

ui->setupUi(this);

this->setWindowTitle("客户端");

m_clientSock=new QTcpSocket(this);

//初始化进度条

ui->progressBar->setValue(0);

connect(m_clientSock,&QTcpSocket::connected,

[=]()

{

ui->textEdit->setText("连接成功!");

}

);

//判断文件头部信息还是内容信息

static bool bIsHead=true;

//通信

connect(m_clientSock,&QTcpSocket::readyRead,

[=]()

{

//获取服务器发来的内容

QByteArray byteBuf=m_clientSock->readAll();

//取出文件头部信息

if(bIsHead)

{

bIsHead=false;

//"filename##9999"

//解析头部信息

m_strFileName=QString(byteBuf).section("##",0,0);

m_fileSize=QString(byteBuf).section("##",1,1).toInt();

m_recvSize=0;

//根据文件的大小,设置进度条范围

ui->progressBar->setRange(0,m_fileSize/1024);

//m_strFileName="G:\\"+m_strFileName;   //自拟文件路径

m_file.setFileName(m_strFileName);      //默认源程序上一级目录

bool bIsOpen=m_file.open(QIODevice::WriteOnly);

if(bIsOpen==false)

{

//打开失败

}

}

//将读取的内容写入新文件

else

{

//写入文件

qint64 len=m_file.write(byteBuf);

m_recvSize+=len;

//更新进度条

ui->progressBar->setValue(m_recvSize/1024);

//接收完毕

if(m_recvSize==m_fileSize)

{

QMessageBox::information(this,"提示","接收完成!");

QString strTemp=QString("%1,接收完成").arg(m_strFileName);

ui->textEdit->append(strTemp);

ui->progressBar->setValue(0);

m_file.close();

//此处一定改,不然发下一文件失败,无法获取头部信息

bIsHead=true;

}

}

}

);

}

Client::~Client()

{

delete ui;

delete m_clientSock;

m_clientSock=NULL;

}

void Client::on_pushButton_clicked()

{

//设置对方的IP和端口

QString ip=ui->IP->text();

quint16 port=ui->Port->text().toInt();

//请求连接

m_clientSock->connectToHost(ip.toUtf8(),port);

}

void Client::on_pushButton_2_clicked()

{

//断开连接

m_clientSock->disconnectFromHost();

m_clientSock->close();

}

main.cpp

#include "FileServer.h"

#include <QApplication>

#include "client.h"

int main(int argc, char *argv[])

{

QApplication a(argc, argv);

Widget w;

w.show();

Client c;

c.show();

return a.exec();

}

程序结果图:

27Tcp文件传输的更多相关文章

  1. Linux主机上实现树莓派的交叉编译及文件传输,远程登陆

    0.环境 Linux主机OS:Ubuntu14.04 64位,运行在wmware workstation 10虚拟机 树莓派版本:raspberry pi 2 B型. 树莓派OS:官网下的的raspb ...

  2. putty提供的两个文件传输工具PSCP、PSFTP详细介绍

    用 SSH 来传输文件 PuTTY 提供了两个文件传输工具 PSCP (PuTTY Secure Copy client) PSFTP (PuTTY SFTP client) PSCP 通过 SSH ...

  3. c# 局域网文件传输实例

    一个基于c#的点对点局域网文件传输小案例,运行效果截图 //界面窗体 using System;using System.Collections.Generic;using System.Compon ...

  4. 使用 zssh 进行 Zmodem 文件传输

    Zmodem 最早是设计用来在串行连接(uart.rs232.rs485)上进行数据传输的,比如,在 minicom 下,我们就可以方便的用 Zmodem (说 sz .rz 可能大家更熟悉)传输文件 ...

  5. 在windows 与Linux间实现文件传输(C++&C实现)

    要实现windows与linux间的文件传输,可以通过socket网络编程来实现. 这次要实现的功能与<Windows下通过socket进行字符串和文件传输>中实现的功能相同,即客户端首先 ...

  6. Windows下通过socket进行字符串和文件传输

    今天在windows平台下,通过socket实现了简单的文件传输.通过实现这一功能,了解基本的windows网络编程和相关函数的使用方法. 在windows平台上进行网络编程,首先都需要调用函数WSA ...

  7. Linux下几种文件传输命令 sz rz sftp scp

    Linux下几种文件传输命令 sz rz sftp scp 最近在部署系统时接触了一些文件传输命令,分别做一下简单记录: 1.sftp Secure Ftp 是一个基于SSH安全协议的文件传输管理工具 ...

  8. 艺萌TCP文件传输及自动更新系统介绍(TCP文件传输)(四)

    艺萌TCP文件上传下载及自动更新系统介绍(TCP文件传输) 该系统基于开源的networkComms通讯框架,此通讯框架以前是收费的,目前已经免费并开源,作者是英国的,开发时间5年多,框架很稳定. 项 ...

  9. 艺萌TCP文件上传下载及自动更新系统介绍(TCP文件传输)(一)

    艺萌TCP文件上传下载及自动更新系统介绍(TCP文件传输) 该系统基于开源的networkComms通讯框架,此通讯框架以前是收费的,目前已经免费并开元,作者是英国的,开发时间5年多,框架很稳定. 项 ...

随机推荐

  1. 如何让input number类型的标签不产生上下加减的按钮

    之前用 input type="number" 来放数字框,发现有个上下加减的东西,感觉不太好 这个容易出现0 然后减为负数 这种情况下怎么去掉右边的那个上下加减的小按钮呢?前端同 ...

  2. swift - 利用UIDatePicker实现定时器的效果

    效果图如下: 可以通过UIDatePicker调整倒计时的时间,然后点击UIButton开始倒计时,使用NSTimer进行倒计时的时间展示,我是声明了一个label也进行了标记, 然后点击按钮开始倒计 ...

  3. BigDecimal类(精度计算类)的加减乘除

    BigDecimal类 对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDecimal类也可以进行大数 ...

  4. 格式化输出%s和%S的区别

    使用s时,printf是针对单字节字符的字符串,而wprintf是针对宽字符的 使用S时,正好相反,printf针对宽字符 CString中的format与printf类似,在unicode字符集的工 ...

  5. vertical-align负值和margin-bottom负值的区别

    先看一下vertical-align在W3C当中的值有哪一些: 可是它有数值这一说确实很少提起,我们来看这么一段代码: <!DOCTYPE html> <html lang=&quo ...

  6. 利用html实现类似于word自动生成的目录的效果

    在word中的自动生成目录当中,我们会看到是这样的目录结构: 嗯,自动生成固然是简单,但是在html当中,却没有一个合适的标签来去做.今天后台导出PDF的时候告诉我,他需要用html做一个这样的结构, ...

  7. 使用NSTimer实现动画

    1.新建empty AppLication,添加HomeViewController页面, iphone.png图片 2.在 HomeViewController.xib中添加Image View,并 ...

  8. 【Linux】 ftp 主动被动模式

    LNMP 搭建得服务器,在使用ftp时候,报如下错误: 经查,是ftp 主动模式被动模式问题 工具:  Xftp5   ,把被动模式勾 取消 (其他客户端可以网上查一下 相应的 被动模式转主动模式设置 ...

  9. CRUX下实现进程隐藏(1)

    想必能找到这里的都是被吴一民的操作系统大作业坑过的学弟学妹了,当初我也是千辛万苦才把这个作业完成了,本着服务后辈的宗旨,尽量让学弟学妹少走弯路,我会把实现的大概思路记录下来.本系列一共三篇文章,分别实 ...

  10. android系统自带图标

      android:src="@android:drawable/ic_media_rew"