jsp开发网站,网站切换,网站名词,代理平台微信号“ 所有生而孤独的人#xff0c;葆有的天真 ” 为了⽀持跨平台, QT对⽹络编程的 API 也进⾏了重新封装。本章会上手一套基于QT的网络通信编写。
UDP Socket 在使用Qt进行网络编程前#xff0c;需要在Qt项目中的.pro文件里添加对应的网络模块( network ).
QT core gui net…
“ 所有生而孤独的人葆有的天真 ” 为了⽀持跨平台, QT对⽹络编程的 API 也进⾏了重新封装。本章会上手一套基于QT的网络通信编写。
UDP Socket 在使用Qt进行网络编程前需要在Qt项目中的.pro文件里添加对应的网络模块( network ).
QT core gui network
QUdpsocket 核心API 名称 类型 说明 原⽣ API bind(const QHostAddress, quint16) 方法 绑定指定的端⼝号 bind receiveDatagram() ⽅法 返回 QNetworkDatagram . 读取 ⼀个 UDP 数据报. recvfrom writeDatagram(const QNetworkDatagram) ⽅法 发送⼀个 UDP 数据报. sendto readyRead 信号 在收到数据并准备就绪后触发 QNetworkDatagram(const QByteArray, const QHostAddress , quint16 ) 构造函数 data() ⽅法 获取数据报内部持有的数据. 返回QByteArray senderAddress() ⽅法 获取对端的 IP 地址. senderPort() ⽅法 获取对端的端⼝号
基于udp的简单回显程序
· 服务端 创建界面包含一个 QListWidget 用于显示消息 在主类中创建QUdpSocket成员 进行初始化 // 1. 设置窗口标题this-setWindowTitle(服务器);// 2. 实例化socket new QUdpSocket(this);// 3. 连接信号槽, 处理收到的请求connect(socket,QUdpSocket::readyRead,this,MainWindow::processRequest);// 4. 端口bind(ip,port)bool ret socket-bind(QHostAddress::Any,9090);if(ret false){QMessageBox::critical(nullptr,服务器启动错误,socket-errorString());return;} 一般来说都是先建立信号与槽的连接再进行网络端口的绑定。如果顺序反过来当网络端口进行bind后客户端就可以发送来消息处理此时如果没来得及连接信号槽为这个请求提供的服务就可能失效。 槽函数实现(实现对端消息的回显功能)
QString MainWindow::process(const QString req)
{return req;
}void MainWindow::processRequest()
{// 当走到这里 说明服务器已经收到对端信息递达的信号 触发的槽函数处理~// 1.获取请求const QNetworkDatagram requestDatagram socket-receiveDatagram();// QNetworkDatagram.data() 拿到对端请求的原始数据QString request requestDatagram.data();// 2. 计算处理请求const QString response process(request);// 3.把响应写回到客⼾端QNetworkDatagram responseDatagram(response.toUtf8(),\requestDatagram.senderAddress(),requestDatagram.senderPort());socket-writeDatagram(responseDatagram);// 4.显示打印日志QString log [ requestDatagram.senderAddress().toString() : QString::number(requestDatagram.senderPort()) ] req: request | resp: response;ui-listWidget-addItem(log);
}
· 客户端 创建一个界面包含用户发送消息窗口、发送按钮、输入栏。发送窗口仍然使用QListWidget依次是pushButton、QLineEdit。 先使⽤⽔平布局( layout) 把两个控件齐整。 进入到控件中的sizePolicy 设置为 “Expanding ”。 接着再使用垂直布局将消息发送窗口与水平布局的两个空间再进行空间管理。当然我们需要在垂直布局中设置比例否则比较难看~ 这样简易的界面也算完成了。 初始化IP和端口 在mainwindows.h中定义两个静态变量
static const QString server_ip 127.0.0.1;
static const quint16 server_port 9090; 消息发送实现槽函数 完成QUdpsocket实例化后我们只需要关注数据报的发送即可。 void MainWindow::on_send_putton_clicked()
{// 1. 获取输入的内容const QString text ui-message_edit-text();// 2. 利用text构造数据报QNetworkDatagram requestDatagram \QNetworkDatagram(text.toUtf8(),QHostAddress(server_ip),server_port);// 3.发送请求socket-writeDatagram(requestDatagram);// 4.前端回显ui-message_screen-addItem(客户端说: text);// 5.每发完一条消息 清空输入框ui-message_edit-clear();
}接收客户端回显槽函数 // 接收服务端回显connect(socket,QUdpSocket::readyRead,this,[](){const QNetworkDatagram responseDatagram socket-receiveDatagram();QString resp responseDatagram.data();ui-message_screen-addItem(QString(服务器回显: resp));}); 测试: 双方都能看到对端发送的消息并能及时回显。 TCP Socket TCP相对于UDP而言要复杂很多只要你曾学过网络知识。我们首先来了解了解Tcp Socket中的核心API。
QTcpServer 核心API 名称 类型 说明 对标原⽣ API listen(const QHostAddress, quint16 port) 方法 绑定指定的地址和端⼝号, 并开始监听 bind 和 listen nextPendingConnection() 方法 从系统中获取到⼀个已经建⽴好的 tcp 连接. 返回⼀个 QTcpSocket , 表⽰这个 客⼾端的连接. accept newConnection() 信号 有新的客⼾端建⽴连接好之后触发 readAll() 方法 读取当前接收缓冲区中的所有数据. 返回 QByteArray 对象 read write(const QByteArray ) 方法 把数据写⼊ socket 中 write readyRead 信号有数据到达并准备就绪时触发 deleteLater() 方法 暂时把 socket 对象标记为⽆效. disconnected() 信号 连接断开时触发 想对比于udp的核心API我们会发现没有了于 Datagram 相关的任何接口了。这本质是因为TCP是面向字节流而非udp那样的数据报。
基于TCP的简单回显程序 因为都是做回显那么这里服务器、客户端的前端依旧同udp是一的 ~
· 服务端 编写QTcpServer
#include QMainWindow
#include QString
#include QTcpServer
#include QHostAddress
#include QMessageBox
#include QTcpSocket// 设置窗口信息this-setWindowTitle( 服务器 );// 1.实例化tcpserver new QTcpServer(this);// 2.通过信号槽, 处理客⼾端建⽴的新连接.connect(tcpserver,QTcpServer::newConnection,this,MainWindow::processConnection);// 3.监听bindbool ret tcpserver-listen(QHostAddress::Any,9090);if(ret false){QMessageBox::critical(nullptr,服务器启动失败,tcpserver-errorString());exit(-1);} 继续修改 widget.cpp, 实现处理连接的具体⽅法processConnection
void MainWindow::processConnection()
{// 1.根据 listen 获取接收到的新连接// 注: 这里是 QTcpSocketQTcpSocket* clientsocket tcpserver-nextPendingConnection();// 更新服务端日志QString log QString([) clientsocket-peerAddress().toString() : \QString::number(clientsocket-peerPort()) ] 客户端上线;ui-listWidget-addItem(log);
} 完成回显工作槽函数实现 可以发现不管是做udp还是tcp的网络模型服务我们都舍弃了用循环的方式处理请求。这会导致我们一旦存在占用资源的连接不及时释放cpu资源那么别的请求就不会被读取到直到该请求的任务执行完成。 Qt中的槽机制恰好避免了这样的困境一旦发出信号就执行槽函数即可~ // 信号槽处理 处理收到请求的情况connect(clientsocket,QTcpSocket::readyRead,this,[](){// 字节流 把所有字节都 读上来const QString req clientsocket-readAll();// 根据请求 制作响应const QString resp process(req);// 写回客户端clientsocket-write(resp.toUtf8());// 服务端回显QString log [ clientsocket-peerAddress().toString() : QString::number(clientsocket-peerPort()) \ ] req: req;ui-listWidget-addItem(log);}); 由于Tcp可靠性的特征每一次客户端的连接都会被服务端保存着。当客户端断开连接时而服务端并不是释放两者之间用于连接的资源时就会导致 资源泄漏~~ // 通过信号槽, 处理断开连接的情况connect(clientsocket,QTcpSocket::disconnected,this,[](){QString log QString([) clientsocket-peerAddress().toString() : \QString::number(clientsocket-peerPort()) ] 客户端下线;ui-listWidget-addItem(log);// 释放资源clientsocket-deleteLater(); // 并不会立即释放}); · 客户端 初始化mainwindow.cpp this-setWindowTitle(客户端);// 1. 实例化socketsocket new QTcpSocket(this);// 2. 建立连接socket-connectToHost(127.0.0.1,9090);// 3.等待并确认连接是否出错bool ret socket-waitForConnected();if(ret false){QMessageBox::critical(nullptr,连接失败,socket-errorString());exit(-1);} 给按钮增加点击的 slot 函数, 实现发送请求给服务器
void MainWindow::on_send_clicked()
{// 获得输入的内容 并输出在界面上const QString text ui-edit-text();ui-edit-clear();ui-messageScreen-addItem(已发送: text);// 真正的发送消息socket-write(text.toUtf8());
} 通过信号槽, 处理收到的服务器的响应 connect(socket,QTcpSocket::readyRead,this,[](){QString resp socket-readAll();qDebug() resp;ui-messageScreen-addItem(服务端回显: resp);}); 测试 不管是响应还是当客户端断开连接时我们都能够完成对应的功能。 HTTP 进⾏ Qt 开发时, 和服务器之间的通信很多时候也会⽤到 HTTP 协议。我们大概需要以下几个步骤 • 通过 HTTP 从服务器获取数据. • 通过 HTTP 向服务器提交数据. 核心API 关键类有三个 QNetworkAccessManager、QNetworkRequest、QNetworkReply . QNetworkAccessManager 提供了 HTTP 的核⼼操作. ⽅法 说明 get(const QNetworkRequest ) 发起⼀个 HTTP GET 请求. 返回 QNetworkReply 对象. post(const QNetworkRequest , const QByteArray ) 发起⼀个 HTTP POST 请求. 返回 QNetworkReply 对 象. QNetworkRequest 表⽰⼀个 HTTP 请求. 如果需要发送⼀个带有 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 设置请求头. QNetworkReply 表⽰⼀个 HTTP 响应 ⽅法 说明 error() 获取出错状态. errorString() 获取出错原因的⽂本. readAll() 读取响应 body header(QNetworkRequest::KnownHeaders header) 设置 cookie 构建一个Http客户端 因为我们只需要构建一个模拟的HTTP请求。服务端则不需要我们进行什么编写~~ QPlainTextEdit vs QTextEdit QTextEdit会进⾏富 ⽂本解析, 如果得到的 HTTP 响应体积很⼤, 就会导致界⾯渲染缓慢甚⾄被卡住。 修改 mainwindow.h, 创建 QNetworkAccessManager 属性 #include QMainWindow
#include QNetworkAccessManager
#include QNetworkRequest
#include QNetworkReplyQNetworkAccessManager* manager; 创建实例并初始化 this-setWindowTitle( Http请求发起器 );// 1. 实例初始化manager new QNetworkAccessManager(this); 编写按钮的 slot 函数, 实现发送 HTTP 请求功能. void MainWindow::on_pushButton_clicked()
{// 1. 根据输入框里url构造QurlQUrl url(ui-lineEdit-text());// 2. 构造http响应QNetworkRequest req(url);// 3. 以什么方法访问QNetworkReply* resp manager-get(req);// 通过信号槽来处理响应connect(resp,QNetworkReply::finished,this,[](){if(resp-error() QNetworkReply::NoError){QString html(resp-readAll());ui-plainTextEdit-setPlainText(html);}else{ui-plainTextEdit-setPlainText(resp-errorString());}resp-deleteLater();});
}测试 本篇到此结束感谢你的阅读。
祝你好运向阳而生~