如何在 PNaCl 中等待 WebSocket 响应

Posted

技术标签:

【中文标题】如何在 PNaCl 中等待 WebSocket 响应【英文标题】:How to wait for WebSocket response in PNaCl 【发布时间】:2015-05-31 03:07:00 【问题描述】:

我正在通过 PPAPI 中的pp::WebSocketAPI 在 PNaCl 插件上实现“在继续之前等待 WebSocket 响应”机制。下面是一个简化的版本,将回复的数据存储到一个全局的std::string中,而函数myecho()通过WebSocket发送一个字符串并轮询直到全局字符串发生变化。驱动网页与 NaCl SDK 中的 WebSocket 示例相同。

#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

但是,这种方法不起作用。在连接到 echo 测试服务器ws://echo.websocket.org 并发送“hello”后,我就得到了

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

(从不回复)

我用另一个手工制作的WebSocket服务器测试,消息成功发送到服务器。除了我附加的 sn-p 中的usleep() 轮询之外,我还尝试使用pthread_cond_wait()pthread_cond_signal() 来等待并通知收到的消息。

如何正确“等待pp::WebSocketAPI接收数据”?

【问题讨论】:

【参考方案1】:

函数myecho() 阻止MyPPPluginInstance::HandleMessage() 并反过来阻止从WebSocket 接收。

我添加了一个pp::SimpleThread 作为MyPPPluginInstance 类的新数据成员,并通过pp::SimpleThread::message_loop().PostWork()myecho() 分派到另一个线程。它运行顺利。

【讨论】:

以上是关于如何在 PNaCl 中等待 WebSocket 响应的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Cypress.io 中等待 WebSocket STOMP 消息

Flask 之 WebSocket

PNaCl 再见,WebAssembly 你好!

如何让观察者在认证后等待 WebSocket 的创建

PNaCl & gtest—pnacl-ld: 不兼容的目标文件 (X8664 != X8632)

Tornado websocket 客户端:如何异步 on_message? (从未等待协程)