QT学习(网络)
TCP
进入代码环节
***.pro
QT += core gui network
界面如下:
***1.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtNetwork/QtNetwork>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_startButton_clicked();
void start(); //开始监听
void acceptConnection(); //建立连接
void updateServerProgress(); //更新进度条,接收数据
void displayError(QAbstractSocket::SocketError socketError);//显示错误
private:
Ui::Widget *ui;
QTcpServer tcpServer;
QTcpSocket *tcpServerConnection;
qint64 totalBytes; //存放总大小信息
qint64 bytesReceived; //已收到数据的大小
qint64 fileNameSize; //文件名的大小信息
QString fileName; //存放文件名
QFile *localFile; //本地文件
QByteArray inBlock; //数据缓冲区
};
#endif // WIDGET_H
***1.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
totalBytes = 0;
bytesReceived = 0;
fileNameSize = 0;
connect(&tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection()));//当发现新连接时发出newConnection()信号
}
Widget::~Widget()
{
delete ui;
}
/******************************************************
监听来自发送方的事件
关联“开始监听”按钮
*******************************************************/
void Widget::start() //开始监听
{
ui->startButton->setEnabled(false);
bytesReceived =0;
if(!tcpServer.listen(QHostAddress::LocalHost,6666))
{
qDebug() << tcpServer.errorString();
close();
return;
}
ui->serverStatusLabel->setText(tr("监听"));
}
/****************************************************************************
实现接受连接
关联updateServerProgress()和displayError(QAbstractSocket::SocketError) 函数
******************************************************************************/
void Widget::acceptConnection() //接受连接
{
tcpServerConnection = tcpServer.nextPendingConnection();
connect(tcpServerConnection,SIGNAL(readyRead()),this,SLOT(updateServerProgress()));
connect(tcpServerConnection,
SIGNAL(error(QAbstractSocket::SocketError)),this,
SLOT(displayError(QAbstractSocket::SocketError)));
ui->serverStatusLabel->setText(tr("接受连接"));
tcpServer.close();
}
/*****************************************************************************
实现更新进度条
接收文件时提示:"接收文件 **** ..."
接收完毕后提示: "接收文件 **** 成功!"
*******************************************************************************/
void Widget::updateServerProgress() //更新进度条,接收数据
{
QDataStream in(tcpServerConnection);
in.setVersion(QDataStream::Qt_4_6);
if(bytesReceived <= sizeof(qint64)*2)
{ //如果接收到的数据小于16个字节,那么是刚开始接收数据,我们保存到//来的头文件信息
if((tcpServerConnection->bytesAvailable() >= sizeof(qint64)*2) && (fileNameSize == 0))
{
in >> totalBytes >> fileNameSize;//接收数据总大小信息和文件名大小信息
bytesReceived += sizeof(qint64) * 2;
}
if((tcpServerConnection->bytesAvailable() >= fileNameSize) && (fileNameSize != 0))
{
in >> fileName;//接收文件名,并建立文件
ui->serverStatusLabel->setText(tr("接收文件 %1 ...").arg(fileName));
bytesReceived += fileNameSize;
localFile= new QFile(fileName);
if(!localFile->open(QFile::WriteOnly))
{
qDebug() << "open file error!";
return;
}
}
else return;
}
if(bytesReceived < totalBytes)
{ //如果接收的数据小于总数据,那么写入文件
bytesReceived += tcpServerConnection->bytesAvailable();
inBlock= tcpServerConnection->readAll();
localFile->write(inBlock);
inBlock.resize(0);
}
//更新进度条
ui->serverProgressBar->setMaximum(totalBytes);
ui->serverProgressBar->setValue(bytesReceived);
if(bytesReceived == totalBytes)
{ //接收数据完成时
tcpServerConnection->close();
localFile->close();
ui->startButton->setEnabled(true);
ui->serverStatusLabel->setText(tr("接收文件 %1 成功!").arg(fileName));
}
}
/****************************************
实现错误处理函数
出错则显示错误
否则提示"服务端就绪"
******************************************/
void Widget::displayError(QAbstractSocket::SocketError) //错误处理
{
qDebug() << tcpServerConnection->errorString();
tcpServerConnection->close();
ui->serverProgressBar->reset();
ui->serverStatusLabel->setText(tr("服务端就绪"));
ui->startButton->setEnabled(true);
}
/*******************************************
“开始监听”按钮的单击事件
关联 start(),负责监听
**********************************************/
void Widget::on_startButton_clicked() //开始监听按钮
{
start();
}
***2.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtNetwork/QtNetwork>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void send(); //连接服务器
void startTransfer(); //发送文件大小等信息
void updateClientProgress(qint64); //发送数据,更新进度条
void displayError(QAbstractSocket::SocketError); //显示错误
void openFile(); //打开文件
void on_openButton_clicked();
void on_sendButton_clicked();
private:
Ui::Widget *ui;
QTcpSocket *tcpClient;
QFile *localFile; //要发送的文件
qint64 totalBytes; //数据总大小
qint64 bytesWritten; //已经发送数据大小
qint64 bytesToWrite; //剩余数据大小
qint64 loadSize; //每次发送数据的大小
QString fileName; //保存文件路径
QByteArray outBlock; //数据缓冲区,即存放每次要发送的数据
};
#endif // WIDGET_H
***2.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
loadSize = 4*1024;
totalBytes = 0;
bytesWritten = 0;
bytesToWrite = 0;
tcpClient = new QTcpSocket(this);
connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer()));//当连接服务器成功时,发出connected()信号,我们开始传送文件
connect(tcpClient,SIGNAL(bytesWritten(qint64)),this,
SLOT(updateClientProgress(qint64)));//当有数据发送成功时,我们更新进度条
connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this,
SLOT(displayError(QAbstractSocket::SocketError)));
ui->sendButton->setEnabled(false);//开始使”发送“按钮不可用
}
Widget::~Widget()
{
delete ui;
}
/*************************************************
“打开”按钮单击事件:选择在本机要发功的文件
与“打开”按钮关联
打开成功后,提示"打开文件 ***** 成功!"
***************************************************/
void Widget::openFile() //打开文件
{
fileName = QFileDialog::getOpenFileName(this);
if(!fileName.isEmpty())
{
ui->sendButton->setEnabled(true);
ui->clientStatusLabel->setText(tr("打开文件 %1 成功!").arg(fileName));
}
}
/****************************************************
“发送”按钮的单击事件:连接服务器
与“发送”按钮关联
主机:localhost
端口:6666
****************************************************/
void Widget::send() //连接到服务器,执行发送
{
ui->sendButton->setEnabled(false);
bytesWritten = 0;//初始化已发送字节为0
ui->clientStatusLabel->setText(tr("连接中..."));
tcpClient->connectToHost(ui->hostLineEdit->text(),ui->portLineEdit->text().toInt());//连接
}
/***************************************************
实现文件头结构的发送
*****************************************************/
void Widget::startTransfer() //实现文件大小等信息的发送
{
localFile = new QFile(fileName);
if(!localFile->open(QFile::ReadOnly))
{
qDebug() << "open file error!";
return;
}
totalBytes = localFile->size();//文件总大小
QDataStream sendOut(&outBlock,QIODevice::WriteOnly);
sendOut.setVersion(QDataStream::Qt_4_6);
QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);
sendOut << qint64(0) << qint64(0) << currentFileName;//依次写入总大小信息空间,文件名大小信息空间,文件名
totalBytes += outBlock.size();//这里的总大小是文件名大小等信息和实际文件大小的总和
sendOut.device()->seek(0);//返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间
sendOut<<totalBytes<<qint64((outBlock.size() - sizeof(qint64)*2));//发送完头数据后剩余数据的大小
bytesToWrite = totalBytes - tcpClient->write(outBlock);
ui->clientStatusLabel->setText(tr("已连接"));
outBlock.resize(0);
}
/*******************************************************
更新进度条,实现文件的传送
每次发送loadSize大小的数据,这里设置为4KB,如果剩余的数据不足4KB,就发送剩余数据的大小
实时更新发送进度
发送完毕后,提示"传送文件 ***** 成功"
如果没有发送任何数据,则关闭文件
*******************************************************/
void Widget::updateClientProgress(qint64 numBytes)
{
//已经发送数据的大小
bytesWritten += (int)numBytes;
if(bytesToWrite > 0) //如果已经发送了数据
{
//每次发送loadSize大小的数据,这里设置为4KB,如果剩余的数据不足4KB,就发送剩余数据的大小
outBlock = localFile->read(qMin(bytesToWrite,loadSize));
bytesToWrite -= (int)tcpClient->write(outBlock);//发送完一次数据后还剩余数据的大小
outBlock.resize(0);//清空发送缓冲区
} else {
localFile->close(); //如果没有发送任何数据,则关闭文件
}
//更新进度条
ui->clientProgressBar->setMaximum(totalBytes);
ui->clientProgressBar->setValue(bytesWritten);
if(bytesWritten == totalBytes) //发送完毕
{
ui->clientStatusLabel->setText(tr("传送文件 %1 成功").arg(fileName));
localFile->close();
tcpClient->close();
}
}
/****************************************
实现错误处理函数
出错则显示错误
否则提示"客户端就绪"
******************************************/
void Widget::displayError(QAbstractSocket::SocketError) //显示错误
{
qDebug() << tcpClient->errorString();
tcpClient->close();
ui->clientProgressBar->reset();
ui->clientStatusLabel->setText(tr("客户端就绪"));
ui->sendButton->setEnabled(true);
}
/*********************************************************
“打开”按钮的单击事件
关联 openFile(),负责打开本地文件
*******************************************************/
void Widget::on_openButton_clicked()//打开按钮
{
openFile();
}
/***************************************************
“发送”按钮的单击事件
关联 send(),负责发送文件
******************************************************/
void Widget::on_sendButton_clicked()//发送按钮
{
send();
}
学习链接:
网络Tcp2