Qt学习笔记-带TCP数据传输的局域网聊天软件

程序运行截图如下:



然后选择一个文件,进行传输。


程序结构如下:


代码如下:

tcpreceiver.h

#ifndef TCPRECEIVER_H
#define TCPRECEIVER_H

#include <QWidget>
#include <QAbstractSocket>
#include <QTcpServer>
class QTcpSocket;
class QFile;


namespace Ui {
class TcpReceiver;
}

class TcpReceiver : public QWidget
{
    Q_OBJECT

public:
    explicit TcpReceiver(const int port,QWidget *parent = 0);
    ~TcpReceiver();

public slots:
    void start();
    void acceptConnection();
    void updateServerProgress();
    void displayError(QAbstractSocket::SocketError socketError);
    void startBtnClicked();

private:
    Ui::TcpReceiver *ui;

    QTcpServer m_tcpServer;
    QTcpSocket *m_tcpServerConnection;
    qint64 m_totalBytes;
    qint64 m_bytesReceived;
    qint64 m_fileNameSize;
    QString m_fileName;
    QFile *m_localFile;
    QByteArray m_inBlock;
    int m_port;
};

#endif // TCPRECEIVER_H


tcpsender.h

#ifndef TCPSENDER_H
#define TCPSENDER_H

#include <QWidget>
#include <QAbstractSocket>
class QTcpSocket;
class QFile;

namespace Ui {
class TcpSender;
}

class TcpSender : public QWidget
{
    Q_OBJECT

public:
    explicit TcpSender(const QString ip,const QString port,QWidget *parent = 0);
    ~TcpSender();

public slots:
    void openFile();
    void send();
    void startTransfer();
    void updateClientProgress(qint64);
    void displayError(QAbstractSocket::SocketError);
    void openBtnClicked();
    void sendBtnClicked();

private:
    Ui::TcpSender *ui;
    QTcpSocket *m_tcpClient;
    QFile *m_localFile;
    qint64 m_totalBytes;
    qint64 m_bytesWritten;
    qint64 m_bytesToWrite;
    qint64 m_payloadSize;
    QString m_fileName;
    QByteArray m_outBlock;
};

#endif // TCPSENDER_H


widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class QUdpSocket;
class TcpSender;
class TcpReceiver;

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

public slots:
    void dataReceived();
    void btnClicked();
    void widgetEnable(QString data);
    void setPortBtnClickd();
    void peerLineEditChange(QString data);
    void sendFileBtnClicked();

private:
    Ui::Widget *ui;
    QUdpSocket *m_udpSocket;
    TcpSender *m_tcpSender;
    TcpReceiver *m_tcpReceiver;
    QString m_peerIp;
};

#endif // WIDGET_H


main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}


tcpreceiver.cpp

#include "tcpreceiver.h"
#include "ui_tcpreceiver.h"

#include <QtNetwork>
#include <QDebug>


TcpReceiver::TcpReceiver(const int port, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TcpReceiver)
{
    ui->setupUi(this);
    m_port=port;
    connect(&m_tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection()));
    connect(ui->startButton,SIGNAL(clicked(bool)),this,SLOT(startBtnClicked()));
}

void TcpReceiver::start(){
    if(!m_tcpServer.listen(QHostAddress::LocalHost,m_port)){
        qDebug()<<m_tcpServer.errorString();
        close();
        return;
    }
    ui->startButton->setEnabled(false);
    m_totalBytes=0;
    m_bytesReceived=0;
    m_fileNameSize=0;
    ui->serverStatusLabel->setText("监听");
    ui->serverProgressBar->reset();
}

void TcpReceiver::acceptConnection(){
    m_tcpServerConnection=m_tcpServer.nextPendingConnection();
    connect(m_tcpServerConnection,SIGNAL(readyRead()),this,SLOT(updateServerProgress()));
    connect(m_tcpServerConnection,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));
    ui->serverStatusLabel->setText("接受连接");
    //关闭服务器不再监听,直接进入数据收发
    m_tcpServer.close();
}

