李优秀 发表于 2024-8-25 11:29:16

[Qt][Qt 网络][下]具体讲解

1.TCP Socket

1.核心API概览



[*]核⼼类是两个:QTcpServer和QTcpSocket
[*]QTcpServer用于监听端口,和获取客户端毗连

[*]listen(const QHostAddress&, quint16 port):绑定指定的所在和端⼝号,并开始监听

[*]成员方法
[*]对标原⽣API:bind() 和listen()

[*]nextPendingConnection():从系统中获取到⼀个已经建⽴好的tcp毗连

[*]返回⼀个QTcpSocket,表⽰这个客⼾端的毗连

[*]通过这个socket对象完成和客⼾端之间的通信

[*]成员方法
[*]对标原⽣API:accept()

[*]newConnection():有新的客⼾端建⽴毗连好之后触发

[*]信号
[*]类似于IO多路复用的关照机制


[*]QTcpSocket⽤于客⼾端和服务器之间的数据交互

[*]readAll():读取当前吸取缓冲区中的所有数据,返回QByteArray对象

[*]成员方法
[*]对标原生API:read()

[*]write(const QByteArray &):把数据写⼊socket中

[*]成员方法
[*]对标原生API:write()

[*]deleteLater():暂时把socket对象标记为⽆效,Qt会在下个事件循环中析构释放该对象

[*]成员方法
[*]类似于"半⾃动化的垃圾接纳"

[*]readyRead():有数据到达并预备就绪时触发

[*]信号
[*]类似于IO多路复⽤中的关照机制

[*]disconnected():毗连断开时触发

[*]信号
[*]类似于IO多路复⽤中的关照机制


2.回显服务器

// 构造函数中
{
        server = new QTcpServer(this);

        // 通过信号槽, 指定如何处理连接.
        connect(server, &QTcpServer::newConnection,
                        this, &Widget::ProcessConnection);
       
        // 绑定并监听端口号
        if (!ret = server->listen(QHostAddress::Any, 2333))
        {
          QMessageBox::critical(this, "服务器启动失败!", server->errorString());
          exit(1);
        }
}

void Widget::ProcessConnection()
{
    // 通过 server 拿到一个 socket 对象, 通过这个对象来和客户端进行通信
    QTcpSocket* clientSocket = tcpServer->nextPendingConnection();
   
    QString log = "[" + clientSocket->peerAddress().toString() + ":" +
                              QString::number(clientSocket->peerPort()) + "] 客户端上线!";
    ui->listWidget->addItem(log);

    // 通过信号槽, 来处理客户端发来请求的情况
    connect(clientSocket, &QTcpSocket::readyRead, this, [=]() {
      // a) 读取出请求数据,此处 readAll 返回的是 QByteArray, 通过赋值转成 QString
      QString request = clientSocket->readAll();
      
      // b) 根据请求处理响应
      const QString& response = Process(request);
      
      // c) 把响应写回到客户端
      clientSocket->write(response.toUtf8());
      
      // d) 把上述信息记录到日志中.
      QString log = "[" + clientSocket->peerAddress().toString() + ":" +
                                    QString::number(clientSocket->peerPort()) + "] "
                                + " req: " + request + ", resp: " + response;
      ui->listWidget->addItem(log);
    });

    // 通过信号槽, 来处理客户端断开连接的情况.
    connect(clientSocket, &QTcpSocket::disconnected, this, [=]() {
      // a) 把断开连接的信息通过日志显示出来.
      QString log = "[" + clientSocket->peerAddress().toString() + ":" +
                            QString::number(clientSocket->peerPort()) + "] 客户端下线!";
      ui->listWidget->addItem(log);
      
      // b) 手动释放 clientSocket. 直接使用 delete 是下策, 使用 deleteLater 更加合适
      clientSocket->deleteLater();
    });
}

QString Widget::Process(const QString request)
{
    return request;
}
3.回显客户端

// 构造函数中
{
        socket = new QTcpSocket(this);
       
        // 服务器建立连接,此处是非阻塞的IO通信
        socket->connectToHost("127.0.0.1", 2333);
       
        // 连接信号槽, 处理响应
        connect(socket, &QTcpSocket::readyRead, this, [=]() {
          // a) 读取出响应内容
          QString response = socket->readAll();
          
          // b) 把响应内容显示到界面上.
          ui->listWidget->addItem("服务器说: " + response);
        });
       
        // 等待连接建立的结果. 确认是否连接成功.
        if (!socket->waitForConnected())
        {
          QMessageBox::critical(this, "连接服务器出错", socket->errorString());
          exit(1);
        }
}

void Widget::on_pushButton_clicked()
{
    const QString& text = ui->lineEdit->text();
   
    socket->write(text.toUtf8());
   
    ui->listWidget->addItem("客户端说: " + text);
    ui->lineEdit->setText("");
}
2.HTTP Client



[*]Qt只提供了Http客户端,并没有提供Http服务端的库
[*]关键类主要是三个:QNetworkAccessManager , QNetworkRequest , QNetworkReply
[*]QNetworkAccessManager提供了HTTP的核⼼操纵

[*]get(const QNetworkRequest&):发起⼀个HTTP GET哀求,返回QNetworkReply对象

[*]本身不是壅闭函数,只负责发出去哀求,不负责等哀求回来
[*]收是QNetworkReply负责的,会收到finished信号

[*]post(const QNetworkRequest&, const QByteArray&):发起⼀个HTTP POST哀求,返回QNetworkReply对象

[*]QNetworkRequest表⽰⼀个HTTP哀求(不含body),如果必要发送⼀个带有body的哀求(⽐如post),会在QNetworkAccessManager的post()中通过单独的参数来传⼊body

[*]QNetworkRequest(const QUrl&):通过URL构造⼀个HTTP哀求
[*]setHeader(QNetworkRequest::KnownHeaders header, const QVariant& value):设置哀求头

[*]QNetworkRequest::KnownHeaders是⼀个罗列类型,常⽤取值

[*]ContentTypeHeader:描述body的类型
[*]ContentLengthHeader:描述body的⻓度
[*]LocationHeader:⽤于重定向报⽂中指定重定向所在
[*]CookieHeader:设置cookie
[*]UserAgentHeader:设置User-Agent



[*]QNetworkReply表⽰⼀个HTTP相应,这个类同时也是QIODevice的⼦类

[*]error():获取出错状态
[*]errorString():获取出错缘故原由的⽂本
[*]readAll():读取相应body
[*]header(QNetworkRequest::KnownHeaders header):读取相应指定header的值
[*]finished():为一个信号,在客⼾端收到完整的相应数据之后触发

[*]示例:// 构造函数中
{
    manager = new QNetworkAccessManager(this);
}

void Widget::on_pushButton_clicked()
{
    // 1. 获取到输入框中的 url
    QUrl url(ui->lineEdit->text());
   
    // 2. 构造一个 HTTP 请求对象
    QNetworkRequest request(url);
   
    // 3. 发送请求
    QNetworkReply* response = manager->get(request);
   
    // 4. 通过信号槽, 来处理响应
    connect(response, &QNetworkReply::finished, this, [=]() {
      if (response->error() == QNetworkReply::NoError)
      {
            QString html = response->readAll();
            ui->plainTextEdit->setPlainText(html);
      }
      else
      {
            ui->plainTextEdit->setPlainText(response->errorString());
      }
      
      // 需要对 response 进行手动释放.
      response->deleteLater();
    });
}

3.其他模块



[*]Qt中还提供了FTP,DNS,SSL等⽹络相关的组件⼯具,有必要可以具体学习,查阅官方文档

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: [Qt][Qt 网络][下]具体讲解