如何在 PNaCl 中等待 WebSocket 响应
How to wait for WebSocket response in PNaCl
我正在通过 PPAPI 中的 pp::WebSocketAPI
在 PNaCl 插件上实施 "wait for WebSocket response before continuation" 机制。下面是一个简化版本,将回复的数据存储到全局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
然而,这种方法并没有奏效。连接到回显测试服务器 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服务器进行测试,消息成功发送到服务器。除了我所附代码段中的 usleep()
轮询外,我还尝试使用 pthread_cond_wait()
和 pthread_cond_signal()
等待并通知收到的消息。
我应该怎样做才能"wait pp::WebSocketAPI
receive data"正确?
函数 myecho()
阻止 MyPPPluginInstance::HandleMessage()
并以某种方式反过来阻止从 WebSocket 接收。
我添加了一个 pp::SimpleThread
作为 class MyPPPluginInstance
的新数据成员,并通过 pp::SimpleThread::message_loop().PostWork()
将 myecho()
分派给另一个线程。运行流畅。
我正在通过 PPAPI 中的 pp::WebSocketAPI
在 PNaCl 插件上实施 "wait for WebSocket response before continuation" 机制。下面是一个简化版本,将回复的数据存储到全局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
然而,这种方法并没有奏效。连接到回显测试服务器 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服务器进行测试,消息成功发送到服务器。除了我所附代码段中的 usleep()
轮询外,我还尝试使用 pthread_cond_wait()
和 pthread_cond_signal()
等待并通知收到的消息。
我应该怎样做才能"wait pp::WebSocketAPI
receive data"正确?
函数 myecho()
阻止 MyPPPluginInstance::HandleMessage()
并以某种方式反过来阻止从 WebSocket 接收。
我添加了一个 pp::SimpleThread
作为 class MyPPPluginInstance
的新数据成员,并通过 pp::SimpleThread::message_loop().PostWork()
将 myecho()
分派给另一个线程。运行流畅。