话不多说,直接上自己写的一个tcp小程序,分为客户端和服务端两个程序,实现了单一方向的数据传输。下面来看具体的代码:
先看客户端的程序。创建基于Qt Widgets Application
的应用程序,选择基类QDialog,修改类名为myClient。创建后在.pro文件中添加“QT += network”,保存文件。在.ui文件中拖入控件,布局界面,界面效果如下图所示,再在Client.h和Client.cpp中添加代码。
界面效果如下:
客户端的代码:
myClinet.h
#ifndef MYCLIENT_H
#define MYCLIENT_H#include <QDialog>
#include <QAbstractSocket>QT_BEGIN_NAMESPACE
namespace Ui { class myClient; }
QT_END_NAMESPACEclass QTcpSocket;
class QFile;class myClient : public QDialog
{Q_OBJECTpublic:myClient(QWidget *parent = nullptr);~myClient();
private slots:void openFile();void send();void startTransfer();void updateClientProgress(qint64);void displayError(QAbstractSocket::SocketError);void on_openButton_clicked();void on_sendButton_clicked();private:Ui::myClient *ui;QTcpSocket *tcpClient;QFile *localFile;//要发送的文件qint64 totalBytes;//发送的总字节数qint64 bytesWritten;//已经发送数据的大小qint64 bytesToWrite;//剩余数据的大小qint64 payloadSize;//每次发送数据的大小QString fileName;//保存文件的路径QByteArray outBlock;//存放每次要发送的数据块,缓存区
};
#endif // MYCLIENT_H
myClinet.cpp
#include "myclient.h"
#include "ui_myclient.h"
#include <QtNetwork>
#include <QFileDialog>
#include <QDebug>myClient::myClient(QWidget *parent): QDialog(parent), ui(new Ui::myClient)
{ui->setupUi(this);payloadSize = 64 *1024;//64kb 1kb = 1024btotalBytes =0;bytesWritten = 0;bytesToWrite = 0;tcpClient = new QTcpSocket(this);connect(tcpClient,&QTcpSocket::connected,this,&myClient::startTransfer);connect(tcpClient,&QTcpSocket::bytesWritten,this,&myClient::updateClientProgress);void (QTcpSocket:: *tcpSocketsign)(QAbstractSocket::SocketError) = &QTcpSocket::error;connect(tcpClient,tcpSocketsign,this,&myClient::displayError);ui->sendButton->setEnabled(false);
}myClient::~myClient()
{delete ui;tcpClient->close();
}void myClient::openFile()
{fileName = QFileDialog::getOpenFileName(this);if(!fileName.isEmpty()){ui->sendButton->setEnabled(true);ui->clientStateLabel->setText(tr("打开文件%1成功!").arg(fileName));}
}void myClient::send()
{ui->sendButton->setEnabled(false);bytesWritten = 0;ui->clientStateLabel->setText(tr("连接中....."));tcpClient->connectToHost(ui->hostLineEdit->text(),ui->portLineEdit->text().toInt());
}void myClient::startTransfer()
{localFile = new QFile(fileName);if(!localFile->open(QFile::ReadOnly)){qDebug()<<"client open file error!";return ;}totalBytes = localFile->size();QDataStream sendOut(&outBlock,QIODevice::WriteOnly);sendOut.setVersion(QDataStream::Qt_5_13);QString currentFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/') - 1);sendOut<<qint64(0)<<qint64(0)<<currentFileName;totalBytes += outBlock.size();sendOut.device()->seek(0);sendOut<<totalBytes<<qint64(outBlock.size()-sizeof(qint64)*2);bytesToWrite = totalBytes-tcpClient->write(outBlock);ui->clientStateLabel->setText(tr("已连接"));outBlock.resize(0);
}void myClient::updateClientProgress(qint64 numBytes)
{bytesWritten += (int)numBytes;if(bytesToWrite > 0){outBlock = localFile->read(qMin(bytesToWrite,payloadSize));bytesToWrite -= (int)tcpClient->write(outBlock);outBlock.resize(0);}else{localFile->close();}ui->clientProgressBar->setMaximum(totalBytes);ui->clientProgressBar->setValue(bytesWritten);if(bytesWritten == totalBytes){ui->clientStateLabel->setText(tr("传文件%1成功").arg(fileName));localFile->close();tcpClient->close();}
}void myClient::displayError(QAbstractSocket::SocketError)
{qDebug()<<tcpClient->errorString();tcpClient->close();ui->clientProgressBar->reset();ui->clientStateLabel->setText(tr("客户端就绪"));ui->sendButton->setEnabled(true);
}void myClient::on_openButton_clicked()
{ui->clientProgressBar->reset();ui->clientStateLabel->setText(tr("状态:等待打开文件!"));openFile();
}void myClient::on_sendButton_clicked()
{send();
}
main.cpp中没有改动,所以便不再贴代码。下面用创建客户端的方法,来创建服务端的程序,基类依旧选择QDialog,修改类名为myServer,创建好项目后,在.ui文件中进行界面的实现,在.pro文件中添加“QT += network”,并保存。在myServer.h和myServer.cpp文件中添加代码,完整的代码如下。
myServer.h
#ifndef MYSERVER_H
#define MYSERVER_H#include <QDialog>
#include <QAbstractSocket>
#include <QTcpServer>QT_BEGIN_NAMESPACE
namespace Ui { class myServer; }
QT_END_NAMESPACEclass QTcpSocket;
class QFile;
class myServer : public QDialog
{Q_OBJECTpublic:myServer(QWidget *parent = nullptr);~myServer();
private slots:void start();void acceptConnection();void updateServerProgress();void displayError(QAbstractSocket::SocketError socketError);void on_startButton_clicked();private:Ui::myServer *ui;QTcpServer tcpServer;QTcpSocket *tcpServerConnection;//客户端连接对象qint64 totalBytes;//存放总大小信息qint64 bytesReceived;//接收的字节数qint64 fileNameSize;//文件名大小信息QString fileName;//存放文件名QFile *localFile;//本地文件QByteArray inBlock;//缓存区
};
#endif // MYSERVER_H
myServer.cpp
#include "myserver.h"
#include "ui_myserver.h"
#include <QtNetwork>
#include <QDebug>myServer::myServer(QWidget *parent): QDialog(parent), ui(new Ui::myServer)
{ui->setupUi(this);connect(&tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection()));
}myServer::~myServer()
{delete ui;
}void myServer::start()
{if(!tcpServer.listen(QHostAddress::LocalHost,6666)){qDebug()<<tcpServer.errorString();close();return ;}ui->startButton->setEnabled(false);totalBytes = 0;bytesReceived = 0;fileNameSize = 0;ui->serverStatusLabel->setText(tr("监听"));ui->serverProgressBar->reset();
}void myServer::acceptConnection()
{qDebug()<<"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 myServer::updateServerProgress()
{QDataStream in(tcpServerConnection);in.setVersion(QDataStream::Qt_5_13);if(bytesReceived <= sizeof(qint64)*2){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;fileName.prepend("./");localFile = new QFile(fileName);if(!localFile->open(QFile::WriteOnly)){qDebug()<<"server:open file error!";return ;}}else{qDebug()<<"没有接收到文件";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));fileName = " ";}
}void myServer::displayError(QAbstractSocket::SocketError socketError)
{qDebug()<<tcpServerConnection->errorString();tcpServerConnection->close();ui->serverProgressBar->reset();ui->serverStatusLabel->setText(tr("服务端就绪"));ui->startButton->setEnabled(true);
}void myServer::on_startButton_clicked()
{start();
}
现在可以编译运行了,先启动服务端程序,然后点击监听按钮,再启动客户端的程序,由于本程序实现的是本机的通信,所以客户端的主机名或IP这一栏中可以输入localhost或者127.0.0.1,端口为6666,程序中有些,点击打开按钮,弹出文件选择框,选择要传输的文件,像txt,png,jpg,doc,pdf等都可以传输,选好文件后,点击发送按钮,此时会在服务端和客户端显示传输的进度。
当然这只是一个非常简单的程序,对于刚入手的小白希望有所帮助,另外,在.ui文件中实现界面的时候,lineEdit的背景提示文本,需要在属性一栏中设置placeholderText的文本。此文可能是我在QtCreate快速入门一书中看到的,具体记不清了,留以记载,以备后用。