TCP即TransmissionControl Protocol,传输控制协议。与UDP不同,它是面向连接和数据流的可靠传输协议。也就是说,它能使一台计算机上的数据无差错的发往网络上的其他计算机,所以当要传输大量数据时,我们选用TCP协议。

TCP协议的程序使用的是客户端/服务器(C/S)模式,在Qt中提供了QTcpSocket类来编写客户端程序,使用QTcpServer类编写服务器端程序。我们在服务器端进行端口的,一旦发现客户端的连接请求,就会发出newConnection()信号,可以关联这个信号到我们自己的槽进行数据的发送。而在客户端,一旦有数据到来就会发出readyRead()信号,可以关联此信号进行数据的接收。其实,在程序中最难理解的地方就是程序的发送和接收了,为了让大家更好的理解,

下面我们已发送文件为案例

pro 文件

qt怎么发送keyPressEvent信号_数据


客户端 ui界面

qt怎么发送keyPressEvent信号_数据_02


.h 文件

#include <QMainWindow>
#include <QtNetwork>
#include <QFileDialog>
#include <QFileInfo>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void on_pB_open_clicked();   //打开文件
    void on_pB_Send_clicked();  //发送文件

    void send();  //连接服务器
    void startTransfer();  //发送文件大小等信息
    void updateClientProgress(qint64); //发送数据,更新进度条
    void displayError(QAbstractSocket::SocketError); //显示错误
    void openFile();   //打开文件

private:
    Ui::MainWindow *ui;
    QTcpSocket *tcpClient;
    QFile *localFile;  //要发送的文件
    qint64 totalBytes;  //数据总大小
    qint64 bytesWritten;  //已经发送数据大小
    qint64 bytesToWrite;   //剩余数据大小
    qint64 loadSize;   //每次发送数据的大小
    QString fileName;  //保存文件路径
    QByteArray outBlock;  //数据缓冲区,即存放每次要发送的数据
};

.cpp 文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    loadSize = 4*1024;
    totalBytes = 0;
    bytesWritten = 0;
    bytesToWrite = 0;
    tcpClient = new QTcpSocket(this);  //创建TCP客户端
    //当连接服务器成功时,发出connected()信号,我们开始传送文件
    connect(tcpClient,SIGNAL(connected()),this,SLOT(startTransfer()));
    //当有数据发送成功时,我们更新进度条
    connect(tcpClient,SIGNAL(bytesWritten(qint64)),this,
            SLOT(updateClientProgress(qint64)));
    connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this,
            SLOT(displayError(QAbstractSocket::SocketError)));
    //开始使”发送“按钮不可用
    ui->pB_Send->setEnabled(false);
}

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

void MainWindow::on_pB_open_clicked()
{
    openFile();
}

void MainWindow::on_pB_Send_clicked()
{
    send();
}

void MainWindow::send()
{
    ui->pB_Send->setEnabled(false);
    bytesWritten=0;
    ui->label_ClientStatus->setText(tr("连接中..."));
    //连接主机
    tcpClient->connectToHost(ui->lineEdit_host->text(),
                             ui->lineEdit_prot->text().toInt());
}

void MainWindow::startTransfer()
{
    localFile = new QFile(fileName);
    if(!localFile->open(QFile::ReadOnly))
    {
        qDebug() << "open file error!";
        return;
    }

    //文件总大小
    totalBytes = localFile->size();

    QDataStream sendOut(&outBlock,QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_5_8);
    //当前文件名称
    QString currentFileName = fileName.right(fileName.size()
                                             - fileName.lastIndexOf('/')-1);

    //依次写入总大小信息空间,文件名大小信息空间,文件名
    sendOut << qint64(0) << qint64(0) << currentFileName;

    //这里的总大小是文件名大小等信息和实际文件大小的总和
    totalBytes += outBlock.size();

    sendOut.device()->seek(0);
    //返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间
    sendOut<<totalBytes<<qint64((outBlock.size() - sizeof(qint64)*2));

    //发送完头数据后剩余数据的大小
    bytesToWrite = totalBytes - tcpClient->write(outBlock);

    ui->label_ClientStatus->setText(tr("已连接"));
    outBlock.resize(0);
}

void MainWindow::updateClientProgress(qint64 numBytes)
{
    //已经发送数据的大小
    bytesWritten += (int)numBytes;
    if(bytesToWrite > 0) //如果已经发送了数据
    {
        //每次发送loadSize大小的数据,这里设置为4KB,如果剩余的数据不足4KB,
        //就发送剩余数据的大小
        outBlock = localFile->read(qMin(bytesToWrite,loadSize));

        //发送完一次数据后还剩余数据的大小
        bytesToWrite -= (int)tcpClient->write(outBlock);

        //清空发送缓冲区
        outBlock.resize(0);

    } else {
        localFile->close(); //如果没有发送任何数据,则关闭文件
    }

    //更新进度条
    ui->progressBar_client->setMaximum(totalBytes);
    ui->progressBar_client->setValue(bytesWritten);

    if(bytesWritten == totalBytes) //发送完毕
    {
        ui->label_ClientStatus->setText(tr("传送文件 %1 成功")
                                       .arg(fileName));
        localFile->close();
        tcpClient->close();
    }
}

void MainWindow::displayError(QAbstractSocket::SocketError)
{
    qDebug() << tcpClient->errorString();
    tcpClient->close();
    ui->progressBar_client->reset();
    ui->label_ClientStatus->setText(tr("客户端就绪"));
    ui->pB_Send->setEnabled(true);
}

