Как дождаться ответа WebSocket в PNaCl

Я реализую механизм «ожидания ответа WebSocket перед продолжением» в плагине PNaCl через pp::WebSocketAPI в PPAPI. Ниже приведена упрощенная версия, которая сохраняет данные ответа в глобальном std::string, в то время как функция myecho() отправляет строку через WebSocket и опрашивает до тех пор, пока глобальная строка не изменится. Веб-страница драйвера такая же, как в примере WebSocket в NaCl SDK.

#include <string>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/var_array_buffer.h"
#include "ppapi/utility/websocket/websocket_api.h"

class MyWebSocketReceiveListener
{
public:
    virtual void onWebSocketDataReceived(const std::string& data) = 0;
};

class MyWebSocketAPI : protected pp::WebSocketAPI
{
public:
    MyWebSocketAPI(pp::Instance* ppinstance, MyWebSocketReceiveListener* recvlistener)
        : pp::WebSocketAPI(ppinstance), m_onReceiveListener(recvlistener), m_ppinstance(ppinstance) {}
    virtual ~MyWebSocketAPI() {}

    bool isConnected() { return pp::WebSocketAPI::GetReadyState() == PP_WEBSOCKETREADYSTATE_OPEN; }
    void open(const std::string& url) { pp::WebSocketAPI::Connect(url, NULL, 0); }
    void close() { pp::WebSocketAPI::Close(PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, "bye"); }
    void sendData(const std::string& data) { pp::WebSocketAPI::Send(data); }

protected:
    virtual void WebSocketDidOpen() { m_ppinstance->PostMessage("Connected"); }
    virtual void WebSocketDidClose(bool wasClean, uint16_t code, const pp::Var& reason) {}
    virtual void HandleWebSocketMessage(const pp::Var& message)
    {
        if (message.is_array_buffer()) {
            pp::VarArrayBuffer vararybuf(message);
            char *data = static_cast<char*>(vararybuf.Map());
            std::string datastr(data, data + vararybuf.ByteLength());
            vararybuf.Unmap();
            m_onReceiveListener->onWebSocketDataReceived(datastr);
        } else { // is string
            m_onReceiveListener->onWebSocketDataReceived(message.AsString());
        }
    }
    virtual void HandleWebSocketError() {}
private:
    MyWebSocketAPI(const MyWebSocketAPI&);
    MyWebSocketAPI& operator=(const MyWebSocketAPI&);

    MyWebSocketReceiveListener* const m_onReceiveListener;
    pp::Instance * const m_ppinstance;
};

static std::string g_returnval;

class MyPPPluginInstance : public pp::Instance, public MyWebSocketReceiveListener {
public:
    explicit MyPPPluginInstance(PP_Instance instance)
        : pp::Instance(instance), rpcwebsocket_(this, this) {}
    virtual ~MyPPPluginInstance() {}
    virtual void HandleMessage(const pp::Var& var_message);
    virtual void onWebSocketDataReceived(const std::string& data)
    {
        g_returnval = data;
    }

private:
    bool IsConnected() { return rpcwebsocket_.isConnected(); }
    void Open(const std::string& url)
    {
        rpcwebsocket_.open(url);
        PostMessage(pp::Var("connecting..."));
    }
    void Close()
    {
        if (!IsConnected())
            return;
        rpcwebsocket_.close();
    }

    MyWebSocketAPI rpcwebsocket_;
};

std::string myecho(pp::Instance* inst, MyWebSocketAPI& ws, const std::string& in)
{
    ws.sendData(in);
    while (g_returnval.empty()) {
        usleep(1000 * 1000); // 1 sec
        inst->PostMessage("Waiting for response...");
    }
    return g_returnval;
}

void MyPPPluginInstance::HandleMessage(const pp::Var& var_message) {
    if (!var_message.is_string())
        return;
    std::string message = var_message.AsString();
    // This message must contain a command character followed by ';' and
    // arguments like "X;arguments".
    if (message.length() < 2 || message[1] != ';')
        return;
    switch (message[0]) {
    case 'o':
        // The command 'o' requests to open the specified URL.
        // URL is passed as an argument like "o;URL".
        Open(message.substr(2));
        break;
    case 'c':
        // The command 'c' requests to close without any argument like "c;"
        Close();
        break;
    case 'b':
    case 't':
        PostMessage(std::string("Calling remote echo for ") + message.substr(2));
        std::string ret(myecho(this, rpcwebsocket_, message.substr(2)));
        PostMessage(ret);
        break;
    }
}

// Creates MyPPPluginInstance objects when invoked.
class MyPPPluginModule : public pp::Module {
public:
    MyPPPluginModule() : pp::Module() {}
    virtual ~MyPPPluginModule() {}

    virtual pp::Instance* CreateInstance(PP_Instance instance) {
        return new MyPPPluginInstance(instance);
    }
};

// Implement the required pp::CreateModule function that creates our specific
// kind of Module.
namespace pp {
    Module* CreateModule() { return new MyPPPluginModule(); }
}  // namespace pp

Однако этот подход не сработал. После подключения к эхо-тестовому серверу ws://echo.websocket.org и отправки "привет" я просто получаю

connecting...
Connected
Calling remote echo for hello
Waiting for response...
Waiting for response...
Waiting for response...
Waiting for response...
Waiting for response...

(никогда не отвечает)

Я использовал другой созданный вручную сервер WebSocket для тестирования, и сообщение было успешно отправлено на сервер. И в дополнение к опросу usleep(), как в моем прикрепленном фрагменте, я также пытался использовать pthread_cond_wait() и pthread_cond_signal() для ожидания и уведомления о полученном сообщении.

Что мне сделать, чтобы "ждать pp::WebSocketAPI получения данных" правильно?


person timrau    schedule 31.05.2015    source источник


Ответы (1)


Функция myecho() блокирует MyPPPluginInstance::HandleMessage() и как-то в свою очередь блокирует получение от WebSocket.

Я добавил pp::SimpleThread в качестве нового члена данных класса MyPPPluginInstance и отправил myecho() в другой поток через pp::SimpleThread::message_loop().PostWork(). Работает плавно.

person timrau    schedule 01.06.2015