文章目录
1 发送文字
1.1 服务端
1.1.1 添加模块
在pro中加入网络模块:
QT += network
1.1.2 头文件coding
添加文件头:
#include <QAbstractSocket>
#include <QTcpServer>
添加类前置声明:
class QTcpServer;
添加私有声明:
private slots://槽函数声明
void sendMessage();//发送文字
private:
QTcpServer tcpServer;
1.1.3 源文件(cpp)coding
添加头文件:
#include <QtNetwork>
#include <QDebug>
在模块的构造函数中添加:
//创建实例对象
tcpServer = new QTcpServer(this);
//监听端口
if(!tcpServer->listen(QHostAddress::LocalHost, 6666)){//如果不是本地的6666端口,则报错,并关闭
qDebug() << tcpServer->errorString();
close();
}
//否则,则发射newConnection信号,连接
connect(tcpServer, &QTcpServer::newConnection,
this, &Server::sendMessage);
槽函数实现:
void Server::sendMessage()
{
//暂存要发送的数据
QByteArray block;
QDataStream out (&block, QIODevice::WriteOnly);
//设置数据流的版本(服务端要和客户端相同)
out.setVersion(QDataStream::Qt_5_6);
//预留两个字节的大小,便于后面填写数据的大小信息
out << (quint16)0;
out << tr("hello TCP!!!");//使用tr编码
//跳转到数据块开头
out.device()->seek(0);
//获得实际数据大小,填充前面空的两个字节
out << (quint64)(block.size() - sizeof (quint16));
//获得已建立连接的套接字
//获取连接套接字
QTcpSocket* clientConnection = tcpServer->nextPendingConnection();
//断开连接时,删除该套接字
connect(clientConnection, &QTcpSocket::disconnected,
clientConnection, &QTcpSocket::deleteLater);
//发送block数据
clientConnection->write(block);
//一直等所有套接字发送完毕后,关闭套接字
clientConnection->disconnectFromHost();
//发送数据成功后,显示提醒
ui->label->setText("send message successful!!!");
}
1. 2 客户端
1. 2.1 添加模块
在pro中加入网络模块:
QT += network
1. 2.2 头文件coding
添加头文件:
#include <QAbstractSocket>
添加类前置声明:
class QTcpSocket;
添加私有成员数据结构:
private:
QTcpSocket* tcpSocket;
QString message;
quint16 blockSize; //用来存放数据的大小
添加成员函数:
private slots:
void newConnect();
void readMessage();
void displayError(QAbstractSocket::SocketError);
void on_pushButton_clicked();
1. 2.3 源文件(cpp)coding
添加头文件:
#include <QtNetwork>
#include <QAbstractSocket>
#include <QDebug>
在模块的构造函数中添加:
tcpSocket = new QTcpSocket(this);
//可读的
connect(tcpSocket, SIGNAL(readyDead()), this, SLOT(readMessage()));
//错误的
connect(tcpSocket, SIGNAL(error(QAbstracSocket::SocketError)), this,
SLOT(displayError(QAbstractSocket::SocketError)));
添加readMessage槽函数定义:
void Client::readMessage()
{
QDataStream in(tcpSocket);
//设置数据流版本,要和服务器一样
in.setVersion(QDataStream::Qt_5_6);
//刚开始接收数据
if(blockSize == 0){
//判断接收数据是否大于两个字节
//也就是文件信息所占的字节
if(tcpSocket->bytesAvailable() < (int)sizeof(quint16)) return;
in >> blockSize;
}
//没有得到全部数据,继续接收
if(tcpSocket->bytesAvailable() < blockSize) return;
//将接收到的数据存放在变量中
in >> message;
//显示接收到的数据
ui->messageLabel->setText(message);
}
添加displayError槽函数定义:
void Client::displayError(QAbstractSocket::SocketError)
{
qDebug() << tcpSocket->errorString();
}
2 传输文件
2.1 服务端
2.1.1 添加模块
添加模块
在pro中加入网络模块:
QT += network
2.1.2 头文件coding
头文件以及类的前置声明:
#include <QAbstractSocket>
#include <QTcpServer>
class QTcpSocket;
class QFile;
添加私有数据结构:
private:
QTcpServer tcpServer;
QTcpSocket* tcpServerConnection;新增
qint64 totalBytes;//存放总大小信息
qint64 bytesReceived;//已收到数据的大小
qint64 fileNameSize;//文件名的大小信息
QString fileName;//存放文件名
QFile* localFile;//本地文件
QByteArray inBlock;//数据缓冲区
添加槽函数声明:
private slots:
void start();//开始监听
void acceptConnect();//初始化
void updateServerProgress();//更新QProgressBar
void displayError(QAbstractSocket::SocketError socketError);//展示错误
void on_startButton_clicked();//按钮点击事件
2.1.3 源文件coding
头文件声明:
#include <QtNetwork>
#include <QDebug>
构造函数添加
connect(&tcpServer, SIGNAL(newConnection()),
this, SLOT(acceptConnect()));
监听端口(定义在Button里):
// 开始监听按钮
void Server::on_startButton_clicked()
{
start();
}
监听事件:
void Server::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();//重设进度条
}
acceptConnect槽函数定义:
//初始化工作,每次接收完文件后,都要重新监听
void Server::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();
}
updateServerProgress槽函数定义:
void Server::updateServerProgress()
{
QDataStream in(tcpServerConnection);
in.setVersion(QDataStream::Qt_4_0);
// 如果接收到的数据小于16个字节,保存到来的文件头结构
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;
localFile = new QFile(fileName);
if (!localFile->open(QFile::WriteOnly)) {
qDebug() << "server: 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));
}
}
displayError槽函数定义:
void Server::displayError(QAbstractSocket::SocketError socketError)
{
qDebug() << tcpServerConnection->errorString();
tcpServerConnection->close();
ui->serverProgressBar->reset();
ui->serverStatusLabel->setText(tr("服务端就绪"));
ui->startButton->setEnabled(true);
}
2.2 客户端
2.2.1 添加模块
添加模块
添加模块
在pro中加入网络模块:
QT += network
2.2.2 头文件coding
头文件和前置类声明:
#include <QDialog>
#include <QAbstractSocket>
#include <Qfile>
class QTcpSocket;
class QFile;
私有数据数据结构成员定义:
private:
QTcpSocket* tcpClient;
QFile* localFile;//要发送的数据
qint64 totalBytes;//发送数据的大小
qint64 bytesWriten;//已发送数据大小
qint64 bytesToWrite;//剩余的数据大小
qint64 payloadSize; //每次发送数据的大小
QString fileName;//保存文件路径
QByteArray outBlock;//数据缓冲区
槽函数声明:
private slots:
void openFile();//打开文件
void send();//连接服务器
void startTransfer();//发送文件头结构(数据的大小、文件名大小、文件名)
void updateClientProgress(quint64);//跟新进度条
void displayError(QAbstractSocket::SocketError);//显示错误
void on_openButton_clicked();//打开文件
void on_sendButton_clicked();//发送文件
2.2.3 源文件coding
添加头文件:
#include <QtNetwork>
#include <QAbstractSocket>
#include <QDebug>
构造函数添加:
//变量初始化
payloadSize = 64 * 1024;//64kb
totalBytes = 0;
bytesWriten = 0;
bytesToWrite = 0;
tcpClient = new QTcpSocket(this);
//服务器连接成功时,发出conenct()信号,开始传送文件
connect(tcpClient, SIGNAL(connected()), this, SLOT(startTransfer()));
//更新进度题条
connect(tcpClient, SIGNAL(bytesWritten(qint64)), this,
SLOT(updateClientProgress(qint64)));
//展示错误
connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(displayError(QAbstracSocket::SocketError)));
打开文件按钮点击事件:
void Client::on_openButton_clicked()
{
ui->clientProgressBar->reset();
ui->clientStatusLabel->setText(tr("状态:等待打开文件"));
openFile();
}
openFile定义:
void Client::openFile()
{
fileName = QFileDialog::getOpenFileName(this);
if(!fileName.isEmpty()){
ui->sendButton->setEnabled(true);//按键能不能被按下
ui->clientStatusLabel->setText(tr("打开文件 %1 成功!").arg(fileName));
}
}
发送文件按钮点击事件:
void Client::on_sendButton_clicked()
{
send();
}
send函数定义:
void Client::send()
{
ui->sendButton->setEnabled(false);
//连接服务器
tcpClient->connectToHost(ui->hostLineEdit->text(),
ui->portLineEdit->text().toInt());
}
startTransfer槽函数定义:
void Client::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_6);
QString currentFileName = fileName.right(fileName.size()
- fileName.lastIndexOf('/') - 1);
//保留总大小信息,文件名大小信息,然后输入文件名
//总大小:总大小信息,文件名大小信息,文件名和实际文件大小总和
sendOut << qint64(0) << quint64(0) << currentFileName;
//整个数据的大小 = 文件头结构 + 实际文件大小
//整个数据的大小放在数据流最开始,占用第一个qint64(0)
//文件名大小占用第二个qint64(0)
totalBytes += outBlock.size();
sendOut.device()->seek(0);
//返回outblock信息
//用两个实际的大小代替两个qint64(0)空间
sendOut << totalBytes << qint64(outBlock.size() - sizeof (qint64)*2);
//发送完文件头结构后,剩余数据大小
bytesToWrite = totalBytes - tcpClient->write(outBlock);
ui->clientStatusLabel->setText(tr("已连接"));
outBlock.resize(0);
}
updateClientProgress槽函数定义:
void Client::updateClientProgress(quint64 numBytes)
{
//已发送数据的大小
bytesWriten += (int) numBytes;
//如果已经发送了数据
if(bytesToWrite > 0){
//每次发送payloadSize大小的数据,也就是64k
//如果不足64k, 就发送剩余的数据大小
outBlock = localFile->read(qMin(bytesToWrite, payloadSize));
bytesToWrite -= (int)tcpClient->write(outBlock);
//清空发送缓存
outBlock.resize(0);
}else{//没有任何发送的数据,则关闭文件
localFile->close();
}
//更新进度条
ui->clientProgressBar->setMaximum(totalBytes);
ui->clientProgressBar->setValue(bytesWriten);
//如果发送完毕
if(bytesWriten == totalBytes){
ui->clientStatusLabel->setText(tr("传送文件 %1 成功").arg(fileName));
localFile->close();
tcpClient->close();
}
}
displayError槽函数定义:
void Client::displayError(QAbstractSocket::SocketError)
{
qDebug() << tcpClient->errorString();
tcpClient->close();
ui->clientProgressBar->reset();
ui->clientStatusLabel->setText(tr("客户端就绪"));
ui->sendButton->setEnabled(true);
}