Qt:网络编程

来源:互联网 发布:js上下移动div 编辑:程序博客网 时间:2024/06/05 20:27

一、UDP应用(QUdpSocket)
服务器:
1、初始化套接字
2、绑定
3、接收/发送
4、关闭套接字
客户端:
1、初始化套接字
2、发送/接收
3、关闭套接字
Qt提供了QUdpSocket类用于编写UDP程序,QUdpSocket类提供的另一个重要功能是广播
eg:
客户端://客户端仅广播,服务器端仅接收
class Sender : public QObject{
    Q_OBJECT
public:
    Sender(QObject *parent = 0);
    ~Sender();
    void start();
private slots:
    void broadcastDatagram();
private:
    QUdpSocket *udpSocket;
    QTimer *timer;
    int messageNo;
};
Sender::Sender(QObject *parent) : QObject(parent){
    timer = new QTimer(this);
    udpSocket = new QUdpSocket(this);   //创建套接字
    connect(timer,SIGNAL(timeout()),this,SLOT(broadcastDatagram()));
    messageNo = 1;
}
void Sender::start(){
    timer->start(1000);
}
void Sender::broadcastDatagram(){
    qDebug() << (tr("开始广播:%1").arg(messageNo));
    QByteArray datagram = "BroadCast Message: " + QByteArray::number(messageNo);
    udpSocket->writeDatagram(datagram.data(),datagram.size(),
                             QHostAddress::Broadcast,44444);//发送
    ++messageNo;
}
//QHostAddress::Broadcast对应IPv4下的广播地址.QHostAddress::LocalHost(端到端)
int main(int argc,char *argv[]){
    QCoreApplication app(argc,argv);
    Sender sender;
    sender.start();
    return app.exec();
}
//////////////////////////////////////////////////////////////////////////////////
服务器端:
class Receiver : public QObject{
    Q_OBJECT
public:
    Receiver(QObject *parent = 0);
    ~Recvier();
private slots:
    void processPendingDatagrams();
private:
    QUdpSocket *udpSocket;
};
Receiver::Receiver(QObject *parent) : QObject(parent){
    udpSocket = new QUdpSocket(this); //创建套接字
    udpSocket->bind(44444); //绑定
    connect(udpSocket,SIGNAL(readyRead()),
            this,SLOG(processPendingDatagrams()));
}


void Receiver::processPendingDatagrams(){
    while(udpSocket->hasPendingDatagrams){
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(datagram.data(),datagram.size()); //接收
        qDebug() << (tr("接收数据:\"%1\"")
                     .arg(datagram.data()));
    }
}
//main函数与客户端类似
实际应用中,客户端和服务器双方均需要进行数据的收发,即全双工工作方式,这时需分别使用QUdpSocket::bind()函数绑定
各自的主机和端口来读取数据,同时还可以在同一个套接字上使用QUdpSocket::writeDatagram()函数向不同的目的地和端口发送数据。
/*********************************************************************************************************************/
二、TCP应用(QTcpSocket,QTcpServer):
服务器端:
1、初始化套接字
2、绑定
3、监听
4、接受连接
5、接收/发送
6、关闭套接字
客户端:
1、初始化套接字
2、连接
3、发送/接收
4、关闭套接字
QTcpSocket类通过其父类QAbstractSocket继承了QIODevice类,因此可以使用QTextStream和QDataStream这样的流结构类。
QTcpServer类在服务器端处理外来的TCP客户端连接,该类直接继承于QObject基类。
eg:
客户端:
class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
public slots:
    void start();
    void startTransfor();
    void updateClientProgress(qint64 numBytes);
    void displayError(QAbstractSocket::SocketError socketError);
    void openFile();
