如果還不了解 WebSocket,可以參考我的上一篇博客:【WebSocket】入門教程(JS)
Qt 提供的 QWebSocket 既可以用於客戶端應用程序,也可以用於服務端應用程序,接口大部分和 QTcpSocket 一致。
QWebSocket 當前不支持 WebSocket 擴展和 WebSocket 子協議,僅支持 WebSocket 協議的版本13 (如 RFC 6455 中所述)。
一、WebSocket測試網站
上面是本人搜索到的可以用於 WebSocket 測試的工具網站,用於測試下面寫的 WebSocketClinet。
二、效果展示
效果圖如下所示:
三、代碼實現
准備工作
創建 Qt Widget 工程,首先需要在 .pro 文件中引入:
QT += websockets
WebSocketClinet.h
#ifndef WEBSOCKETCLIENT_H
#define WIDGET_H
#include <QObject>
#include <QtWebSockets>
#include <QDebug>
#include <QUrl>
// WebSocket客戶端管理類
class WebSocketClinet : public QObject
{
Q_OBJECT
public:
explicit WebSocketClinet(QObject *parent = 0);
~WebSocketClinet();
void connectUrl(QString url); // 連接websocket服務器的URL
void close(); // 關閉websocket
void sendTextMsg(const QString &message); // 發送Text類型的消息
void sendBinaryMsg(const QByteArray &data); // 發送Binary類型的消息
bool getConStatus(); // 返回服務器連接狀態
signals:
void sigRecvTextMsg(QString message); // 接受到Text類型消息的信號
private slots:
void slotConnected(); // 連接成功
void slotDisconnected(); // 斷開連接
void slotRecvTextMsg(QString message); // 接受字符數據
void slotRecvBinaryMsg(QByteArray message); // 接受二進制數據
void slotError(QAbstractSocket::SocketError error); // 響應報錯
private:
void reconnect(); // 斷開重連
QWebSocket *m_pWebSocket;
QUrl m_url;
bool m_bConnected = false; // 為true,表明已連接服務器,否則未連接上
};
#endif // WEBSOCKETCLIENT_H
WebSocketClinet.cpp
#include "WebSocketClinet.h"
WebSocketClinet::WebSocketClinet(QObject *parent)
: QObject(parent)
{
m_pWebSocket = new QWebSocket();
// 連接相應的信號槽
connect(m_pWebSocket, SIGNAL(connected()), this, SLOT(slotConnected()));
connect(m_pWebSocket, SIGNAL(disconnected()), this, SLOT(slotDisconnected()));
connect(m_pWebSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotError(QAbstractSocket::SocketError)));
}
WebSocketClinet::~WebSocketClinet()
{
if(m_pWebSocket != 0)
{
m_pWebSocket->deleteLater();
m_pWebSocket = 0;
}
}
// 連接websocket服務器的URL
void WebSocketClinet::connectUrl(QString url)
{
m_url = QUrl(url);
m_pWebSocket->open(m_url);
}
// 關閉websocket
void WebSocketClinet::close()
{
m_pWebSocket->close();
}
// 發送Text類型的消息
void WebSocketClinet::sendTextMsg(const QString &message)
{
if(!m_bConnected)
{
qDebug() << __FILE__ << __LINE__ << "Failed to" << __FUNCTION__ << ", it's not running...";
return;
}
//qDebug() << "send: " << message;
m_pWebSocket->sendTextMessage(message);
}
// 發送Binary類型的消息
void WebSocketClinet::sendBinaryMsg(const QByteArray &data)
{
if(!m_bConnected)
{
qDebug() << __FILE__ << __LINE__ << "Failed to" << __FUNCTION__ << ", it's not running...";
return;
}
m_pWebSocket->sendBinaryMessage(data);
}
// 返回服務器連接狀態
bool WebSocketClinet::getConStatus()
{
return m_bConnected;
}
// 連接成功
void WebSocketClinet::slotConnected()
{
qDebug()<<"connect successful";
m_bConnected = true;
connect(m_pWebSocket, SIGNAL(textMessageReceived(QString)), this, SLOT(slotRecvTextMsg(QString)));
connect(m_pWebSocket, SIGNAL(binaryMessageReceived(QByteArray)), this, SLOT(slotRecvBinaryMsg(QByteArray)));
}
// 斷開連接
void WebSocketClinet::slotDisconnected()
{
qDebug() << __FILE__ << __LINE__ << "disconnected";
reconnect();
}
// 接受字符數據
void WebSocketClinet::slotRecvTextMsg(QString message)
{
emit sigRecvTextMsg(message);
}
// 接受二進制數據
void WebSocketClinet::slotRecvBinaryMsg(QByteArray message)
{
qDebug() << "slotRecvBinaryMsg: " << message;
}
// 響應報錯
void WebSocketClinet::slotError(QAbstractSocket::SocketError error)
{
qDebug() << __FILE__ << __LINE__ << (int)error << m_pWebSocket->errorString();
}
// 斷開重連
void WebSocketClinet::reconnect()
{
qDebug() << "websocket reconnected";
m_pWebSocket->abort();
m_pWebSocket->open(m_url);
}
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QListWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMessageBox>
#include "WebSocketClinet.h"
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void slotSendMsg(); // 發送消息的槽函數
void slotRecvTextMsg(QString sMessage); // 接受WebSocketClient傳來的文本消息
private:
QListWidget *listwidget;
QLineEdit *lineedit;
WebSocketClinet *m_pWebSocketClinet; // WebSocket客戶端
};
#endif // WIDGET_H
Widget.cpp
#include "widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent)
{
this->setWindowTitle("WebSocket客戶端");
// 初始化窗口部件
listwidget = new QListWidget;
lineedit = new QLineEdit;
QPushButton *sendbutton = new QPushButton("發 送");
QPushButton *cancelbutton = new QPushButton("取 消");
this->connect(sendbutton, SIGNAL(clicked()), this, SLOT(slotSendMsg()));
this->connect(cancelbutton, SIGNAL(clicked()), this,SLOT(close()));
// 布局
QHBoxLayout * hlayout = new QHBoxLayout;
hlayout->addStretch(0);
hlayout->addWidget(sendbutton);
hlayout->addWidget(cancelbutton);
QVBoxLayout *vlayout = new QVBoxLayout(this);
vlayout->addWidget(listwidget);
vlayout->addWidget(lineedit);
vlayout->addLayout(hlayout);
// 初始化服務器
m_pWebSocketClinet = new WebSocketClinet;
m_pWebSocketClinet->connectUrl("ws://49.234.18.41:8866");
connect(m_pWebSocketClinet, SIGNAL(sigRecvTextMsg(QString)), this, SLOT(slotRecvTextMsg(QString)));
}
Widget::~Widget()
{
}
// 發送消息的槽函數
void Widget::slotSendMsg()
{
QString content = lineedit->text(); //獲取單行文本框內要發送的內容
if(!content.isEmpty())
{
QDateTime datetime = QDateTime::currentDateTime();
QString str = "send to server : " + datetime.toString("yyyy-M-dd hh:mm:ss") + tr("\n");
str += content;
listwidget->addItem(str); // 將要發送的內容顯示在listwidget
m_pWebSocketClinet->sendTextMsg(str); // 發送消息到服務器
}
else
{
QMessageBox::critical(this, "錯誤", "不能發送空消息!", QMessageBox::Ok);
}
lineedit->clear();
}
// 接受WebSocketClient傳來的文本消息
void Widget::slotRecvTextMsg(QString sMessage)
{
// 加上時間幀
QDateTime datetime = QDateTime::currentDateTime();
QString str = tr("recv from server : ") + datetime.toString("yyyy-M-dd hh:mm:ss") + tr("\n");
str += sMessage;
listwidget->addItem(str); // 將接收到的內容加入到listwidget
}
參考: