Qt-socket网络 - tcp文件传输
待完善
done逻辑
datastream &file binary
传图片 Qbuffer
服务端
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QString>
#include <QFile>
#include <QFileInfo>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
void myGetNewConnect();
void myGetNewRead();
void myDisconnected();
void on_pushButton_2_clicked();
private:
Ui::Widget *ui;
QTcpServer *servSock;
QTcpSocket *listenSock;
QHostAddress servAddress;
quint64 servPort;
QString servSockInfo;
QString clientSockInfo;
bool isHead;
QFile file;
qint64 fileSize;
QString fileName;
qint64 fileNameSize;
qint64 dataReadBytes;
qint64 dataHeadReadBytes;
void setFileInfo();
qint64 BToLittleEndian(qint64 num);
void BToLittleEndianFromByteArray(QByteArray &arr);
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->lineEdit->setText("127.0.0.1");
ui->lineEdit_2->setText("8080");
ui->pushButton_2->setEnabled(false);
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(1024);
ui->progressBar->setValue(0);
servSockInfo=QString("%1:%2").arg(ui->lineEdit->text(),ui->lineEdit_2->text());
servSock=new QTcpServer(this);
listenSock=new QTcpSocket(this);
servAddress=QHostAddress(ui->lineEdit->text());
servPort=ui->lineEdit_2->text().toUInt();
isHead=true;
fileNameSize=0;
fileSize=0;
dataHeadReadBytes=0;
dataReadBytes=0;
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
if (!servSock->listen(servAddress,servPort))
{
ui->textEdit->append("ERROR: server listen failed...");
ui->textEdit->append(servSock->errorString());
return;
}
connect(servSock,&QTcpServer::newConnection,this,&Widget::myGetNewConnect);
ui->textEdit->append("server listening... waiting a connect...");
ui->pushButton->setEnabled(false);
ui->pushButton_2->setEnabled(true);
}
void Widget::myGetNewConnect()
{
listenSock=servSock->nextPendingConnection();
if (listenSock->isValid() && listenSock->isOpen())
{
ui->lineEdit_3->setText(listenSock->peerAddress().toString());
ui->lineEdit_4->setText(QString::number(listenSock->peerPort()));
clientSockInfo=QString("%1:%2").arg(ui->lineEdit_3->text(),ui->lineEdit_4->text());
ui->textEdit->append(QString("receive a new connect request: [ %1 -> %2 ]").arg(servSockInfo,clientSockInfo));
connect(listenSock,&QTcpSocket::readyRead,this,&Widget::myGetNewRead);
connect(listenSock,&QTcpSocket::disconnected,this,&Widget::myDisconnected);
listenSock->write("connect server success...");
}
}
void Widget::myGetNewRead()
{
/* version 1 */
if (isHead)
{
if (listenSock->bytesAvailable()>=sizeof(qint64) &&
fileNameSize==0)
{
QByteArray fileNameSizeArray=listenSock->read(sizeof(qint64));
memcpy(&fileNameSize,fileNameSizeArray.data(),fileNameSizeArray.size());
fileNameSize=BToLittleEndian(fileNameSize);
dataHeadReadBytes+=sizeof(qint64);
}
if (listenSock->bytesAvailable()>=fileNameSize &&
fileNameSize!=0)
{
QByteArray fileName_temp;
qint32 fileNameSize_temp;
QByteArray fileNameSize_temp_arr;
fileName_temp=listenSock->read(fileNameSize+4);
fileNameSize_temp_arr=fileName_temp.first(4);
BToLittleEndianFromByteArray(fileNameSize_temp_arr);
memcpy(&fileNameSize_temp,fileNameSize_temp_arr.data(),4);
if(fileNameSize==fileNameSize_temp)
{
fileName=QString(fileName_temp.mid(4));
}
else
{
ui->textEdit->append("ERROR: parse the head data with fileName failed");
return;
}
dataHeadReadBytes+=(fileNameSize+4);
if (fileName.isEmpty())
{
ui->textEdit->append("ERROR: receive head data failed");
return ;
}
}
if (fileSize==0 && dataHeadReadBytes>sizeof(qint64) && fileNameSize!=0 &&
listenSock->bytesAvailable()>=sizeof(qint64))
{
QByteArray fileSizeArray=listenSock->read(sizeof(qint64));
memcpy(&fileSize,fileSizeArray.data(),fileSizeArray.size());
fileSize=BToLittleEndian(fileSize);
dataHeadReadBytes+=sizeof(qint64);
this->setFileInfo();
isHead=false;
}
else
{
return ;
}
}
else
{
if (dataReadBytes<=fileSize)
{
qint64 dataSize=0;
QByteArray dataArray=listenSock->readAll();
if (!dataArray.isEmpty())
{
dataSize=file.write(dataArray);
if (dataSize>0)
{
dataReadBytes+=dataSize;
ui->progressBar->setValue(dataReadBytes/1024);
}
if (dataReadBytes==fileSize)
{
ui->textEdit->append("receive data and write done...");
file.close();
ui->textEdit->append("close the file");
}
}
}
}
}
void Widget::setFileInfo()
{
ui->textEdit->append("receive file head info:");
ui->textEdit->append("-- file name: "+fileName);
ui->textEdit->append("-- file size: "+QString::number(fileSize));
ui->textEdit->append("create a file ing...");
file.setFileName(fileName);
if (!file.open(QIODevice::WriteOnly))
{
ui->textEdit->append("ERROR: open file failed");
}
ui->textEdit->append("writing data to file...");
ui->progressBar->setMaximum(fileSize/1024);
}
void Widget::myDisconnected()
{
}
void Widget::on_pushButton_2_clicked()
{
listenSock->close();
servSock->close();
}
qint64 Widget::BToLittleEndian(qint64 num)
{
qint64 num_littleEnidan;
num_littleEnidan=((num & 0x00000000000000FF)<<(7*8) | (num & 0x000000000000FF00)<<(5*8) |
(num & 0x0000000000FF0000)<<(3*8) | (num & 0x00000000FF000000)<<(1*8) |
(num & 0x000000FF00000000)>>(1*8) | (num & 0x0000FF0000000000)>>(3*8) |
(num & 0x00FF000000000000)>>(5*8) | (num & 0xFF00000000000000)>>(7*8));
return num_littleEnidan;
}
void Widget::BToLittleEndianFromByteArray(QByteArray &arr)
{
QByteArray arr_temp=arr;
qint64 arrSize=arr.size();
for (int i=0;i<arrSize;i++)
{
arr[i]=arr_temp[arrSize-1-i];
}
}
.ui
客户端
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QHostInfo>
#include <QFileDialog>
#include <QFile>
#include <QDebug>
#include <QString>
#include <QDataStream>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
void on_pushButton_3_clicked();
void myGetNewRead();
void on_pushButton_2_clicked();
void dataContinueSend();
void myDisconnected();
private:
Ui::Widget *ui;
QTcpSocket *clientSock;
QHostAddress servAddress;
quint16 servPort;
QString info;
QFile file;
QFileInfo fileInfo;
QTimer *myTimer;
qint64 fileSize;
QByteArray dataArray;
};
#define sendBufferSize 512
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QProgressBar>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setWindowTitle("client");
this->resize(720,570);
ui->lineEdit->setText("127.0.0.1");
ui->lineEdit_2->setText("8080");
ui->pushButton_2->setEnabled(false);
// ui->pushButton_3->setEnabled(false);
ui->progressBar->setValue(0);
clientSock=new QTcpSocket(this);
servAddress=QHostAddress(ui->lineEdit->text());
servPort=ui->lineEdit_2->text().toUInt();
info=QString("%1:%2").arg(ui->lineEdit->text(),ui->lineEdit_2->text());
connect(clientSock,&QTcpSocket::readyRead,this,&Widget::myGetNewRead);
connect(clientSock,&QTcpSocket::bytesWritten,this,&Widget::dataContinueSend);
connect(clientSock,&QTcpSocket::disconnected,this,&Widget::myDisconnected);
dataArray.resize(sendBufferSize);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
clientSock->abort(); // 取消之前所有的连接,且抛弃所有缓冲区的内容
clientSock->connectToHost(servAddress,servPort);
if (!clientSock->waitForConnected(10000))
{
ui->textEdit->append("ERROR: connect failed...");
return;
}
if (!clientSock->isValid())
{
ui->textEdit->append("ERROR: the socket is invalid...");
}
ui->textEdit->append("connect server "+info);
ui->pushButton->setEnabled(false); // connect
ui->pushButton_3->setEnabled(true); // openfile
}
void Widget::on_pushButton_3_clicked()
{
QString fileName=QFileDialog::getOpenFileName(this,"打开文件","./");
if (fileName.isEmpty())
{
ui->textEdit->append("ERROR: connot find file");
return;
}
fileInfo=QFileInfo(fileName);
fileSize=fileInfo.size();
ui->lineEdit_3->setText(fileInfo.absoluteFilePath());
ui->progressBar->setMaximum(fileSize/512);
file.setFileName(fileInfo.absoluteFilePath());
if (!file.open(QIODevice::ReadOnly))
{
ui->textEdit->append("ERROR: file open failed...");
return ;
}
ui->textEdit->append("ready to send file info:");
ui->textEdit->append("-- file name: "+fileInfo.fileName());
ui->textEdit->append("-- file size: "+QString::number(fileSize)+" bytes");
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(fileSize/1024);
ui->pushButton_3->setEnabled(false);
ui->pushButton_2->setEnabled(true);
}
void Widget::myGetNewRead()
{
QByteArray readArray=clientSock->readAll();
if (!readArray.isEmpty())
{
ui->textEdit->append(QString("receive from server %1:").arg(info));
ui->textEdit->append(readArray);
}
if (QString(readArray)=="done")
{
ui->textEdit->append("server receive done...");
ui->textEdit->append("close client...");
clientSock->disconnectFromHost();
}
}
void Widget::on_pushButton_2_clicked()
{
// 以数据流的方式绑定dataHead
QByteArray dataHead;
QDataStream dataHeadStream(&dataHead,QIODevice::WriteOnly);
dataHeadStream.setVersion(QDataStream::Qt_6_2);
// 文件名大小+文件名+文件大小
qint64 fileNameSize=(qint64)(fileInfo.fileName().toUtf8().size());
dataHeadStream<<fileNameSize<<fileInfo.fileName().toUtf8()<<fileSize; // QString qint64
// 当成功写入的时候 返回实际写入的字节数,并且触发bytesWrittern信号
qDebug()<<"head size: "<<dataHead.size();
qint64 dataHeadWrittenBytes=clientSock->write(dataHead);
ui->textEdit->append("head info:");
ui->textEdit->append("head data format: qint64+QString+qint64:");
ui->textEdit->append(QString("fileNameSize+fileName+fileSize: %1+%2+%3").
arg(QString::number(fileNameSize),fileInfo.fileName(),QString::number(fileSize)));
if (dataHeadWrittenBytes!=(qint64)dataHead.size())
{
ui->textEdit->append("ERROR: file head data written failed");
file.close();
return ;
}
dataHead.resize(0);
// version 2
// 将文件绑定到数据流上
// QByteArray dataArray;
// dataArray.resize(fileSize);
// QDataStream dataStream(&dataArray,QIODevice::WriteOnly);
// dataStream<<file.readAll();
}
void Widget::dataContinueSend()
{
qint64 writtenNum=0;
qint64 readNum=0;
static qint64 dataWrittenBytes=0;
static qint64 databytesToWirte=fileSize-dataWrittenBytes;
if (databytesToWirte>0)
{
// 读取最小的放入,返回实际读取的
readNum=file.read(dataArray.data(),qMin(databytesToWirte,sendBufferSize));
if (readNum>0)
{
// 发送
writtenNum=(clientSock->write(dataArray));
dataWrittenBytes+=writtenNum; // 已经写了多少
databytesToWirte-=writtenNum; // 还有多少没写
// dataArray.clear();
ui->progressBar->setValue(dataWrittenBytes/512);
}
}
if (databytesToWirte<1024)
{
qDebug()<<"data";
}
if (dataWrittenBytes==fileSize)
{
file.close();
ui->textEdit->append("DONE: client send data success...");
}
}
void Widget::myDisconnected()
{
clientSock->close();
}