private:
    QProgressBar *clientProgressBar;
    QLabel *clientStatusLabel;
    QPushButton *startButton;
    QPushButton *quitButton;
    QPushButton *openButton;
    QDialogButtonBox *buttonBox;
    QTcpSocket tcpClient;
    
    qint64 TotalBytes;
    qint64 bytesWritten;
    qint64 bytesToWrite;
    qint64 loadSize;
    QString fileName;
    QFile *localFile;
    QByteArray outBlock;
};
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    loadSize = 4 * 1024;
    TotalBytes = 0;
    bytesWritten = 0;
    bytesToWrite = 0;
    clientProgressBar = new QProgressBar;
    clientStatusLabel = new QLabel(tr("客户端就绪"));
    startButton = new QPushButton(tr("开始"));
    quitButton = new QPushButton(tr("退出"));
    openButton = new QPushButton(tr("打开"));
    startButton->setEnabled(false);
    connect(startButton,SIGNAL(clicked()),this,SLOT(start()));
    connect(quitButton,SIGNAL(clicked()),this,SLOT(close()));
    connect(openButton,SIGNAL(clicked()),this,SLOT(openFile()));
    connect(&tcpClient,SIGNAL(connected()),this,SLOT(startTransfor()));
    connect(&tcpClient,SIGNAL(bytesWritten(qint64)),
            this,SLOT(updateClientProgress(qint64)));
    connect(&tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),
            this,SLOT(displayError(QAbstractSocket::SocketError)));
    //成功与服务器建立连接:connected()信号,数据发送成功:bytesWriten()信号,产生错误:error()信号
}
void Dialog::openFile(){
    fileName = QFileDialog::getOpenFileName(this); //获取要传递的文件名
    if(!fileName.isEmpty()){
        startButton->setEnabled(true);
    }
}
void Dialog::start(){
    startButton->setEnabled(false);
    QApplication::setOverrideCursor(Qt::WaitCursor);
    bytesWritten = 0;
    clientStatusLabel->setText(tr("连接中..."));
    tcpClient.connectToHost(QHostAddress::LocalHost,16689); //连接服务器
}
void Dialog::startTransfor(){
    localFile = new QFile(filName);
    if(!localFile->open(QFile::ReadOnly)){
        QMessageBox::warning(this,tr("应用程序"),tr("无法读取文件 %1:\n%2")
                             .arg(fileName).arg(localFile->errorString()));
        return ;
    }
    TotalBytes = localFile->size(); //文件大小
    QDataStream sendOut(&outBlock,QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_5_3);
    QString currentFile = fileName.right(fileName.size()
                                         - fileName.lastIndexOf('/') - 1);
    sendOut << qint64(0) << qint64(0) << currentFile; //先占个地儿
    TotalBytes += outBlock.size(); //发送数据的总长度
    sendOut.device()->seek(0);
    sendOut << TotalBytes << qint64(outBlock.size() - sizeof(qint64)*2);
    bytesToWrite = TotalBytes - tcpClient.write(outBlock); //发送数据
    clientStatusLabel->setText(tr("已连接"));
    qDebug() << currentFile << TotalBytes;
    outBlock.resize(0);
}//发送文件数据格式:总长度(64位),文件名长度(64位),文件名,文件数据
void Dialog::updateClientProgress(qint64 numBytes){ //槽
    bytesWritten += (int)numBytes; //已发送的字节数
    if(bytesToWrite > 0){
        outBlock = localFile->read(qMin(bytesToWrite,loadSize));//尽可能发送4k
        bytesToWrite -= (int)tcpClient.write(outBlock);
        outBlock.resize(0);
    }else{
        localFile->close();
    }
    clientProgressBar->setMaximum(TotalBytes);
    clientProgressBar->setValue(bytesWritten);
    clientStatusLabel->setText(tr("已发送 %1MB").arg(bytesWritten)/(1024*1024);
}
void Dialog::displayError(QAbstractSocket::SocketError socketError){ //槽
    if(socketError == QTcpSocket::RemoteHostClosedError)
        return ;
    QMessageBox::information(this,tr("网络"),tr("产生错误:%1.")
                             .arg(tcpClient.errorString()));
    tcpClient.close(); //出错,关闭套接字
    clientProgressBar->reset();
    clientStatusLabel->setText(tr("客户端就绪"));
    startButton->setEnabled(true);
    QApplication::restoreOverrideCursor();
}
//////////////////////////////////////////////////////////////////////////////////////////
服务器端:
#ifndef DIALOGSRV_H
#define DIALOGSRV_H
#include <QDialog>
class DialogSrv : public QDialog
{
    Q_OBJECT
public:
    explicit DialogSrv(QWidget *parent = 0);


signals:


public slots:
    void start();
    void acceptConnection();
    void updateServerProgress();
    void displayError(QAbstractSocket::SocketError);
private:
    QProgressBar* clientProgressBar;
    QProgressBar* serverProgressBar;
    QLabel* serverStatusLabel;
    QPushButton* startButton;
    QPushButton* quitButton;
    QPushButton* openButton;
    QDialogButtonBox* buttonBox;
    QTcpServer tcpServer;
    QTcpSocket* tcpServerConnection;
    qint64 TotalBytes;
    qint64 bytesRecvived;
    qint64 fileNameSize;
    QString fileName;
    QFile* localFile;
    QByteArray inBlock;


};
#endif // DIALOGSRV_H


#include "dialogsrv.h"
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QProgressBar>
#include <QLabel>
#include <QPushButton>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFile>
#include <QByteArray>
#include <QString>
#include <QMessageBox>
DialogSrv::DialogSrv(QWidget *parent) :
    QDialog(parent)
{
    TotalBytes = 0;
    bytesRecvived = 0;
    fileNameSize = 0;
    serverProgressBar = new QProgressBar;
    serverStatusLabel = new QLabel(tr("服务端就绪"));
    startButton = new QPushButton(tr("接收"));
    quitButton = new QPushButton(tr("退出"));
    connect(startButton,SIGNAL(clicked()),this,SLOT(start()));
    connect(quitButton,SIGNAL(clicked()),this,SLOT(close()));
    connect(&tcpServer,SIGNAL(newConnection()),this,SLOT(acceptConnection()));
    //有可用的Tcp连接时,QTcpServer发出newConnection()信号
}
DialogSrv::start(){
    startButton->setEnabled(false);
    QApplication::setOverrideCursor(Qt::WaitCursor);
    bytesRecvived = 0;
    while(!tcpServer.isListening() &&
          !tcpServer.listen(QHostAddress::LocalHost,16689)){ //监听
        QMessageBox::StandardButton ret =
                QMessageBox::critical(this,
                                      tr("回环"),
                                      tr("无法开始测试:%1")
                                      .arg(tcpServer.errorString()),
                                      QMessageBox::Retry |
                                      QMessageBox::Cancel);


        if(ret = QMessageBox::Cancel)
            return ;
    }
    serverStatusLabel->setText(tr("监听"));
}
void DialogSrv::acceptConnection(){
    tcpServerConnection = tcpServer.nextPendingConnection();
    connect(tcpServerConnection,SIGNAL(readyRead()),this,SLOT(updateServerProgress()));
    connect(tcpServerConnection,SIGNAL(error(QAbstractSocket::SocketError)),
            this,SLOT(displayError(QAbstractSocket::SocketError)));
    serverStatusLabel->setText(tr("接受连接"));
    tcpServer.close(); //只处理一个客户请求
}//当建立的连接有新的可供读取的数据时,QTcpSocket会发出readyRead()信号
void DialogSrv::updateServerProgress(){
    QDataStream in(tcpServerConnection);
    in.setVersion(QDataStream::Qt_5_3);
    if(bytesRecvived <= sizeof(qint64)*2){  //文件头结构的接收分两步完成
        if((tcpServerConnection->bytesAvailable() >= sizeof(qint64)*2)
                && (fileNameSize == 0)){
            in >> TotalBytes >> fileNameSize; //接收总字节数,文件名大小
            bytesRecvived += sizeof(qint64)*2;
        }
        if((tcpServerConnection->bytesAvailable() >= fileNameSize)
                && (fileNameSize != 0)){
            in >> fileName; //接收文件名
            bytesRecvived +=fileNameSize;
            localFile = new QFile(fileName);
            if(!localFile->open(QFile::WriteOnly)){
                QMessageBox::warning(this,tr("应用程序"),
                                     tr("无法读取文件%1:\n%2.")
                                     .arg(fileName)
                                     .arg(localFile->errorString()));
                return ;
            }
        }else{
            reutrn ;
        }
    }
    if(bytesRecvived < TotalBytes){  //读取实际的文件数据
        bytesRecvived += tcpServerConnection->bytesAvailable();
        inBlock = tcpServerConnection->readAll(); //无格式的流,读到缓冲区中
        localFile->write(inBlock);
        inBlock.resize(0);
    }
    serverProgressBar->setMaximum(TotalBytes);
    serverProgressBar->setValue(bytesRecvived);
    qDebug() << bytesRecvived;
    serverStatusLabel->setText(tr("已接收%1MB")
                               .arg(bytesRecvived/(1024*1024)));
    if(bytesRecvived == TotalBytes){
        tcpServerConnection->close(); //关闭连接
        startButton->setEnabled(true);
        QApplication::restoreOverrideCursor();
    }
}

/*********************************************************************************************************************/

UDP和TCP编程总体上都是由信号槽组成并控制的,在实际项目中可灵活修改信号:
    UDP客户端:时间到 -> 写数据报
    UDP服务端:绑定;socket准备读 -> 当有等待的数据报时读数据报
    TCP客户端:一点击开始 -> 连接主机(其中指定主机端口)
              一点击打开 -> 打开文件
              socket一连接上 -> 发送文件写数据
              数据一发送成功 -> 更新(set)进度条
    TCP服务端:一点击开始 -> 判断是否正在监听&&监听(其中指定主机端口)
              当有新的连接 -> {准备读下一个等待的连接 -> 读,更新进度条
                            {错误处理


0 0
原创粉丝点击