WebSocket的简单使用

WebSocket一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端直接向客户端推送数据而不需要客户端进行请求,在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并允许数据进行双向传送。——- 《维基百科》


最近在项目中使用了WebSocket,开发了一个非常简单的服务器端的小程序,用于接收客户端(前端)发送来的摄像头的图像数据,进行相关的处理(我司的人脸跟踪和属性检测)后,将相应的结果再发送给客户端进行相应的展示(广告推荐)。其实这个程序本来不需要使用WebSocket的,完全在本地进行开发就可以了,但是为了一个好看的界面,因此选择了前端进行界面的设计,也就用到了WebSocket进行双端的通信。
然后去查了下有什么用C++已经封装好的WebSocket,在GitHub上看到了一些开源的项目已经用C++实现了WebSocket,不过又发现Qt也实现了WebSocket,包括QWebSocketServer和QWebSocket,再加上Qt比较不错的信号槽机制可以非常简单的进行事件监听,于是选择使用Qt的WebSocket。但是还是遇到了一些问题。

首先是一开始数据接收速度过慢。客户端发给我的是采样png压缩的图片,由于png是无损压缩,每张图片在250k左右,导致我接收这样的一张图片需要300毫秒左右的时间(由于一张图片太大,一张图片客户端分了多次发送,每次在50毫秒左右)。后来改用了JPEG进行图像压缩,大大减少了传输时间。

再次是Qt的QWebSocket需要设置readBufferSize,如果没有设置,我这里的现象是数据在接收到一段时间后,就不会再接收新的数据。通过阅读Qt的源码,QWebSocket的setReadBufferSize方法最终设置的是TCP Socket的receive buffer,如果socket所在的进程不及时将数据从receive buffer中取出,最终导致receive buffer填满,由于TCP的滑动窗口和拥塞控制,接收端会阻止发送端向其发送数据。(QWebSocket的setReadBufferSize方法官方说明:If the buffer size is limited to a certain size, QWebSocket won’t buffer more than this size of data. Exceptionally, a buffer size of 0 means that the read buffer is unlimited and all incoming data is buffered. This is the default. This option is useful if you only read the data at certain points in time (for example, in a real-time streaming application) or if you want to protect your socket against receiving too much data, which may eventually cause your application to run out of memory.)如果不进行设置,QWebSocket 的read buffer将不受限制,但是导致一次接收过大的数据导致应用程序耗尽了内存。

在接收到数据后进行处理时最好采用多线程的机制去处理,以免影响数据的接收。

#ifndef ATTRIBUTESERVER_H
#define ATTRIBUTESERVER_H

#include <QObject>
#include <QByteArray>
#include <QList>
#include <QPair>

#include <opencv2/opencv.hpp>
#include "attributedetect.h"

QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket)

class AttributeServer : public QObject
{
    Q_OBJECT

public:
    explicit AttributeServer(quint16 port, int frame_count = 10, bool debug = false, QObject *parent = Q_NULLPTR);
    ~AttributeServer();

Q_SIGNALS:
    void closed();

private Q_SLOTS:
    void onNewConnection();
    void processTextMessage(QString message);
    void socketDisconnected();

    void onGetAttribute(QString age, QString sex, QString image);

private:
    QWebSocketServer *m_pWebSocketServer;
    QList<QWebSocket *> m_clients;
    bool m_debug;
    QPair<int, long> m_last_id;
    int m_frame_count;

    AttributeDetect* m_attribute_thread;
    QWebSocket* m_current_clients;
};

#endif // ATTRIBUTESERVER_H
#include "attributeserver.h"

#include <iostream>
#include <algorithm>
#include <string>

#include <QtWebSockets/QWebSocketServer>
#include <QtWebSockets/QWebSocket>
#include <QDebug>
#include <QFile>
#include <QMutex>
#include <QWaitCondition>

#include <opencv2/opencv.hpp>