void TcpReceiver::updateServerProgress(){
    QDataStream in(m_tcpServerConnection);
    in.setVersion(QDataStream::Qt_5_7);
    // 如果接收到的数据小于16个字节,保存到来的文件头结构
    if (m_bytesReceived<=sizeof(qint64)*2){
        if((m_tcpServerConnection->bytesAvailable()>=sizeof(qint64)*2)&&(m_fileNameSize==0)){
            // 接收数据总大小信息和文件名大小信息
            in>>m_totalBytes>>m_fileNameSize;
            m_bytesReceived +=sizeof(qint64)*2;
        }
        if((m_tcpServerConnection->bytesAvailable()>=m_fileNameSize)&&(m_fileNameSize!=0)){
            // 接收文件名,并建立文件
            in>>m_fileName;
            ui->serverStatusLabel->setText(tr("接收文件 %1 …").arg(m_fileName));
            m_bytesReceived+=m_fileNameSize;
            m_localFile = new QFile(m_fileName);
            if (!m_localFile->open(QFile::WriteOnly)){
                qDebug() << "server: open file error!";
                return;
            }
        }
        else{
            return;
        }
    }
    // 如果接收的数据小于总数据,那么写入文件
    if(m_bytesReceived<m_totalBytes) {
        m_bytesReceived+=m_tcpServerConnection->bytesAvailable();
        m_inBlock = m_tcpServerConnection->readAll();
        m_localFile->write(m_inBlock);
        m_inBlock.resize(0);
    }
    ui->serverProgressBar->setMaximum(m_totalBytes);
    ui->serverProgressBar->setValue(m_bytesReceived);

    // 接收数据完成时
    if (m_bytesReceived==m_totalBytes){
        m_tcpServerConnection->close();
        m_localFile->close();
        ui->startButton->setEnabled(true);
        ui->serverStatusLabel->setText(tr("接收文件 %1 成功!").arg(m_fileName));
    }
}

void TcpReceiver::displayError(QAbstractSocket::SocketError socketError){
    Q_UNUSED(socketError)
    qDebug()<<m_tcpServerConnection->errorString();
    m_tcpServerConnection->close();
    ui->serverProgressBar->reset();
    ui->serverStatusLabel->setText("服务端就绪");
    ui->startButton->setEnabled(true);
}

void TcpReceiver::startBtnClicked(){
    start();
}

TcpReceiver::~TcpReceiver()
{
    delete ui;
}


tcpsender.cpp

#include "tcpsender.h"
#include "ui_tcpsender.h"

#include <QtNetwork>
#include <QFileDialog>

TcpSender::TcpSender(const QString ip, const QString port, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TcpSender)
{
    ui->setupUi(this);
    ui->hostLineEdit->setText(ip);
    ui->portLineEdit->setText(port);
    ui->hostLineEdit->setEnabled(false);
    ui->portLineEdit->setEnabled(false);
    m_payloadSize=64*1024;
    m_totalBytes=0;
    m_bytesWritten=0;
    m_bytesToWrite=0;
    m_tcpClient=new QTcpSocket(this);
    connect(m_tcpClient,SIGNAL(connected()),this,SLOT(startTransfer()));
    connect(m_tcpClient,SIGNAL(bytesWritten(qint64)),this,SLOT(updateClientProgress(qint64)));
    connect(m_tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));
    connect(ui->sendButton,SIGNAL(clicked(bool)),this,SLOT(sendBtnClicked()));
    connect(ui->openButton,SIGNAL(clicked(bool)),this,SLOT(openBtnClicked()));
}

void TcpSender::openFile(){
    m_fileName=QFileDialog::getOpenFileName(this);
    if(!m_fileName.isEmpty()){
        ui->sendButton->setEnabled(true);
        ui->clientStatusLabel->setText(QString("打开文件 %1 成功!").arg(m_fileName));
    }
}

void TcpSender::send(){
    ui->sendButton->setEnabled(false);
    m_bytesWritten=0;
    ui->clientStatusLabel->setText("连接中...");
    m_tcpClient->connectToHost(ui->hostLineEdit->text(),ui->portLineEdit->text().toInt());
}

void TcpSender::startTransfer(){
    m_localFile=new QFile(m_fileName);
    if(!m_localFile->open(QFile::ReadOnly)){
        qDebug()<<"client:open file error!";
        return;
    }
    m_totalBytes=m_localFile->size();
    QDataStream sendOut(&m_outBlock,QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_5_7);
    QString currentFileName=m_fileName.right(m_fileName.size()-m_fileName.lastIndexOf('/')-1);
    //文件总大小、文件名大小、文件名
    sendOut<<qint64(0)<<qint64(0)<<currentFileName;
    m_totalBytes+=m_outBlock.size();
    sendOut.device()->seek(0);
    sendOut<<m_totalBytes<<qint64(m_outBlock.size()-sizeof(qint64)*2);
    m_bytesToWrite=m_totalBytes-m_tcpClient->write(m_outBlock);
    ui->clientStatusLabel->setText("已连接");
    m_outBlock.resize(0);
}

void TcpSender::updateClientProgress(qint64 numBytes){
    m_bytesWritten+=(int)numBytes;
    if(m_bytesToWrite>0){
        m_outBlock=m_localFile->read(qMin(m_bytesToWrite,m_payloadSize));
        m_bytesToWrite-=(int)m_tcpClient->write(m_outBlock);
        m_outBlock.resize(0);
    }
    else{
        m_localFile->close();
    }
    ui->clientProgressBar->setMaximum(m_totalBytes);
    ui->clientProgressBar->setValue(m_bytesWritten);

    if(m_bytesWritten==m_totalBytes){
        ui->clientStatusLabel->setText(QString("传送文件 %1 成功").arg(m_fileName));
        m_localFile->close();
        m_tcpClient->close();
    }
}

