1.概述
在建立连接的基础上增加了发送文件的功能,在接收端和发送端定义了一个枚举类型,用于判别发送的是文件还是文本消息
enum MSG{
messAge,
fiLe
};
2.客户端
客户端ui
主要函数
1.获取端口号和ip地址,进行连接,再次点击即可断开连接
void Widget::on_btn_listen_clicked()
{
QString address=ui->lineEdit_address->text();
qint16 port=ui->lineEdit_port->text().toInt();
QHostAddress ip=QHostAddress(address);
if(!conState)
{
my_client->connectToHost(ip,port);
if(!my_client->waitForConnected(5000))
{
QMessageBox::critical(this,"错误",my_client->errorString());
return ;
}
else
{
ui->btn_listen->setText("断开连接");
connect(my_client,&QTcpSocket::readyRead,this,&Widget::readyReadClientSlot);
conState=true;
}
}
else
{
conState=false;
my_client->close();
qDebug()<<"断开连接"<<endl;
ui->btn_listen->setText("开始连接");
}
}
2.点击发送消息按钮,获取文本框内容,将消息发送出去,其中type为文本消息类型
void Widget::on_btn_send_clicked()
{
QString str=ui->textEdit->toPlainText();
QString res;
res+="客户端:"+str;
QByteArray arr;
QDataStream data(&arr,QIODevice::WriteOnly);
MSG type=messAge;
data<<type<<res.toLocal8Bit();
my_client->write(arr);
ui->textBrowser->append(res);
ui->textEdit->clear();
ui->textEdit->setFocus();
}
3.接收客户端发来的文本消息
void Widget::readyReadClientSlot()
{
int len=my_client->bytesAvailable();
QByteArray arr=my_client->read(len>65536?65536:len);
QString res=QString::fromLocal8Bit(arr);
ui->textBrowser->append(res);
}
4.浏览文件
void Widget::on_btn_openfile_clicked()
{
QString path=QFileDialog::getOpenFileName(this,"选择文件","E:/");
ui->lineEdit_path->setText(path);
}
5.点击发送文件按钮,首先会将文件名和文件大小传输过去,以作判断传输是否完成依据
void Widget::on_btn_sendfile_clicked()
{
if(!conState)//如果没有连接客户端弹出警告
{
QMessageBox::warning(this,"错误","没有连接服务端");
return;
}
QString path=ui->lineEdit_path->text();//得到文件路径
QFileInfo info(path);
//获取文件信息
filename=info.fileName();
filesize=info.size();
sendsize=0;//初始化发送的文件大小为0
QByteArray arr;
MSG type=fiLe;//发送的内容为文件类型
QDataStream stream(&arr,QIODevice::WriteOnly);
stream<<type<<filename<<filesize;//写入文件名和文件大小
qDebug()<<filename<<" "<<filesize<<endl;
//打开文件
file.setFileName(path);
file.open(QIODevice::ReadOnly);
ui->progressBar->setMaximum(filesize);
my_client->write(arr);//发送内容到服务端
connect(my_client, &QTcpSocket::bytesWritten, this, &Widget::sendtxtSLot);
//resarr=arr;
//emit this->sendFileToYes();//发送完后 发送已经发完文件信息的信号
}
6.当文件信息传输完成后,会发送bytesWritten信号,后继每次传输完成也会发送这个信号,接着传输文件内容,当内容传输完成后,关闭文件,将filesize(发送的文件大小)和sendsize(已发送的文件大小)初始化为0,断开信号与槽,免得与文本消息冲突
void Widget::sendtxtSLot()
{
//timer->start(5000);
qDebug()<<"准备开始发送文件"<<endl;
qDebug()<<"文件信息:"<<filename<<filesize<<endl<<"已经发送的大小:"<<sendsize<<endl;
if(sendsize<filesize)//当发送大小小于文件大小执行 文件比较小就换成了if 一次能读完 换成while现在还有bug
{
//connect(timer,&QTimer::timeout,this,&Widget::sendfiletxt);
qDebug()<<"开始发送文件"<<endl;
QByteArray arr=file.read(1024*10);//一次读取的内容
QByteArray arrsend;
QDataStream data(&arrsend,QIODevice::WriteOnly);
MSG type=fiLe;//发送的内容为文件类型
data<<type<<arr;//读入文件类型和文件内容
my_client->write(arrsend);//发送数据内容
sendsize+=arr.size();
QString strfile=QString::fromUtf8(arr);
ui->textBrowser->append(strfile);
qDebug()<<"更新发送的大小"<<sendsize<<endl;
ui->progressBar->setValue(sendsize);
}
if(filesize==sendsize)
{
qDebug()<<"文件发送完成"<<endl;
file.close();
filesize=0;
sendsize=0;
//timer->stop();
// disconnect(timer,&QTimer::timeout,this,&Widget::sendfiletxt);
disconnect(my_client, &QTcpSocket::bytesWritten, this, &Widget::sendtxtSLot);
}
}
完整代码-客户端
头文件:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTcpSocket>
#include<QDebug>
#include<QMessageBox>
#include<QFile>
#include<QTimer>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
enum MSG{
messAge,
fiLe
};
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
signals:
void sendFileToYes();//测试bug用的信号,已经废弃
private slots:
void on_btn_listen_clicked();//开始/断开连接
void on_btn_send_clicked();//发送文本消息
void readyReadClientSlot();//接收服务端文本消息
void on_btn_openfile_clicked();//浏览文件
void on_btn_sendfile_clicked();//发送文件,首先发送文件信息过去
void sendtxtSLot();//发送文件内容
private:
Ui::Widget *ui;
QTcpSocket *my_client;
bool conState;
//文件
QString filename;
int filesize;
int sendsize;
QFile file;
QByteArray resarr;
QTimer *timer;//测试bug用,已舍弃
};
#endif // WIDGET_H
cpp:
#include "widget.h"
#include "ui_widget.h"
#include<QHostAddress>
#include<QFileDialog>
#include<QFileInfo>
#include<QDataStream>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("客户端");
timer =new QTimer(this);
ui->progressBar->setValue(0);
my_client=new QTcpSocket();
conState=false;
connect(this,&Widget::sendFileToYes,this,&Widget::sendtxtSLot);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_btn_listen_clicked()
{
QString address=ui->lineEdit_address->text();
qint16 port=ui->lineEdit_port->text().toInt();
QHostAddress ip=QHostAddress(address);
if(!conState)
{
my_client->connectToHost(ip,port);
if(!my_client->waitForConnected(5000))
{
QMessageBox::critical(this,"错误",my_client->errorString());
return ;
}
else
{
ui->btn_listen->setText("断开连接");
connect(my_client,&QTcpSocket::readyRead,this,&Widget::readyReadClientSlot);
conState=true;
}
}
else
{
conState=false;
my_client->close();
qDebug()<<"断开连接"<<endl;
ui->btn_listen->setText("开始连接");
}
}
void Widget::on_btn_send_clicked()
{
QString str=ui->textEdit->toPlainText();
QString res;
res+="客户端:"+str;
QByteArray arr;
QDataStream data(&arr,QIODevice::WriteOnly);
MSG type=messAge;
data<<type<<res.toLocal8Bit();
my_client->write(arr);
ui->textBrowser->append(res);
ui->textEdit->clear();
ui->textEdit->setFocus();
}
void Widget::readyReadClientSlot()
{
int len=my_client->bytesAvailable();
QByteArray arr=my_client->read(len>65536?65536:len);
QString res=QString::fromLocal8Bit(arr);
ui->textBrowser->append(res);
}
void Widget::on_btn_openfile_clicked()
{
QString path=QFileDialog::getOpenFileName(this,"选择文件","E:/");
ui->lineEdit_path->setText(path);
}
void Widget::on_btn_sendfile_clicked()
{
if(!conState)//如果没有连接客户端弹出警告
{
QMessageBox::warning(this,"错误","没有连接服务端");
return;
}
QString path=ui->lineEdit_path->text();//得到文件路径
QFileInfo info(path);
//获取文件信息
filename=info.fileName();
filesize=info.size();
sendsize=0;//初始化发送的文件大小为0
QByteArray arr;
MSG type=fiLe;//发送的内容为文件类型
QDataStream stream(&arr,QIODevice::WriteOnly);
stream<<type<<filename<<filesize;//写入文件名和文件大小
qDebug()<<filename<<" "<<filesize<<endl;
//打开文件
file.setFileName(path);
file.open(QIODevice::ReadOnly);
ui->progressBar->setMaximum(filesize);
my_client->write(arr);//发送内容到服务端
connect(my_client, &QTcpSocket::bytesWritten, this, &Widget::sendtxtSLot);
//resarr=arr;
//emit this->sendFileToYes();//发送完后 发送已经发完文件信息的信号
}
void Widget::sendtxtSLot()
{
//timer->start(5000);
qDebug()<<"准备开始发送文件"<<endl;
qDebug()<<"文件信息:"<<filename<<filesize<<endl<<"已经发送的大小:"<<sendsize<<endl;
if(sendsize<filesize)//当发送大小小于文件大小执行 文件比较小就换成了if 一次能读完 换成while现在还有bug
{
//connect(timer,&QTimer::timeout,this,&Widget::sendfiletxt);
qDebug()<<"开始发送文件"<<endl;
QByteArray arr=file.read(1024*10);//一次读取的内容
QByteArray arrsend;
QDataStream data(&arrsend,QIODevice::WriteOnly);
MSG type=fiLe;//发送的内容为文件类型
data<<type<<arr;//读入文件类型和文件内容
my_client->write(arrsend);//发送数据内容
sendsize+=arr.size();
QString strfile=QString::fromUtf8(arr);
ui->textBrowser->append(strfile);
qDebug()<<"更新发送的大小"<<sendsize<<endl;
ui->progressBar->setValue(sendsize);
}
if(filesize==sendsize)
{
qDebug()<<"文件发送完成"<<endl;
file.close();
filesize=0;
sendsize=0;
//timer->stop();
// disconnect(timer,&QTimer::timeout,this,&Widget::sendfiletxt);
disconnect(my_client, &QTcpSocket::bytesWritten, this, &Widget::sendtxtSLot);
}
}
服务端:
服务端ui
主要函数
1.获得端口号和ip地址进行监听,再次点击即可断开监听
void Widget::on_btn_listen_clicked()
{
QString ip=ui->lineEdit_address->text();
qint16 port=ui->lineEdit_port->text().toInt();
QHostAddress address=QHostAddress(ip);
if(!my_sever->isListening())
{
if(!my_sever->listen(address,port))
{
QMessageBox::critical(this,"错误",my_sever->errorString());
return ;
}
else
{
ui->btn_listen->setText("停止监听");
connect(my_sever,&QTcpServer::newConnection,this,&Widget::newConnectionSlot);
}
}
else
{
ui->btn_listen->setText("开始监听");
my_sever->close();
}
}
2.将文本消息发送到每个客户端
void Widget::on_btn_send_clicked()
{
QString str=ui->textEdit->toPlainText();
ui->textEdit->clear();
ui->textEdit->setFocus();
QString res;
res+="服务端: "+str;
QByteArray arr=res.toLocal8Bit();
ui->textBrowser->append(res);
for(int i=0;i<my_client.size();i++)
{
my_client[i]->write(arr);
}
}
3.当服务端发出newConnection信号时,将客户端加入到my_client的一员中,初始化接收到的文件大小和已接收的文件大小
void Widget::newConnectionSlot()
{
qDebug()<<"新用户来了"<<endl;
QTcpSocket* sock=my_sever->nextPendingConnection();
my_client.append(sock);
connect(sock,&QTcpSocket::readyRead,this,&Widget::readyReadSlot);
filesize=0;
recvsize=0;
}
4.接收来自客户端发送的文件或文本消息,首先获得是哪个客户端发送来的消息,然后判断是文本消息类型还是文件类型
注意:if(recvsize<filesize)和if(filesize==0)顺序不能颠倒,否则就会多执行一次if(recvsize<filesize)分支,在此卡了很久的bug!!!
void Widget::readyReadSlot()
{
QTcpSocket *sock=qobject_cast<QTcpSocket*>(sender());
sendnum++;//触发一次槽函数,客户端发来消息的次数加一
ui->lcdNumber->display(sendnum);
int type;//判断客户端发送的消息类型 {messAge:普通消息,fiLe:文件}
int len=sock->bytesAvailable();
QByteArray arr=sock->read(len>65536?65536:len);
QByteArray arrtxt;//普通消息内容
QByteArray arrfile;//文件内容
QDataStream data(&arr,QIODevice::ReadOnly);
QString str;
data>>type;//读入类型
switch (type) {
case messAge://普通消息分支
data>>arrtxt;
str=QString::fromLocal8Bit(arrtxt);
ui->textBrowser->append(str);
break;
case fiLe://文件分支
qDebug()<<"准备接收文件"<<endl;
if(recvsize<filesize)//当接收到的内容不完整时继续读取
{
qDebug()<<"开始接收文件内容"<<endl;
data>>arrfile;//读入文件内容
file.write(arrfile);
recvsize+=arrfile.size();
qDebug()<<"接受的大小:"<<recvsize<<endl;
ui->progressBar->setValue(recvsize);
}
if(filesize==0)//如果文件大小为0,说明才开始传输,首先客户端要传输文件名字,和文件大小
{
qDebug()<<"接收文件信息"<<endl;
data>>filename>>filesize;//读入文件名和文件大小
qDebug()<<"文件信息:"<<recvsize<<" "<<filesize<<endl;
ui->progressBar->setMaximum(filesize);//将进度条的最大值设置为客户端传入文件大小
//打开文件
file.setFileName(filename);
file.open(QIODevice::WriteOnly);
}
if(recvsize==filesize)
{
qDebug()<<"关闭接收文件内容"<<endl;
file.close();
filesize=0;
recvsize=0;
}
break;
default:
break;
}
}
完整代码-服务端
头文件:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTcpServer>
#include<QTcpSocket>
#include<QDebug>
#include<QMessageBox>
#include<QFile>
#include<QFileInfo>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
enum MSG{
messAge,
fiLe
};
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_btn_listen_clicked();//开启、关闭监听
void on_btn_send_clicked();//发送文本消息到客户端
void readyReadSlot();//接收客户端文本消息
void newConnectionSlot();
private:
Ui::Widget *ui;
QTcpServer *my_sever;
QList<QTcpSocket*> my_client;
QString filename;
int filesize;
int recvsize;
QFile file;
int sendnum;//测试用,获得客户端发来了几次消息
};
#endif // WIDGET_H
cpp:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
my_sever=new QTcpServer();
this->setWindowTitle("服务端");
ui->progressBar->setValue(0);
sendnum=0;
ui->lcdNumber->display(sendnum);
//connect(my_sever,&QTcpServer::newConnection,this,&Widget::readyReadSlot);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_btn_listen_clicked()
{
QString ip=ui->lineEdit_address->text();
qint16 port=ui->lineEdit_port->text().toInt();
QHostAddress address=QHostAddress(ip);
if(!my_sever->isListening())
{
if(!my_sever->listen(address,port))
{
QMessageBox::critical(this,"错误",my_sever->errorString());
return ;
}
else
{
ui->btn_listen->setText("停止监听");
connect(my_sever,&QTcpServer::newConnection,this,&Widget::newConnectionSlot);
}
}
else
{
ui->btn_listen->setText("开始监听");
my_sever->close();
}
}
void Widget::on_btn_send_clicked()
{
QString str=ui->textEdit->toPlainText();
ui->textEdit->clear();
ui->textEdit->setFocus();
QString res;
res+="服务端: "+str;
QByteArray arr=res.toLocal8Bit();
ui->textBrowser->append(res);
for(int i=0;i<my_client.size();i++)
{
my_client[i]->write(arr);
}
}
void Widget::readyReadSlot()
{
QTcpSocket *sock=qobject_cast<QTcpSocket*>(sender());
sendnum++;//触发一次槽函数,客户端发来消息的次数加一
ui->lcdNumber->display(sendnum);
int type;//判断客户端发送的消息类型 {messAge:普通消息,fiLe:文件}
int len=sock->bytesAvailable();
QByteArray arr=sock->read(len>65536?65536:len);
QByteArray arrtxt;//普通消息内容
QByteArray arrfile;//文件内容
QDataStream data(&arr,QIODevice::ReadOnly);
QString str;
data>>type;//读入类型
switch (type) {
case messAge://普通消息分支
data>>arrtxt;
str=QString::fromLocal8Bit(arrtxt);
ui->textBrowser->append(str);
break;
case fiLe://文件分支
qDebug()<<"准备接收文件"<<endl;
if(recvsize<filesize)//当接收到的内容不完整时继续读取
{
qDebug()<<"开始接收文件内容"<<endl;
data>>arrfile;//读入文件内容
file.write(arrfile);
recvsize+=arrfile.size();
qDebug()<<"接受的大小:"<<recvsize<<endl;
ui->progressBar->setValue(recvsize);
}
if(filesize==0)//如果文件大小为0,说明才开始传输,首先客户端要传输文件名字,和文件大小
{
qDebug()<<"接收文件信息"<<endl;
data>>filename>>filesize;//读入文件名和文件大小
qDebug()<<"文件信息:"<<recvsize<<" "<<filesize<<endl;
ui->progressBar->setMaximum(filesize);//将进度条的最大值设置为客户端传入文件大小
//打开文件
file.setFileName(filename);
file.open(QIODevice::WriteOnly);
}
if(recvsize==filesize)
{
qDebug()<<"关闭接收文件内容"<<endl;
file.close();
filesize=0;
recvsize=0;
}
break;
default:
break;
}
}
void Widget::newConnectionSlot()
{
qDebug()<<"新用户来了"<<endl;
QTcpSocket* sock=my_sever->nextPendingConnection();
my_client.append(sock);
connect(sock,&QTcpSocket::readyRead,this,&Widget::readyReadSlot);
filesize=0;
recvsize=0;
}
版权归原作者 为阿根廷助威 所有, 如有侵权,请联系我们删除。