一、服务端,选择文件 发送文件
1、服务器端,使用UI设计师编辑好界面
2、创建成员对象
QTcpServer *tcpserver; // 监听·套接字
QTcpSocket *tcpsocket; // 通信套接字
QFile file; // 选择文件对象
QString fileName; // 文件名字
qint64 fileSize; // 文件大小
qint64 SendSize; // 已发送文件的大小
void SendData(); // 发送文件数据
QTimer timer; // 定时器对象
3、与客户端建立连接 并处理客户端的反馈信息
setWindowTitle("服务器端口8888");
tcpserver = new QTcpServer(this);
// 初始设置 两个按钮都不允许按下
ui->buttonSend->setEnabled(false);
ui->buttonSelect->setEnabled(false);
// 监听网口绑定的任意IP 端口位8888
tcpserver->listen(QHostAddress::Any,8888);
// 处理新的连接请求
connect(tcpserver,&QTcpServer::newConnection,
[=]()
{
// 获取最近的套接字
tcpsocket = tcpserver->nextPendingConnection();
QString ip = tcpsocket->peerAddress().toString(); // 获取套接字的IP
quint16 port = tcpsocket->peerPort(); // 获取套接字的端口
QString str = QString("[%1:%2]:连接成功").arg(ip).arg(port); // 组包
ui->textEdit->setText(str);
// 成功连接后可以按按钮
ui->buttonSelect->setEnabled(true);
// 处理客户端发送来的信息
connect(tcpsocket,&QTcpSocket::readyRead,
[=]()
{
QByteArray arr = tcpsocket->readAll();
if(QString(arr) == "file done")
{
// 文件接收完成
ui->textEdit->append("文件发送完成");
file.close();
// 断开客户端
tcpsocket->disconnectFromHost();
tcpsocket->close();
}
});
});
// 定时器发送文件信息
connect(&timer,&QTimer::timeout,
[=]()
{
timer.stop(); // 停止定时器
SendData(); // 发送头信息
});
4、与客户端建立连接后,服务端选择需要发送的文件
// 选择需要发送的文件
void ServerWidget::on_buttonSelect_clicked()
{
QString filepath = QFileDialog::getOpenFileName(this,"open","../");
if(false == filepath.isEmpty())
{
// 获取文件信息
fileName.clear();
fileSize = 0;
QFileInfo info(filepath);
fileName = info.fileName();
fileSize = info.size();
SendSize = 0; // 发送文件大小
// 只读方式打开文
file.setFileName(filepath);
bool isok = file.open(QIODevice::ReadOnly);
if(false == isok)
{
qDebug()<< "只读方式打开文件失败";
}else {
ui->textEdit->append(filepath);
ui->buttonSelect->setEnabled(false);
ui->buttonSend->setEnabled(true);
}
}else {
qDebug() <<"选择文件路径出错";
}
}
5、发送文件,需要先发送文件的信息
// 发送TCP头 发送文件名 文件大小信息
void ServerWidget::on_buttonSend_clicked()
{
// 先发送文件头信息
QString head = QString("%1##%2").arg(fileName).arg(fileSize);
qint64 len = tcpsocket->write(head.toUtf8());
if(len > 0){
// 头部信息发送成功
// 防止沾包 启动定时器 需要延时20ms
timer.start(20);
}else {
qDebug() << "头部信息发送失败";
file.close();
ui->buttonSelect->setEnabled(true);
ui->buttonSend->setEnabled(false);
}
}
6、发送文件
// 发送文件
void ServerWidget::SendData()
{
qint64 len=0;
do{
char buf[4*1024]={0};
len=0;
len = file.read(buf,sizeof(buf)); // 读文件
len =tcpsocket->write(buf,len); // 发送文件
SendSize += len; // 发送文件的长度增加
}while(len>0);
if(SendSize == fileSize) // 已发送的文件大小 等于 文件大小 文件发送完毕
{
ui->textEdit->append("文件发送完毕");
ui->buttonSend->setEnabled(false);
file.close(); // 关闭打开的文件
// 主动与客户端断开
tcpsocket->disconnectFromHost();
tcpsocket->close();
}
}
二、处理完服务端,处理客户端
1、客户端使用UI设计师 编辑如下
2、创建成员变量
QTcpSocket *tcpsocket; // 通信套接字
QFile file; // 文件操作 对象
QString fileName; // 文件名称
qint64 fileSize; // 文件大小
qint64 recvSize; // 接收到文件大小
bool isStart;
3、请求连接至服务端
// 请求与服务端连接
void ClientWidget::on_buttonConnect_clicked()
{
QString ip = ui->lineEditIP->text(); // 获取IP
quint16 port = ui->lineEditPort->text().toInt(); // 获取端口
tcpsocket->connectToHost(QHostAddress(ip),port); // 连接至服务端
ui->progressBar->setValue(0);// 重新连接时 进度条复位
}
4、处理头部信息 以及接受数据处理
setWindowTitle("客户端");
ui->progressBar->setValue(0); // 初始化进度条位置位0
tcpsocket = new QTcpSocket(this);
isStart = true;
// 准备接收数据
connect(tcpsocket,&QTcpSocket::readyRead,
[=]()
{
// 读取服务端发送来的数据
QByteArray buf = tcpsocket->readAll();
QString str = QString(buf); // 将字节数组转为字符串
//处理头部信息
if(true == isStart)
{
isStart = false;
// 服务端组包
// QString head = QString("%1##%2").arg(fileName).arg(fileSize);
// 获取文件信息 拆包
fileName = str.section("##",0,0); // 获取文件名称
fileSize = str.section("##",1,1).toInt(); // 文件大小
recvSize = 0; // 接收到的文件大小至0
// 打开文件
file.setFileName(fileName);
bool isok = file.open(QIODevice::WriteOnly);
if(false == isok)
{
// 打开文件出错
}
QString str = QString("接收文件:[%1:%2]").arg(fileName).arg(fileSize/1024);
QMessageBox::information(this,"文件信息",str);
// 设置进度条 注意进度条数据越界
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(int(fileSize/1024));
ui->progressBar->setValue(0);
}else { // 数据处理
// 保存文件
qint64 len = file.write(buf);
if(len > 0) // 读取到了数据
{
recvSize += len; // 接受到的数据累积
}
// 设置进度条位置
ui->progressBar->setValue(int(recvSize/1024));
// 接收数据完成
if(recvSize == fileSize)
{
// 给客户端发提示信息
tcpsocket->write("file done");
file.close(); // 关闭文件
QMessageBox::information(this,"完成","文件接收完成");
tcpsocket->disconnectFromHost();
tcpsocket->close();
}
}
});
5、最后结果
6、最后需要注意一点:
tcp传输数据,服务端发送数据,可能发送好几次,客户端才可能读一次;,所以 发送数据 与接受数据是不对等的;