QT 网络 TCP(二) 文件传输
一、服务端
1. 新建一个QT Gui Application, 命名为“TCPReceive”, 并修改pro文件(QT += network)
2. 添加所需控件, 如下图所示
其中“服务器端”Label的objectName为ServerStatusLabel;进度条Progress Bar的objectName为ServerProgressBar,设置其value属性为0;“开始监听”按钮的objectName为ListenPushButton
3. 修改Widget.h文件
(1) 添加头文件:#include <QtNetWork>
(2)添加私有变量:
Ui::Widget *ui;
QTcpServer m_TcpServer;
QTcpSocket* m_pTcpServerConnection;
qint64 m_nTotalBytes;//存放总大小信息
qint64 m_nBytesReceived;//已收到数据的大小
qint64 m_nFileNameSize;//文件名的大小信息
QString m_strFileName;//存放文件名
QFile* m_pLocalFile;//本地文件
QByteArray m_inBlock;//数据缓冲区
(3)添加槽函数
private slots:void Listen();void AcceptConnection();void UpdateServerProgress();void DiaplayError(QAbstractSocket::SocketError);void on_ListenPushButton_clicked();4.修改Widget.cpp文件(1)在构造函数添加如下代码:m_nTotalBytes = 0;m_nBytesReceived = 0;m_nFileNameSize = 0;connect(&m_TcpServer, SIGNAL(newConnection()), this,SLOT(AcceptConnection()));(2)实现槽函数
void Widget::Listen(){ui->ListenPushButton->setEnabled(false);m_nBytesReceived = 0;if(!m_TcpServer.listen(QHostAddress::LocalHost, 6666)){qDebug() << m_TcpServer.errorString();close();return ;}ui->ServerStatusLabel->setText(tr("监听"));}void Widget::AcceptConnection(){m_pTcpServerConnection = m_TcpServer.nextPendingConnection();
connect(m_pTcpServerConnection, SIGNAL(readyRead()), this,SLOT(UpdateServerProgress()));connect(m_pTcpServerConnection, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(DiaplayError(QAbstractSocket::SocketError)));ui->ServerStatusLabel->setText(tr("接受连接"));m_TcpServer.close();}void Widget::UpdateServerProgress(){QDataStream in(m_pTcpServerConnection);in.setVersion(QDataStream::Qt_4_8);if(m_nBytesReceived <= sizeof(qint64) * 2){if(m_pTcpServerConnection->bytesAvailable() >= sizeof(qint64) * 2 && m_nFileNameSize == 0)
{in >> m_nTotalBytes >> m_nFileNameSize;m_nBytesReceived += sizeof(qint64) * 2;}if(m_pTcpServerConnection->bytesAvailable() >= m_nFileNameSize && m_nFileNameSize != 0)
{in >> m_strFileName;ui->ServerStatusLabel->setText(tr("接收文件 %1 ...").arg(m_strFileName));m_nBytesReceived += m_nFileNameSize;m_pLocalFile = new QFile(m_strFileName);if(!m_pLocalFile->open(QFile::WriteOnly))
{qDebug() << "Open File Error!";return ;}}else{return ;}}if(m_nBytesReceived < m_nTotalBytes){m_nBytesReceived += m_pTcpServerConnection->bytesAvailable();
m_inBlock = m_pTcpServerConnection->readAll();m_pLocalFile->write(m_inBlock);m_inBlock.resize(0);}ui->ServerProgressBar->setMaximum(m_nTotalBytes);ui->ServerProgressBar->setValue(m_nBytesReceived);if(m_nBytesReceived == m_nTotalBytes){m_pTcpServerConnection->close();
m_pLocalFile->close();
ui->ListenPushButton->setEnabled(true);ui->ServerStatusLabel->setText(tr("接收文件 %1 成功! ").arg(m_strFileName));}}void Widget::DiaplayError(QAbstractSocket::SocketError){qDebug() << m_pTcpServerConnection->errorString();m_pTcpServerConnection->close();
ui->ServerProgressBar->reset();ui->ServerStatusLabel->setText(tr("服务端就绪"));ui->ListenPushButton->setEnabled(true);}void Widget::on_ListenPushButton_clicked(){Listen();}5. 为了可以显示中文信息, 在main.cpp中添加如下代码:
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));6.编译运行, 效果如下图
二、客户端
1. 新建QT Gui Application程序, 命名为TCPSender, 并修改pro文件(添加代码:QT += network)
2. 在ui文件中添加所需控件, 示意图如下:
其中“主机”的Line Edit的objectName为HostLineEdit;“端口”后的Line Edit的objectName为PortLineEdit;下面的Progress Bar的objectName为ClientProgressBar,其value属性设为0;“状态”Label的objetName为ClientStatusLabel;“打开”按钮的objectName为openButton;“发送”按钮的objectName为SendPushButton
3. 修改Widget.h文件
(1)添加头文件:#include <QtNetWork>
(2)添加私有变量:
QTcpSocket* m_pTcpClient;
QFile* m_pLocalFile;//要发送的文件
qint64 m_nTotalBytes;//数据总大小
qint64 m_nBytesWritten;//已经发送数据大小
qint64 m_nBytesToWrite;//剩余数据大小
qint64 m_nLoadSize;//每次发送数据的大小
QString m_strFileName;//保存文件路径
QByteArray m_OutBlock;//数据缓冲区, 即存放每次要发送的数据
(3)添加槽函数:
private slots:
void Send(); //连接服务器
void StartTransfer(); //发送文件大小等信息
void UpdateClientProgress(qint64); //发送数据, 更新进度条
void DiaplayError(QAbstractSocket::SocketError); //显示错误
void OpenFile(); //打开文件
void on_OpenPushButton_clicked();
void on_SendPushButton_clicked();
4.修改Widget.cpp文件
(1)在构造函数中添加如下代码:
m_nLoadSize = 4 * 1024;m_nTotalBytes = 0;m_nBytesWritten = 0;m_nBytesToWrite = 0;m_pTcpClient = new QTcpSocket(this);//当连接服务器成功时, 发出connected信号, 开始传送文件connect(m_pTcpClient, SIGNAL(connected()), this,SLOT(StartTransfer()));//当有数据发送成功时, 更新进度条connect(m_pTcpClient, SIGNAL(bytesWritten(qint64)), this,SLOT(UpdateClientProgress(qint64)));connect(m_pTcpClient, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(DiaplayError(QAbstractSocket::SocketError)));//设置“发送”按钮不可用ui->SendPushButton->setEnabled(false);(2)实现槽函数6. 编译运行, 效果图如下:void Widget::OpenFile(){m_strFileName = QFileDialog::getOpenFileName(this);if(!m_strFileName.isEmpty()){ui->SendPushButton->setEnabled(true);ui->ClientStatusLabel->setText(tr("打开文件 %1 成功!").arg(m_strFileName));}}void Widget::Send(){//设置发送按钮不可用ui->SendPushButton->setEnabled(false);//初始化已发送字节为0m_nBytesWritten = 0;ui->ClientStatusLabel->setText(tr("连接中..."));m_pTcpClient->connectToHost(ui->HostLineEdit->text(), ui->PortLineEdit->text().toInt());}void Widget::StartTransfer(){m_pLocalFile = new QFile(m_strFileName);if(!m_pLocalFile->open(QFile::ReadOnly))
{qDebug() << "Open File Error!";return ;}m_nTotalBytes = m_pLocalFile->size();
QDataStream sendOut(&m_OutBlock, QIODevice::WriteOnly);sendOut.setVersion(QDataStream::Qt_4_8);QString currentFileName = m_strFileName.right(m_strFileName.size() - m_strFileName.lastIndexOf('/') - 1);//依次写入总大小信息空间, 文件名大小信息空间, 文件名sendOut << qint64(0) << qint64(0) << currentFileName;//这里的总大小是文件名大小等信息和实际文件大小的总和m_nTotalBytes += m_OutBlock.size();sendOut.device()->seek(0);
//返回m_OutBlock的开始, 用实际的大小信息代替两个qint64(0)空间sendOut << m_nTotalBytes << qint64(m_OutBlock.size() - sizeof(qint64) * 2);//发送完头数据后剩余数据的大小m_nBytesToWrite = m_nTotalBytes - m_pTcpClient->write(m_OutBlock);ui->ClientStatusLabel->setText(tr("已连接"));m_OutBlock.resize(0);}void Widget::UpdateClientProgress(qint64 numBytes){//已经发送数据的大小m_nBytesWritten += (int)numBytes;//如果已经发送了数据if(m_nBytesToWrite > 0){//每次发送m_nLoadSize大小的数据, 这里设置为4KB, 如果剩余的数据不足, 就发送剩余数据的大小m_OutBlock = m_pLocalFile->read(qMin(m_nBytesToWrite, m_nLoadSize));//发送完一次数据后剩余数据的大小m_nBytesToWrite -= (int)m_pTcpClient->write(m_OutBlock);//清空发送缓冲区m_OutBlock.resize(0);}else //如果没有发送任何数据, 则关闭文件{m_pLocalFile->close();
}//更新进度条ui->ClientProgressBar->setMaximum(m_nTotalBytes);ui->ClientProgressBar->setValue(m_nBytesWritten);//发送完毕if(m_nBytesWritten == m_nTotalBytes){ui->ClientStatusLabel->setText(tr("传送文件 %1 成功").arg(m_strFileName));m_pLocalFile->close();
m_pTcpClient->close();
}}void Widget::DiaplayError(QAbstractSocket::SocketError){qDebug() << m_pTcpClient->errorString();m_pTcpClient->close();
ui->ClientProgressBar->reset();ui->ClientStatusLabel->setText(tr("客户端准备就绪"));ui->SendPushButton->setEnabled(true);}void Widget::on_OpenPushButton_clicked(){OpenFile();}void Widget::on_SendPushButton_clicked(){Send();}5.为了可以显示中文信息, 在main.cpp中添加如下代码:QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
7. 运行服务端, 然后点击监听按钮, 点击客户端中打开按钮, 选择本地文件, 进行传输, 示意图如下