Qt QWebSocket客户端实现例子

前言

最近使用到了webSocket, 记录一下搜索到的比较好的文章。

websocket是什么

WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

  • WebSocket是一种在单个TCP连接上进行全双工通信的协议。
  • WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
  • WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

 

为什么需要websocket

由于HTTP 协议缺陷:通信只能由客户端发起。导致我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。

websocket特点

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

websocket QT中代码实现

WebSocketClient.h
 
class WebSocketClient : public QThread
{
    Q_OBJECT
public:
    WebSocketClient();
    ~WebSocketClient();
 
 
protected:
    virtual void run();
 
public:
    // 启动连接 - 必须把url地址 协议头 设置完成后再启动
    bool startConnect();
 
    // 断开连接
    void disconnect();
 
    //设置链接的URL
    void setConnectUrl(QString val);
 
    // 向协议队列中添加要发送的内容
    bool addSendText(QString val);
 
    // 设置websocket 的 Sec—Websocket-Protocol 头
    void setSecWebSocketProtocolHeader(QString val);
 
    // 设置心跳数据发送时间间隔 单位秒
    void setHeartbeatTimer(int val);
 
    // 设置判断需要重连的心跳发送失败次数
    void setReconnetHeartbeatTimerCount(int val);
 
    // 设置重连的时间间隔 单位秒
    void setReconnectTimer(int val);
 
    // 设置判断断开的重连次数
    void setDisconnectReconnectCount(int val);
 
    // 获取当前状态
    webSocketState getCurrentState();
 
private slots :
    // 连接成功
    void onConnected();
 
    // 连接断开
    void onDisconnected();
 
    // 接收到 协议内容
    void onTextMessageReceived(const QString &val);
 
    // 定时发送心跳
    void onSendHeartbeatText();
 
    // 定时重连
    void onReconnect();
 
private:
    webSocketState			m_webSocketState;					// websocket 状态
    bool					m_bCloseByHand;						// 手动关闭
    QMutex					m_mutex;							// 数据同步
    QLinkedList<QString>	m_sendTextLinkedList;				// 协议链表
    QString					m_strURL;							// URL地址
    QString					m_strSecWebsocketProtocol;			// 协议头 目前使用此协议头 与网页端保持一致
    QString					m_strHeartbeatText;					// 心跳内容
    QWebSocket				m_websocket;						// websocket 客户端
    int						m_nHeartbeatTimer;					// 心跳数据发送时间间隔 单位ms
    int						m_nReconnectHeartbatTimerCount;		// 需要重连的心跳发送失败次数
    int						m_nReconnectTimer;					// 重连的时间间隔 单位ms
    int						m_nDisconnectReconnectCount;		// 重连失败次数
    int						m_nHeartbeatFailedCount;			// 心跳发送失败次数
    int						m_nReconnectFailedCount;			// 重连失败次数
    QTimer					m_timerHeartbeat;					// 心跳发送定时器
    QTimer					m_timerReconnect;					// 断线重连定时器
 
};




WebSocketClient.cpp
 
#include "WebSocketClient.h"
 