//打开文件
void MainWindow::openFile()
{
    fileName = QFileDialog::getOpenFileName(this);
    if(!fileName.isEmpty())
    {
        ui->pB_Send->setEnabled(true);
        ui->label_ClientStatus->setText(tr("打开文件 %1 成功!")
                                        .arg(fileName));
    }
}

思路
我们设计好界面,然后按下“打开”按钮,选择要发送的文件,这时调用了openFile()函数。然后点击“发送”按钮,调用send()函数,与服务器进行连接。当连接成功时就会发出connected()信号,这时就会执行startTransfer()函数,进行文件头结构的发送,当发送成功时就会发出bytesWritten(qint64)信号,这时执行updateClientProgress(qint64 numBytes)进行文件数据的传输和进度条的更新。这里使用了一个loadSize变量,我们在构造函数中将其初始化为4*1024即4字节,它的作用是,我们将整个大的文件分成很多小的部分进行发送,每部分为4字节。而当连接出现问题时就会发出error(QAbstractSocket::SocketError)信号,这时就会执行displayError()函数。

服务端

ui 界面

qt怎么发送keyPressEvent信号_数据_03

.h 文件

#include <QMainWindow>
#include <QtNetwork>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void on_pB_Start_clicked();
    void start();   //开始监听
    void acceptConnection();  //建立连接
    void updateServerProgress();  //更新进度条,接收数据
    void displayError(QAbstractSocket::SocketError socketError);//显示错误

private:
    Ui::MainWindow *ui;
    QTcpServer *tcpServer;
    QTcpSocket *tcpServerConnection;
    qint64 totalBytes;  //存放总大小信息
    qint64 bytesReceived;  //已收到数据的大小
    qint64 fileNameSize;  //文件名的大小信息
    QString fileName;   //存放文件名
    QFile *localFile;   //本地文件
    QByteArray inBlock;   //数据缓冲区
};

.cpp 文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    totalBytes=0;    //总大小
    bytesReceived=0;  //已经接受的数据包大小
    fileNameSize=0;   //文件名大小
    //当发现新连接时发出newConnection()信号
    tcpServer=new QTcpServer(this);
    connect(tcpServer,SIGNAL(newConnection()),this,
            SLOT(acceptConnection()));
}

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

//开始监听
void MainWindow::on_pB_Start_clicked()
{
    start();
}

void MainWindow::start()
{
    ui->pB_Start->setEnabled(false);
    bytesReceived =0;
    if(!tcpServer->listen(QHostAddress::LocalHost,6666))
    {
        qDebug() << tcpServer->errorString();
        close();
        return;
    }
    ui->label_sercerStatus->setText(tr("监听"));
}

//接受链接
void MainWindow::acceptConnection()
{
    tcpServerConnection = tcpServer->nextPendingConnection();
    connect(tcpServerConnection,SIGNAL(readyRead()),this,
            SLOT(updateServerProgress()));
    connect(tcpServerConnection,
            SIGNAL(error(QAbstractSocket::SocketError)),this,
            SLOT(displayError(QAbstractSocket::SocketError)));
    ui->label_sercerStatus->setText(tr("接受连接"));
    tcpServer->close();
}

void MainWindow::updateServerProgress()
{
    QDataStream in(tcpServerConnection);
    in.setVersion(QDataStream::Qt_5_8);
    if(bytesReceived <= sizeof(qint64)*2)
    { //如果接收到的数据小于16个字节,那么是刚开始接收数据,我们保存到//来的头文件信息
        if((tcpServerConnection->bytesAvailable() >= sizeof(qint64)*2)
                && (fileNameSize == 0))
        { //接收数据总大小信息和文件名大小信息
            in >> totalBytes >> fileNameSize;
            bytesReceived += sizeof(qint64) * 2;
        }
        if((tcpServerConnection->bytesAvailable() >= fileNameSize)
                && (fileNameSize != 0))
        {  //接收文件名,并建立文件
            in >> fileName;
            ui->label_sercerStatus->setText(tr("接收文件 %1 ...")
                                           .arg(fileName));
            bytesReceived += fileNameSize;
            localFile= new QFile(fileName);
            if(!localFile->open(QFile::WriteOnly))
            {
                qDebug() << "open file error!";
                return;
            }
        }
        else return;
    }
    if(bytesReceived < totalBytes)
    {  //如果接收的数据小于总数据,那么写入文件
        bytesReceived += tcpServerConnection->bytesAvailable();
        inBlock= tcpServerConnection->readAll();
        localFile->write(inBlock);
        inBlock.resize(0);
    }
    //更新进度条
    ui->progressBar_server->setMaximum(totalBytes);
    ui->progressBar_server->setValue(bytesReceived);

    if(bytesReceived == totalBytes)
    { //接收数据完成时
        tcpServerConnection->close();
        localFile->close();
        ui->pB_Start->setEnabled(true);
        ui->label_sercerStatus->setText(tr("接收文件 %1 成功!")
                                       .arg(fileName));
    }
}

void MainWindow::displayError(QAbstractSocket::SocketError socketError)
{
    qDebug() << tcpServerConnection->errorString();
    tcpServerConnection->close();
    ui->progressBar_server->reset();
    ui->label_sercerStatus->setText(tr("服务端就绪"));
    ui->pB_Start->setEnabled(true);
}

思路

点击监听按钮,如果有新的连接newConnect() 信号 那就执行acceptConnection()

qt怎么发送keyPressEvent信号_文件名_04


如果有中文乱码

那就在mian.cpp中设置n

qt怎么发送keyPressEvent信号_#include_05