QMutex g_mutex;
QWaitCondition g_condition;

std::vector<std::string> g_vecImages;

AttributeServer::AttributeServer(quint16 port, int frame_cont, bool bdebug, QObject *parent)
    : QObject(parent),
    m_frame_count(frame_cont),
    m_debug(bdebug),
    m_pWebSocketServer(new QWebSocketServer("Attribute Server", QWebSocketServer::NonSecureMode, this)),
    m_clients()
{
    if (m_pWebSocketServer->listen(QHostAddress::Any, port)) {
        if (m_debug) {
            qDebug() << "Attribute Server listening on port" << port;
        }
        connect(m_pWebSocketServer, &QWebSocketServer::newConnection,
            this, &AttributeServer::onNewConnection);
        connect(m_pWebSocketServer, &QWebSocketServer::closed,
            this, &AttributeServer::closed);
    }

    m_last_id.first = -1;
    m_last_id.second = 0;

    m_attribute_thread = NULL;
    m_current_clients = NULL;
}

AttributeServer::~AttributeServer()
{
    m_pWebSocketServer->close();
    qDeleteAll(m_clients.begin(), m_clients.end());
}

void AttributeServer::onNewConnection()
{
    qDebug() << "new  connection";
    QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();

    connect(pSocket, &QWebSocket::textMessageReceived, this, &AttributeServer::processTextMessage);
    connect(pSocket, &QWebSocket::disconnected, this, &AttributeServer::socketDisconnected);

    quint16 sizes = pSocket->readBufferSize();
    pSocket->setReadBufferSize(640 * 480);

    if (m_attribute_thread == NULL) {
        m_attribute_thread = new AttributeDetect(this, m_frame_count);
        connect(m_attribute_thread, SIGNAL(sendAttribute(QString, QString, QString)), this, SLOT(onGetAttribute(QString, QString, QString)));
        m_attribute_thread->start();
    }

    m_current_clients = pSocket;
    m_clients << pSocket;
}

bool startWith(std::string src, std::string substr)
{
    if (src.empty() || src.size() < substr.size()) {
        return false;
    }

    bool bflag = true;
    for (int i = 0; i < substr.size(); i++){
        if (src[i] != substr[i]) {
            bflag = false;
            break;
        }
    }

    return bflag;
}

void AttributeServer::processTextMessage(QString message)
{
    static std::stringstream buf;
    static long count = 0;

    std::string curr_message = message.toStdString();
    bool bflag = startWith(curr_message, "data:image/jpeg;");

    if (bflag) {
        // 新的数据
        // 先处理掉旧的数据
        std::string str_message = buf.str();
        if (!str_message.empty()) {
            g_mutex.lock();
            if (g_vecImages.size() > 0) {
                g_vecImages.clear();
            }
            g_vecImages.push_back(str_message);

            g_condition.wakeAll();
            g_mutex.unlock();
        }


        // 清除旧的数据,加入新的数据
        buf = std::stringstream();
        buf << curr_message;

    }
    else {
        // 旧的数据
        buf << message.toStdString();
    }
}

void AttributeServer::socketDisconnected()
{

    QWebSocket *pClient = qobject_cast<QWebSocket*>(sender());

    qDebug() << pClient->errorString();

    if (m_debug) {
        qDebug() << "socketDisconnected: " << pClient;
    }
    if (pClient) {
        m_clients.removeAll(pClient);
        pClient->deleteLater();
    }
}

void AttributeServer::onGetAttribute(QString age, QString sex, QString image)
{
    QString str_send_info = QString("{\"age\":\"%1\",\"sex\":\"%2\",\"image\":\"%3\"}")
        .arg(age)
        .arg(sex)
        .arg(image);

    QByteArray postData = str_send_info.toUtf8();
    static long count = 0;
    qDebug() << "begin send attribute result " << count++;
    m_current_clients->sendTextMessage(postData);
    m_current_clients->flush();
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值