WebSocketClient::WebSocketClient()
    : m_bCloseByHand(false)
    , m_webSocketState(stateNone)
    , m_nHeartbeatTimer(5000)
    , m_nReconnectHeartbatTimerCount(3)
    , m_nReconnectTimer(5000)
    , m_nDisconnectReconnectCount(3)
    , m_nHeartbeatFailedCount(0)
    , m_nReconnectFailedCount(0)
    , m_strURL(tr(""))
    , m_strSecWebsocketProtocol(tr(""))
    , m_strHeartbeatText(tr(""))
{
    connect(&m_websocket, SIGNAL(connected()), this, SLOT(onConnected()));
    connect(&m_websocket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
    connect(&m_websocket, SIGNAL(textMessageReceived(QString)), this, SLOT(onTextMessageReceived(QString)));
    connect(&m_timerHeartbeat, SIGNAL(timeout()), this, SLOT(onSendHeartbeatText()));
    connect(&m_timerReconnect, SIGNAL(timeout()), this, SLOT(onReconnect()));
}
 
WebSocketClient::~WebSocketClient()
{
    if (disconnected  != m_webSocketState)
    {
        disconnect();
    }
}
 
 
void WebSocketClient::run()
{
    while (true)
    {
        if (m_webSocketState == connected)
        {
            m_mutex.lock();
            if (m_sendTextLinkedList.size())
            {
                QString strVal = m_sendTextLinkedList.first();
                if (m_websocket.sendTextMessage(strVal) == strVal.toLocal8Bit().length())
                {
                    m_sendTextLinkedList.erase(m_sendTextLinkedList.begin());
                }
                m_mutex.unlock();
            }
            else
            {
                //当没有数据时,睡眠,否则cpu会过高
                QThread::msleep(20);
                m_mutex.unlock();
            }
 
        }
        else if(m_bCloseByHand)
        {
            break;
        }
    }
}
 
 
bool WebSocketClient::startConnect()
{
    bool bRet = false;
 
    // 如果URL地址 或者 特定的协议头为空 返回false
    if (!m_strURL.isEmpty() && !m_strSecWebsocketProtocol.isEmpty())
    {
        if (!isRunning())
        {
            start();
        }
        QNetworkRequest request;
        request.setUrl(QUrl(m_strURL));
        QByteArray byteHeader = "Protocol";
        request.setRawHeader(byteHeader, m_strSecWebsocketProtocol.toLocal8Bit());
        m_websocket.open(request);
        bRet = true;
    }
 
    return bRet;
 
}
 
void WebSocketClient::disconnect()
{
    m_bCloseByHand = true;
    m_websocket.close();
    m_timerHeartbeat.stop();
    m_timerReconnect.stop();
}
 
 
void WebSocketClient::setConnectUrl(QString val)
{
    m_strURL = val;
}
 
bool WebSocketClient::addSendText(QString val)
{
    bool bRet = false;
    if (m_webSocketState == disconnected || m_webSocketState == stateNone)
    {
        bRet = false;
    }
    else
    {
        m_mutex.lock();
        m_sendTextLinkedList.append(val);
        m_mutex.unlock();
        bRet = true;
    }
    return bRet;
}
 
void WebSocketClient::setSecWebSocketProtocolHeader(QString val)
{
    m_strSecWebsocketProtocol = val;
}
 
void WebSocketClient::setHeartbeatTimer(int val)
{
    if (val > 5000)
    {
        m_nHeartbeatTimer = val;
    }
}
 
void WebSocketClient::setReconnetHeartbeatTimerCount(int val)
{
    if (val > 3)
    {
        m_nReconnectHeartbatTimerCount = val;
    }
}
 
 
void WebSocketClient::setReconnectTimer(int val)
{
    if (val > 5000)
    {
        m_nReconnectTimer = val;
    }
}
 
void WebSocketClient::setDisconnectReconnectCount(int val)
{
    if (val > 3)
    {
        m_nDisconnectReconnectCount = val;
    }
}
 
 
WebSocketClient::webSocketState WebSocketClient::getCurrentState()
{
    return m_webSocketState;
}
 
void WebSocketClient::onConnected()
{
    // 如果是重连成功 停止重连的定时Timer
    if (m_webSocketState == reconnecting)
    {
        m_timerReconnect.stop();
    }
    m_webSocketState = connected;
    m_timerHeartbeat.start(m_nHeartbeatTimer);
    m_nReconnectFailedCount = 0;
    m_nHeartbeatFailedCount = 0;
}
 
 
void WebSocketClient::onDisconnected()
{
    // 如果不是手动关闭 则需要重连
    if (!m_bCloseByHand)
    {
        m_timerHeartbeat.stop();
        m_timerReconnect.start(m_nReconnectTimer);
        m_webSocketState = reconnecting;
    }
}
 
 
void WebSocketClient::onTextMessageReceived(const QString &val)
{
    //收到网页后端发来的内容,处理内容
}
 
 
void WebSocketClient::onSendHeartbeatText()
{
    // 加锁 保证websocket 只在同一时间发送一条数据
    m_mutex.lock();
    int nSendByte =	m_websocket.sendTextMessage(m_strHeartbeatText);
    m_mutex.unlock();
    // 发送心跳失败
    if (nSendByte != m_strHeartbeatText.toLocal8Bit().length())
    {
        m_nHeartbeatFailedCount++;
        // 失败次数等于启动重连的次数 停止发送心跳 开启重连 更新状态
        if (m_nHeartbeatFailedCount == m_nReconnectHeartbatTimerCount)
        {
            m_timerHeartbeat.stop();
            m_timerReconnect.start(m_nReconnectTimer);
            m_webSocketState = reconnecting;
        }
    }
}
 
 
void WebSocketClient::onReconnect()
{
    // close websocket
    m_websocket.close();
    //如果重连次数已到 不再重连 处于断开状态
    if (m_nReconnectFailedCount == m_nReconnectHeartbatTimerCount)
    {
        m_webSocketState = disconnected;
        m_timerReconnect.stop();
    }
    else // 开始连接并计数
    {
        startConnect();
        m_nReconnectFailedCount++;
    }
}

 

 

参考资源

Qt实现QWebSocket客户端,断线重连:https://blog.csdn.net/wzz953200463/article/details/100107882
WebSocket 教程:http://www.ruanyifeng.com/blog/2017/05/websocket.html

 

版权声明:
作者:小何
链接:https://ligo100.cn/houduanjishu/qt/60.html
来源:小何博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
< <上一篇
下一篇>>
文章目录
关闭
目 录