void TcpSender::displayError(QAbstractSocket::SocketError){
    qDebug()<<m_tcpClient->errorString();
    m_tcpClient->close();
    ui->clientProgressBar->reset();
    ui->clientStatusLabel->setText("客户端就绪");
    ui->sendButton->setEnabled(true);
}

void TcpSender::openBtnClicked(){
    ui->clientProgressBar->reset();
    ui->clientStatusLabel->setText("状态:等待打开文件!");
    openFile();
}

void TcpSender::sendBtnClicked(){
    send();
}

TcpSender::~TcpSender()
{
    delete ui;
}


widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QMessageBox>
#include <QHostAddress>
#include <QUdpSocket>
#include "tcpsender.h"
#include "tcpreceiver.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    ui->inputLineEdit->setEnabled(false);
    ui->sendPushButton->setEnabled(false);
    ui->peerLineEdit->setEnabled(false);
    ui->userLineEdit->setEnabled(false);
    ui->sendFilePushButton->setEnabled(false);
    connect(ui->userLineEdit,SIGNAL(textChanged(QString)),this,SLOT(widgetEnable(QString)));
    connect(ui->setPortPushButton,SIGNAL(clicked(bool)),this,SLOT(setPortBtnClickd()));
    connect(ui->peerLineEdit,SIGNAL(textEdited(QString)),this,SLOT(peerLineEditChange(QString)));
    connect(ui->sendFilePushButton,SIGNAL(clicked(bool)),this,SLOT(sendFileBtnClicked()));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::dataReceived(){
    while(m_udpSocket->hasPendingDatagrams()){
        QByteArray datagram;
        QHostAddress peerIp;
        datagram.resize(m_udpSocket->pendingDatagramSize());
        m_udpSocket->readDatagram(datagram.data(),datagram.size(),&peerIp);
        ui->listWidget->insertItem(0,QString::fromLocal8Bit(datagram.data()));
        m_peerIp=peerIp.toIPv4Address();
        QString temp=QString::fromLocal8Bit(datagram.data()).right(4);
        qDebug()<<"temp:"<<temp;
        if(temp=="发送文件"){
            m_tcpReceiver=new TcpReceiver(ui->portLineEdit->text().toInt());
            m_tcpReceiver->show();
        }
    }
}

void Widget::btnClicked()
{
    QString msg=ui->userLineEdit->text()+QString::fromLocal8Bit(" > ")+ui->inputLineEdit->text();
    QByteArray datagram=msg.toLocal8Bit();
    qDebug()<<"msg:"<<msg<<"msg.size"<<msg.size();
    m_udpSocket->writeDatagram(datagram,datagram.size(),QHostAddress::Broadcast,ui->peerLineEdit->text().toInt());
    ui->listWidget->insertItem(0,"我 > "+ui->inputLineEdit->text());
}

void Widget::widgetEnable(QString data)
{
    if(!data.isEmpty()){
        ui->inputLineEdit->setEnabled(true);
        ui->sendPushButton->setEnabled(true);
        ui->sendFilePushButton->setEnabled(true);
    }
}

void Widget::setPortBtnClickd()
{
    m_udpSocket=new QUdpSocket(this);
    connect(m_udpSocket,SIGNAL(readyRead()),this,SLOT(dataReceived()));
    if(!m_udpSocket->bind(ui->portLineEdit->text().toInt(),QUdpSocket::ShareAddress|QUdpSocket::ReuseAddressHint)){
        QMessageBox::information(this,"错误","端口绑定失败");
        return;
    }
    connect(ui->sendPushButton,SIGNAL(clicked(bool)),this,SLOT(btnClicked()));
    ui->peerLineEdit->setEnabled(true);
}

void Widget::peerLineEditChange(QString data)
{
    if(!data.isEmpty()){
        ui->userLineEdit->setEnabled(true);
    }
}

void Widget::sendFileBtnClicked()
{

    QString msg=ui->userLineEdit->text()+QString::fromLocal8Bit(" > ")+"发送文件";
    QByteArray datagram=msg.toLocal8Bit();
    qDebug()<<"msg:"<<msg<<"msg.size"<<msg.size();
    m_udpSocket->writeDatagram(datagram,datagram.size(),QHostAddress::Broadcast,ui->peerLineEdit->text().toInt());
    ui->listWidget->insertItem(0,"我 > 发送文件");

    m_tcpSender=new TcpSender("127.0.0.1",ui->peerLineEdit->text());   //使用本地回环,暂时写死
    m_tcpSender->show();
}



猜你喜欢

转载自blog.csdn.net/qq78442761/article/details/80639464