Я реализую механизм ожидания ответа 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
получать данные «правильно?
Функция myecho()
блоки MyPPPluginInstance::HandleMessage()
и как-то в свою очередь блокирует получение из WebSocket.
Я добавил pp::SimpleThread
как новый член данных класса MyPPPluginInstance
и отправка myecho()
в другой поток через pp::SimpleThread::message_loop().PostWork()
, Работает плавно.