作者在 2015-08-30 10:51:52 发布以下内容
本文还是参考的《Qt及Qt Quick开发实战精解》一书中的第5个例子,即局域网聊天工具中的UDP聊天和TCP文件传送部分。另外http://www.yafeilinux.com/ 上有其源码和相关教程下载。
其发送端界面如下:
/root/图片/chat/server.png
/root/图片/chat/client.png
流程图如下:
server:
client:
TCP部分程序代码和注释如下:
dialog.h:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QTime>
class QTcpServer;
class QFile;
class QTcpSocket;
class QUdpSocket;
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void initser();// 初始化服务器。
private:
Ui::Dialog *ui;
qint16 tport;
qint16 uport;
qint16 u2port;
qint64 totalBytes;
qint64 writeBytes;
qint64 tobeWriteBytes;
qint64 payloadSize;//被初始化为第一个常量。
QByteArray outBlock;
QTcpSocket *clintcon;
QUdpSocket *udpSocket;
QTcpServer *tser;
QString fileName;
QString theFileName;
QFile *locFile;
QTime time;
protected:
void SendFileName(QString fileName);
QString getIP();
private slots:
void sendMsg();
void updClintProgress(qint64 numBytes);
void getFileName(QString name);
void refused();//关闭服务器。
void on_sCloseBtn_clicked();
void on_sOpenBtn_clicked();
void on_sSendBtn_clicked();
signals:
void sendFileName(QString fileName);
};
#endif // DIALOG_H
dialog.cpp:
#include "dialog.h"
#include "ui_dialog.h"
#include <QFile>
#include <QDebug>
#include <QMessageBox>
#include <QFileDialog>
#include <QTcpServer>
#include <QTcpSocket>
#include <QNetworkInterface>
#include "QObject"
#include <QUdpSocket>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <qtcpserver.h>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
uport = 8888;
u2port = 7777;
udpSocket->bind(u2port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(refused()));
//sendMessage(NewParticipant);
tport = 6666;
tser = new QTcpServer(this);
connect(tser,SIGNAL(newConnection()),this,SLOT(sendMsg()));
connect(this,SIGNAL(sendFileName(QString)),this,SLOT(getFileName(QString)));
initser();
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::initser()//初始化服务器。
{
totalBytes = 0;
tobeWriteBytes = 0;
writeBytes = 0;
ui->sOpenBtn->setEnabled(true);//open按钮可用
ui->sSendBtn->setEnabled(false);//发送按钮不可用
ui->label_2->setText("请选择要发送的文件");
tser->close();
}
void Dialog::refused()//关闭服务器。
{
tser->close();
ui->label_2->setText(tr("对方拒绝接收!"));
}
//发送文件
void Dialog::sendMsg()
{
qDebug() << "--------sendMsg start-----------" ;
ui->sSendBtn->setEnabled(false);//设置按钮不可用
clintcon = tser->nextPendingConnection();//获取一个已经连接tcp套接字。
connect(clintcon,SIGNAL(bytesWritten(qint64)),this,SLOT(updClintProgress(qint64)));
ui->label_2->setText(tr("开始传送文件 %1!").arg(theFileName));
locFile = new QFile(fileName);//只读方式打开文档
if(!locFile->open((QFile::ReadOnly))){
QMessageBox::warning(this,tr(""),tr("无法读取文件%1:\n %2").arg(fileName).arg(locFile->errorString()));
return;
}
totalBytes = locFile->size();//文件的总字节数。
qDebug() << "fileTotalBytes:" << totalBytes;
QDataStream sendOut(&outBlock,QIODevice::WriteOnly);
sendOut.setVersion(QDataStream::Qt_4_7);
time.start();
QString curFile = fileName.right(fileName.size() - fileName.lastIndexOf('/') -1 );//待发送文件名。
qDebug() << "curFile:" << curFile;
sendOut << qint64(0) << qint64(0) << curFile;
totalBytes += outBlock.size();//文件名大小信息+实际文件大小。
sendOut.device()->seek(0);//设置文件指针从0开始
sendOut << totalBytes << qint64((outBlock.size() - sizeof(qint64)*2));
tobeWriteBytes = totalBytes - clintcon->write(outBlock);//发送缓存区的数据,同时修改待发送的数据长度。
qDebug() << "tobeWriteBytes:" << tobeWriteBytes;
qDebug() << "outBlock:" << outBlock;
outBlock.resize(0);
}
void Dialog::on_sCloseBtn_clicked()
{
if(tser->isListening()){
tser->close();//当tcp正在监听时,关闭tcp服务器端应用,即按下close键时就不监听tcp请求了
if(locFile->isOpen()){
locFile->close();
}
clintcon->abort();//Socket::终止当前的连接,禁止写入缓存区。
}
close();
}
void Dialog::on_sOpenBtn_clicked()
{
fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
"/home"
);//tr("Images (*.png *.xpm *.jpg *.txt *.gz)")
//fileName = QFileDialog::getOpenFileName(this);
if(theFileName.isEmpty()){
theFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/') - 1);
ui->label_2->setText(tr("要发送的文件为:%1").arg(theFileName));
qDebug() << "thefilename:" << theFileName;
}
qDebug() << "fileName:" << fileName;
ui->sSendBtn->setEnabled(true);
}
void Dialog::on_sSendBtn_clicked()
{
qint8 a;
a = tser->listen(QHostAddress::Any,tport);
if(!a){
qDebug() << tser->errorString();
close();
}
qDebug() << a;
ui->label_2->setText(tr("等待对方接收...."));
emit sendFileName(theFileName);
qDebug() << "-----emit- success!------";
ui->sSendBtn->setEnabled(false);
}
//进度条修改进度。
void Dialog::updClintProgress(qint64 numBytes)
{
qDebug() << "udpClintProgress start....";
qApp->processEvents();
//qDebug() << "writeBytes1:"<< writeBytes;
//writeBytes += (int)numBytes;
//qDebug() << "writeBytes2:" << writeBytes;
if(tobeWriteBytes > 0){//没发送完。
outBlock = locFile->read(qMin(tobeWriteBytes,payloadSize));//每次最多发送64k大小。
tobeWriteBytes -= (int)clintcon->write(outBlock);
writeBytes += (totalBytes - tobeWriteBytes);//已经发送了的。
outBlock.resize(0);
}else {
locFile->close();
}
ui->progressBar->setMaximum(totalBytes);
ui->progressBar->setValue(writeBytes);
float useTime = time.elapsed();
double speed = writeBytes/useTime*1000000;
ui->label_2->setText(tr("(%1 m/s)").arg(speed));
qDebug() << "writeBytes:"<< writeBytes;
qDebug() << "totalBytes:" << totalBytes;
if(writeBytes == totalBytes){
locFile->close();
tser->close();
ui->label_2->setText(tr("文件:%1发送成功!").arg(theFileName));
}
}
//获取文件名
void Dialog::getFileName(QString name)
{
qDebug() << "------1getFileName--------" << name;
theFileName = name;
//qDebug() << "fileNamesize" << strlen(fileName);
SendFileName(theFileName);
}
//发送文件名
void Dialog::SendFileName(QString fileName)
{
QByteArray data;
QDataStream out(&data, QIODevice::WriteOnly);
QString address,clientaddr;
address = getIP();
//clientaddr = getIP();
//QString clientAddress = ui->userTableWidget->item(row, 2)->text();//(row,,2)为ip地址
out << address << fileName;//发送本地ip,所发送的文件名
qDebug() << "SendFileName :" << fileName;
qDebug() << "server IP:" << address;
int a= udpSocket->writeDatagram(data,data.length(),QHostAddress::Broadcast, uport);//向8888端口广播
qDebug() << "a" << a;
}
//获取IP
QString Dialog::getIP()
{
QList<QHostAddress> list = QNetworkInterface::allAddresses();
foreach (QHostAddress address, list) {
if(address.protocol() == QAbstractSocket::IPv4Protocol){
if(address.toString().contains("192.168.1")||address.toString().contains("127.0"))
continue;
return address.toString();
}
}
return 0;
}
client.h
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QHostAddress>
#include <QFile>
#include <QTime>
#include <QDebug>
#include <QMessageBox>
#include"thread.h"
enum MessageType{FileName, refuse};
class QTcpSocket;
class QUdpSocket;
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
void setHostaddr(QHostAddress addr); //获取主机ip
void setFileName(QString FileName); //获取文件保存路径
void reciveFileNmae();
private slots:
void on_cCancelBtn_clicked();
void on_cClosebtn_clicked();
protected:
void SendRefuse(MessageType type);
void closeEvent(QCloseEvent *);
void hansPendingFile(QString serAddress,QString fileName);
QString getIP();
private:
Thread *b;
Ui::Dialog *ui;
QUdpSocket *udpSocket;
QTcpSocket *cClint;
QHostAddress hostAddr;
qint16 tport;
qint16 uport;
qint16 u2port;
quint16 blockSize;
qint64 totalBytes;
qint64 recevBytes;
qint64 fileNameSize;
QString FileName;
QString *SerAdress;
QFile *locFile; //要接收的文件
QByteArray inBlock; //缓存一次接收的数据
QTime Time;
private slots:
void processPendingDatarams();
void newConn();
void readMsg();
void displayError(QAbstractSocket::SocketError);
signals:
};
#endif // DIALOG_H
client.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include <QTcpSocket>
#include <QUdpSocket>
#include <QTime>
#include <QDebug>
#include <QMessageBox>
#include <QNetworkInterface>
#include <QFile>
#include <QFileDialog>
#include <QDateTime>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <qiodevice.h>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowTitle("client");
totalBytes = 0;
recevBytes = 0;
fileNameSize = 0;
udpSocket = new QUdpSocket(this);
uport = 8888;
u2port = 7777;
udpSocket->bind(QHostAddress::Broadcast, uport, QUdpSocket::ShareAddress| QUdpSocket::ReuseAddressHint);
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(processPendingDatarams()));
tport = 6666;
cClint = new QTcpSocket(this);
connect(cClint,SIGNAL(readyRead()),this,SLOT(readMsg()));
connect(cClint, SIGNAL(error(QAbstractSocket::SocketError)), this,
SLOT(displayError(QAbstractSocket::SocketError)));
}
Dialog::~Dialog()
{
delete ui;
}
// 设置地址
void Dialog::setHostaddr(QHostAddress addr)
{
qDebug() << "--------setHostaddr---------";
hostAddr = addr;
qDebug() << "hostAddr:" << hostAddr;
newConn();
}
//设置文件名
void Dialog::setFileName(QString FileName)
{
locFile = new QFile(FileName);
}
void Dialog::newConn() // 设置与服务器建立连接的。
{
qDebug() << "---------newConn start---------";
blockSize = 0;
cClint->abort();
cClint->connectToHost(hostAddr,tport);
Time.start();
}
//接收文件
void Dialog::readMsg(){
qDebug() << "-----------readMsg start-----------";
QDataStream in(cClint);
in.setVersion(QDataStream::Qt_4_7);
float useTime = Time.elapsed();
if(recevBytes <= sizeof(qint64)*2){
if((cClint->bytesAvailable() >= sizeof(qint64)*2)&& fileNameSize == 0){
//接收数据总大小信息和数据文件名大小。
in >> totalBytes >> fileNameSize;
qDebug() << "totalBytes:" << totalBytes;
qDebug() << "fileNameSize:" << fileNameSize;
recevBytes += sizeof(qint64)*2;
qDebug() << "1recevBytes:" << recevBytes;
}
if((cClint->bytesAvailable() >= fileNameSize) && (fileNameSize != 0)){
//开始接收文件,并建立文件。
in >> FileName;
qDebug() << "FileName:" << FileName;
recevBytes += fileNameSize;
qDebug() << "2recevBytes:" << recevBytes;
if(!locFile->open(QFile::WriteOnly)){
QMessageBox::warning(this,tr("应用程序"),tr("无法读取文件%1: \n%2").arg(FileName).arg(locFile->errorString()));
return;
}
}else{
return;
}
}
qDebug() << "-----------BytesArray write newFile of start-----------";
if(recevBytes < totalBytes){
recevBytes += cClint->bytesAvailable(); //把缓存区的内容写入文件。
inBlock = cClint->readAll();
locFile->write(inBlock);
inBlock.resize(0); //清空缓存区
}
ui->progressBar->setMaximum(totalBytes);
ui->progressBar->setValue(recevBytes);//设置进度条的进度
double speed = recevBytes/useTime;
ui->label->setText(tr("(%1 m/s)").arg(speed));
qDebug() << "recevBytes:" << recevBytes;
qDebug() << "totalBytes:" << totalBytes;
if(recevBytes == totalBytes){
locFile->close();
cClint->close();
ui->label->setText(tr("接收文件 %1完毕 ").arg(FileName));
}
}
void Dialog::closeEvent(QCloseEvent *)
{
on_cClosebtn_clicked();
}
void Dialog::on_cCancelBtn_clicked()
{
cClint->abort();
if(locFile->isOpen()){
locFile->close();
}
}
void Dialog::on_cClosebtn_clicked() //关闭服务
{
cClint->abort();
if(locFile->isOpen()){
locFile->close();
}
close();
}
//处理接收到的文件名
void Dialog::hansPendingFile(QString serAddress,QString fileName)
{
QString cIpAddress = getIP(); //"192.168.31.119";
qDebug() << "cIpAddress:" << cIpAddress;
int btn = QMessageBox::information(this,tr("接收文件"),
tr("文件%1是否接收?").arg(fileName),
QMessageBox::Yes,QMessageBox::No);
if(btn == QMessageBox::Yes){
QString name = QFileDialog::getSaveFileName(0,tr("保存文件"),fileName);
if(!name.isEmpty()){
Dialog *clint = new Dialog(this);
setFileName(name);
setHostaddr(QHostAddress(serAddress));//默认一台电脑用。
}
}
else{
SendRefuse(refuse);
sleep(2);
on_cClosebtn_clicked();
}
}
QString Dialog::getIP()
{
QList<QHostAddress> list = QNetworkInterface::allAddresses();
foreach (QHostAddress address, list) {
if(address.protocol() == QAbstractSocket::IPv4Protocol){
if(address.toString().contains("192.168.1")||address.toString().contains("127.0"))
continue;
return address.toString();
}
}
return 0;
}
//处理udp发送名字信号时函数
void Dialog::processPendingDatarams()
{
qDebug() << "pending Datarams......." ;
while(udpSocket->hasPendingDatagrams()){
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size());
QDataStream in(&datagram,QIODevice::ReadOnly);
QString time = QDateTime::currentDateTime()
.toString("yyyy-MM-dd hh:mm:ss");
//QString clientAddress, fileName;
QString fileName,serAddress;
in >> serAddress >> fileName;
qDebug() << "pending fileName" << fileName;
hansPendingFile(serAddress,fileName);
}
}
void Dialog::SendRefuse(MessageType refuse)
{
QByteArray data;
QDataStream out(&data, QIODevice::WriteOnly);
out << refuse;
qDebug() << "data" << data;
int a= udpSocket->writeDatagram(data,data.length(),QHostAddress::LocalHost, u2port);
qDebug() << "------------writeDatagram a:" << a;
}
void Dialog::displayError(QAbstractSocket::SocketError socketError)
{
switch(socketError)
{
//RemoteHostClosedError为远处主机关闭了连接时发出的错误信号
case QAbstractSocket::RemoteHostClosedError : break;
default : qDebug() << cClint->errorString();
}
}