2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Lernende Vorumgebung
QT TCP Multithread-Netzwerkkommunikation – CSDN-Blog
WebSocket ist eine Netzwerktechnologie, die einen Vollduplex-Kommunikationskanal über eine einzige TCP-Verbindung bereitstellt. Im Jahr 2011 standardisierte die IETF das WebSocket-Protokoll als RFC6455, und QWebSocket kann sowohl in Clientanwendungen als auch in Serveranwendungen verwendet werden.
Es implementiert eine Vollduplex-Kommunikation zwischen dem Browser und dem Server, sodass der Server proaktiv Informationen an den Client senden kann.
Hauptmerkmal:
Im Gegensatz zu HTTP ermöglicht WebSocket dem Server, aktiv Daten an den Client zu senden, ohne dass der Client eine Anfrage initiieren muss.
Aufbauend auf dem TCP-Protokoll wird die Vollduplex-Kommunikation zwischen dem Server und dem Client über einen einzigen Port über das Ws-Protokoll (WebSocket) durchgeführt.
Unterstützt die Datenübertragung im Text- oder Binärformat.
Das Protokoll basiert auf einer einzigen TCP-Verbindung. Der Server und der Client müssen nur eine Verbindung herstellen, und die Verbindung wird nicht geschlossen.
Unterstützt clientseitige und serverseitige Bibliotheken in mehreren Programmiersprachen wie JavaScript, Java, C#, Python usw.
Häufige Anwendungsszenarien:
Chatroom: Unterstützt Echtzeitgespräche mit geringer Latenz.
Online-Spiel: eine Synchronisierungs-Engine, die eine Echtzeit-Synchronisierung des Spielstatus erfordert.
Aktienkurse: Angebotssoftware, die dem Kunden Kurse in Echtzeit übermitteln muss.
Videokonferenzen: Echtzeitkommunikation von Sprache und Video mit geringer Latenz ist erforderlich.
Kollaborative Bearbeitung in Echtzeit: Beispielsweise erfordert der Online-Code-Editor eine Synchronisierung in Echtzeit.
QWebSocket ist die von Qt bereitgestellte WebSocket-Funktionsbibliothek. Es basiert auf dem Qt-Netzwerkmodul und implementiert das WebSocket-Protokoll im RFC6455-Standard. Sie müssen QT+=websockets zur Qmake-Datei hinzufügen
QWebSocket unterstützt nur Text-/Binärnachrichtenformate und keine anderen erweiterten Formate. Wenn andere benutzerdefinierte Protokolle hinzugefügt werden müssen, müssen Entwickler dies selbst auf der Anwendungsebene erledigen.
Es unterstützt den Zugriff auf WebSocket-Dienste über die Standard-http/https-Ports 80/443 und unterstützt außerdem das WSS-Protokoll (Encrypted WebSocket). So kann es problemlos mit vorhandenen Webservern interagieren.
Da es auf der QT-CP-Socket-Implementierung basiert, unterstützt es vollständig alle Qt-Netzwerkfunktionen, wie Proxy-Einstellungen, SSL-Konfiguration usw. Dies ist einfacher zu verwenden als einige Low-Level-C-Schnittstellen.
Es unterstützt sowohl aktive als auch passive Verbindungsmodi. Aktive Verbindungen erfolgen über connectToHost() und passive Verbindungen werden über den Listening-Port Accept() akzeptiert. Beide Modi sind sehr praktisch.
Für QT-GUI-Anwendungen können der Nachrichtenempfang und Schnittstellenaktualisierungen einfach durchgeführt werden, wodurch die Komplexität der Multithread-Programmierung vermieden wird. Aktualisieren Sie beispielsweise einfach die Schnittstelle direkt in textMessageReceived().
Nach QT5.10 wird asynchrone E/A unterstützt und die Leistung ist im Vergleich zu zuvor leicht verbessert. Auch die Unterstützung für Netzwerkerweiterungen ist besser.
-
- origin()
- 即 websocket=new QWebSocket("C1我是客户端",QWebSocketProtocol::VersionLatest,this);
- websocket->origin() -》 C1我是客户端
-
- void connectToHost(const QUrl &url) - 用于连接到指定主机的websocket服务,这个函数是异步的。
- void close() - 关闭与服务器的连接。
- void textMessageReceived(const QString &message) - 收到文本消息时触发的信号,其参数就是收到的文本消息内容。
- void binaryMessageReceived(const QByteArray &message) - 收到二进制消息时触发的信号,参数是原始二进制数据。
- void error(QAbstractSocket::SocketError socketError) - 发生错误时触发的信号,参数是错误类型。
- void stateChanged(QAbstractSocket::SocketState state) - 连接状态变化时触发,可以得知连接是否建立等。
- void textMessageSent(qint64 numBytes) - 发送文本消息完成后触发,numBytes是字节数。
- void bytesWritten(qint64 bytes) - 消息发送过程中的写入回调, bytes是一个部分发送出去的字节数。
- void abort() - 主动断开连接。
- bool waitForConnected(int msec = 30000) - 阻塞等待连接建立成功。
- QString hostName() - 获取当前连接的主机名,常用于判断连接是否成功。
- quint16 port() - 获取主机端口号。
- bool openMode() - 判断当前是否为主动连接还是被动接受模式。
- void writeTextMessage(const QString &text) - 发送文本消息,相比textMessage等更直观。
- void writeMessage(const QByteArray &data) - 发送二进制数据。
- qint64 bytesAvailable() - 查看接收缓存中可读取字节数。
- qint64 readBufferSize() - 设置双向数据接收缓存大小。
- void pauseIncomingPayload() - 暂停接收消息流。
- void resumeIncomingPayload() - 恢复接收。
- bool isValid() - 检查连接是否有效。
- 另外,作为QT套接字,它还支持一些通用功能:
- void setProxy() - 设置代理。
- void encrypt() - 设置SSL安全连接。
- void flush() - 强制输出缓存写出。
- bool waitForBytesWritten() - 等待数据发送完毕。
-
- void QWebSocket::sendTextMessage(const QString &message) 用于发送文本消息
- 使用这个函数发送文本消息主要有以下几点需要注意的地方:
- 1发送文本消息前请确保WebSocket连接已经建立。可以通过ReadyState判断连接状态。
- 2发送的消息内容必须是纯文本,不支持转义编码等更多格式。
- 3一条消息发送完毕后,会触发textMessageSent()信号通知。
- 4可以通过waitForBytesWritten()等待数据完全发送出去。
- 5发送数据顺序可能与收到响应顺序不一致,需要应用层自己处理序号等。
- 6若消息较大,建议使用write或send到套接字后flush,而不是sendTextMessage。
- 7跨平台考虑,消息内容编码最好使用QString而不是QByteArray。
- 8使用该函数发送的文本消息类型,服务端一般对应文本框接受。
- 9可以绑定消息发送断开连接的异常处理等。
Sowohl die Funktionen sendTextMessage als auch writeTextMessage können zum Senden von Textnachrichten verwendet werden, es gibt jedoch einige Unterschiede:
sendTextMessage gehört zur Mitgliedsfunktion von QWebSocket;
writeTextMessage ist eine Mitgliedsfunktion von QAbstractSocket und QWebSocket erbt von QAbstractSocket.
Intern konvertiert sendTextMessage die Nachricht zunächst in QByteArray und sendet sie dann über die Schreibfunktion, was eine weitere Konvertierung darstellt.
writeTextMessage schreibt den zu sendenden QString direkt, was etwas effizienter ist.
sendTextMessage ist eine synchrone Operation und wird nach Abschluss des Sendens zurückgegeben.
writeTextMessage unterstützt asynchrone Aufrufe und die Rückruffunktion kann über Lambda angegeben werden.
sendTextMessage gibt keine Fehlerinformationen zurück und kann nur durch Signalfehler behandelt werden;
writeTextMessage kann den zurückgegebenen Fehlercode abrufen, um die Sendesituation zu bestimmen.
sendTextMessage konzentriert sich auf WebSocket und eignet sich zum Aufrufen der WebSocket-API.
writeTextMessage ist allgemeiner und kann mit anderen QAbstractSocket-Unterklassen verwendet werden.
Allgemein:
sendTextMessage ist einfacher zu verwenden, gut gekapselt und für den einfachen Gebrauch geeignet;
writeTextMessage ist etwas effizienter, unterstützt mehr Funktionen wie Asynchronität und Fehlerbehandlung und eignet sich für Szenarien mit hohen Leistungs- oder Kontrollanforderungen.
Private Kommunikation zwischen Client und Client, Kommunikation zwischen Client und Server.
Prozess: QWebServer erstellen, neuen Verbindungsrückruf binden und auf Trennung warten.
Rückruf für neue Verbindung: Wenn der Sammlung eine neue Verbindung hinzugefügt wird, werden Offline- sowie Empfangs- und Fehlerrückrufe an den neuen Verbindungssocket gebunden.
Schaltfläche „Nachricht senden“: Alle und eins klassifizieren, die festgelegte Sammlung durchlaufen und mit sendTextMesg senden
- #include "widget.h"
- #include "ui_widget.h"
- //这个是服务器端
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- /*
- * mode
- NonSecureMode: 不安全模式,即不使用SSL/TLS进行通信。这是默认值。
- SecureMode: 安全模式,以SSL/TLS安全通信。客户端和服务端之间的连接将使用SSL握手建立安全通道。
- AutomaticallyAcceptServerCertificates: 自动接受服务器证书。在SecureMode下,客户端无法验证证书时,自动接受服务器发来的证书以建立连接。
- VerifyNone: 不验证证书。以SecureMode运行,但不会验证客户端和服务端使用的证书。
- */
- webServer = new QWebSocketServer("testWebServer",QWebSocketServer::NonSecureMode,this);
- QObject::connect(webServer,&QWebSocketServer::newConnection,this,&Widget::MyselfNewConnectCallBackSlot);
-
- webServer->listen(QHostAddress::Any,8888);
- }
- void Widget::MyselfNewConnectCallBackSlot(){//新连接回调
-
- if(webServer->hasPendingConnections()){
- QWebSocket* websocket=webServer->nextPendingConnection();
- ui->msgtext->append(websocket->origin()+"客户端已连接到服务器");
- sockets<<websocket;
-
- QListWidgetItem * item =new QListWidgetItem;
- item->setText(websocket->origin());
- ui->clinetls->addItem(item);
-
-
- //绑定离开
- QObject::connect(websocket,&QWebSocket::disconnected,this,[websocket,this](){
- ui->msgtext->append(websocket->origin()+"客户端断开服务器连接");
- sockets.removeOne(websocket);
- for(int i=0;i<ui->clinetls->count();i++)
- {
- QListWidgetItem *item=ui->clinetls->item(i);
-
- if(item->text()==websocket->origin())
- {
- ui->clinetls->removeItemWidget(item);
- delete item;
- break;
- }
- }
-
- websocket->deleteLater();
-
- });
-
-
- //接受消息回调
- QObject::connect(websocket,&QWebSocket::textMessageReceived,this,[this](const QString &msg){
- QJsonDocument doc =QJsonDocument::fromJson(msg.toLatin1().data());
- if(doc.isNull()){
- QWebSocket* websocket =qobject_cast<QWebSocket*>(sender());//sender 触发信号的源头
- ui->msgtext->append("收到客户端消息["+websocket->origin()+"]--->"+msg);
-
- }else{
- //客户端之间的单发消息
- QJsonObject obj=doc.object();
- QString dst=doc["dst"].toString();
- for (auto& socket : sockets) {
- if(dst == socket->origin()){
- socket->sendTextMessage(msg);
- }
- }
- }
- });
-
-
- QObject::connect(websocket,QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error),
- this,[this](QAbstractSocket::SocketError error){
- QWebSocket* web =qobject_cast<QWebSocket*>(sender());
- ui->msgtext->append(web->origin()+"出错"+web->errorString());
- });
- }
-
-
-
- }
- Widget::~Widget()
- {
- delete ui;
- for(auto socket:sockets)
- {
- socket->close();
- }
-
- webServer->close();
- }
-
-
- void Widget::on_sendpb_clicked()
- {
- QString send =ui->sendtext->toPlainText().trimmed();
- if(send.isEmpty())return;
- if(ui->allradio->isChecked()){
- //群发
- if(sockets.size()==0)return;
- foreach(auto &socket,sockets){
- socket->sendTextMessage(send);
- }
- ui->msgtext->append("服务器给所有连接发送:"+send);
- }else{ //私发 取客户端名称 找 发
- if(!ui->clinetls->currentItem())return;
- QString cname =ui->clinetls->currentItem()->text();
-
- for(auto &socket:sockets)
- {
- if(socket->origin()==cname)
- {
- socket->sendTextMessage(send);
- ui->msgtext->append("服务端给["+socket->origin()+"]发送--->"+send);
-
- break;
- }
- }
- }
-
-
- ui->sendtext->clear();
- }
Klicken Sie auf die Schaltfläche, um den WebSocket-Verbindungsprozess zu implementieren: WebSocket erstellen, verschiedene Rückrufe binden und über open(URL) eine Verbindung herstellen.
Schaltfläche „Nachricht senden“: Private Nachrichten und Servernachrichten klassifizieren, private Nachrichten im JSON-Format kapseln und dann senden.
- #include "widget.h"
- #include "ui_widget.h"
- #include<QMessageBox>
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
-
- websocket=nullptr;
-
- }
-
- Widget::~Widget()
- {
- delete ui;
- }
-
-
- void Widget::on_sendpb_clicked()
- {
- if(!websocket && !websocket->isValid())
- return;
-
- QString send=ui->sendtext->toPlainText().trimmed();
- if(send.isEmpty())return;
-
-
- QString cname =ui->onetextmsg->text().trimmed();
- if(cname.isEmpty()){ //发给服务器
- websocket->sendTextMessage(send);
- ui->msgtext->append("发送消息:"+send);
- }else{
- //私聊发
- QJsonObject obj;
- obj["src"]=websocket->origin();
- obj["dst"]=cname;
- obj["msg"]=send;
- //将一个QJsonObject类型的obj转换成JSON字符串。 Compact参数指定格式化方式为紧凑格式(每个元素占一行)。紧凑格式输出结构清晰,容量小,适合传输和存储。
- QString str(QJsonDocument(obj).toJson(QJsonDocument::Compact));
- websocket->sendTextMessage(str);
- }
- ui->sendtext->clear();
-
-
- }
-
- void Widget::on_connect_clicked()
- {
-
- if(websocket == nullptr){
- if(ui->server_addr->text().trimmed().isEmpty()){
- QMessageBox::critical(this,"错误","服务器名称不能为空,请重新检查!",QMessageBox::Yes);
- return;
- }
- //用于移除字符串开头和结尾处的空白字符。 输入: " test " trimmed()后的结果: "test" VersionLatest使用最新的websocket协议版本。
- websocket=new QWebSocket(ui->client_name->text().trimmed(),QWebSocketProtocol::VersionLatest,this);
-
- //连接回调
- QObject::connect(websocket,&QWebSocket::connected,this,[this](){
- ui->msgtext->append("已经连接上"+websocket->peerAddress().toString());
- isConnecting=true;
- ui->connect->setText("断开服务器");
- });
- //断开回调
- QObject::connect(websocket,&QWebSocket::disconnected,this,[this](){
- ui->msgtext->append("已"+websocket->peerAddress().toString()+"断开连接");
- isConnecting=false;
- ui->connect->setText("连接服务器");
- });
-
- QObject::connect(websocket,QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error)
- ,this,[this](QAbstractSocket::SocketError){
- ui->msgtext->append(websocket->origin()+"出错"+websocket->errorString());
-
- });
-
-
- //接受消息回调 网上知名bug 当你连续发送 A:A*100 B:200时 接收方:A*100200 通过消息队列方式发送即可
- connect(websocket,&QWebSocket::textMessageReceived
- ,this,[this](const QString &msg){
- QJsonDocument jsd=QJsonDocument::fromJson(msg.toUtf8().data());
-
- if(jsd.isNull()) //解析失败 即没有 c1:c2 客户端与客户端私聊
- {
- ui->msgtext->append("收到消息:"+msg);
- }
- else
- {
- QJsonObject jsobj=jsd.object();
- ui->msgtext->append("收到来自"+jsobj["src"].toString()+"的消息:"+jsobj["msg"].toString());
- }
- },Qt::QueuedConnection);
-
-
-
- }
-
- if(isConnecting){ //连接是成功的
- websocket->close();
- websocket->deleteLater();
- websocket=nullptr;
-
- } else
- {
- websocket->open(QUrl(ui->server_addr->text().trimmed()));
- }
- }
Im Allgemeinen stellt QWebSocket als Komponente der QT-Netzwerkbibliothek eine Reihe praktischer APIs für die Entwicklung von WebSocket-Clients und -Servern bereit.
Seine Hauptvorteile sind:
Vollständig objektorientiertes Design, API ist einfach und benutzerfreundlich.
Hochgradig integriert mit anderen QT-Netzwerkkomponenten, wie z. B. SSL/Proxy-Unterstützung.
Mithilfe eines ereignisgesteuerten Modells müssen sich Entwickler nicht mit zugrunde liegenden Details wie Multithreading befassen.
Natürlich in Qt-GUI-Anwendungen integriert, können Nachrichten und Schnittstellenaktualisierungen direkt aufgerufen werden.
Bietet eine vollständige Implementierung der WebSocket-Basisspezifikationen, die sofort verwendet werden können, um die Entwicklung zu erleichtern.
Auch die Performance ist gut, insbesondere da QT5.10 asynchrone I/O-Aufrufe unterstützt.
Umfangreiche Beispiele und Open-Source-Projekte stehen als Referenz zur Verfügung und die Eintrittsbarriere ist niedrig.
Zu den zu beachtenden Punkten gehören:
Einige erweiterte Websocket-Protokollformate werden nicht unterstützt und müssen selbst implementiert werden.
Die Reihenfolge des Sendens und Empfangens von Nachrichten muss von Ihnen selbst gesteuert werden.
Die Unterstützung der Datei- und Streaming-Big-Data-Übertragung ist nicht freundlich und unkompliziert genug.
Die zugrunde liegenden intelligenten Zeiger und verwendeten Speicherverwaltungsmechanismen können nicht geändert werden.
Die Unterstützung für neue C++-Standardfunktionen ist relativ konservativ.
Insgesamt bietet QWebSocket eine sehr gute und ausgereifte Wahl für die meisten Tcp-basierten WebSocket-Anwendungen. Hohe Entwicklungseffizienz und wenige Fehler. Auch für QT-Anwendungen bevorzugt. Bei übergeordneten Anforderungen können andere zugrunde liegende Implementierungen in Betracht gezogen werden. Aber für die meisten Fälle ist QWebSocket gut genug.