15、TCP传输文件
一、服务端,选择文件 发送文件
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传输数据,服务端发送数据,可能发送好几次,客户端才可能读一次;,所以 发送数据 与接受数据是不对等的;
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号