root/native_client_sdk/src/examples/api/socket/socket.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. echo_server_
  2. HandleMessage
  3. IsConnected
  4. IsUDP
  5. Connect
  6. OnResolveCompletion
  7. Close
  8. Send
  9. Receive
  10. OnConnectCompletion
  11. OnReceiveFromCompletion
  12. OnReceiveCompletion
  13. OnSendCompletion
  14. CreateInstance
  15. CreateModule

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stdio.h>
#include <string.h>
#include <sstream>

#include "echo_server.h"

#include "ppapi/cpp/host_resolver.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/tcp_socket.h"
#include "ppapi/cpp/udp_socket.h"
#include "ppapi/cpp/var.h"
#include "ppapi/utility/completion_callback_factory.h"

#ifdef WIN32
#undef PostMessage
// Allow 'this' in initializer list
#pragma warning(disable : 4355)
#endif

class ExampleInstance : public pp::Instance {
 public:
  explicit ExampleInstance(PP_Instance instance)
    : pp::Instance(instance),
      callback_factory_(this),
      send_outstanding_(false),
      echo_server_(NULL) {}

  virtual ~ExampleInstance() {
    delete echo_server_;
  }

  virtual void HandleMessage(const pp::Var& var_message);

 private:
  bool IsConnected();
  bool IsUDP();

  void Connect(const std::string& host, bool tcp);
  void Close();
  void Send(const std::string& message);
  void Receive();

  void OnConnectCompletion(int32_t result);
  void OnResolveCompletion(int32_t result);
  void OnReceiveCompletion(int32_t result);
  void OnReceiveFromCompletion(int32_t result, pp::NetAddress source);
  void OnSendCompletion(int32_t result);

  pp::CompletionCallbackFactory<ExampleInstance> callback_factory_;
  pp::TCPSocket tcp_socket_;
  pp::UDPSocket udp_socket_;
  pp::HostResolver resolver_;
  pp::NetAddress remote_host_;

  char receive_buffer_[kBufferSize];
  bool send_outstanding_;
  EchoServer* echo_server_;
};

#define MSG_CREATE_TCP 't'
#define MSG_CREATE_UDP 'u'
#define MSG_SEND 's'
#define MSG_CLOSE 'c'
#define MSG_LISTEN 'l'

void ExampleInstance::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 MSG_CREATE_UDP:
      // The command 'b' requests to create a UDP connection the
      // specified HOST.
      // HOST is passed as an argument like "t;HOST".
      Connect(message.substr(2), false);
      break;
    case MSG_CREATE_TCP:
      // The command 'o' requests to connect to the specified HOST.
      // HOST is passed as an argument like "u;HOST".
      Connect(message.substr(2), true);
      break;
    case MSG_CLOSE:
      // The command 'c' requests to close without any argument like "c;"
      Close();
      break;
    case MSG_LISTEN:
      {
        // The command 'l' starts a listening socket (server).
        int port = atoi(message.substr(2).c_str());
        echo_server_ = new EchoServer(this, port);
        break;
      }
    case MSG_SEND:
      // The command 't' requests to send a message as a text frame. The
      // message passed as an argument like "t;message".
      Send(message.substr(2));
      break;
    default:
      std::ostringstream status;
      status << "Unhandled message from JavaScript: " << message;
      PostMessage(status.str());
      break;
  }
}

bool ExampleInstance::IsConnected() {
  if (!tcp_socket_.is_null())
    return true;
  if (!udp_socket_.is_null())
    return true;

  return false;
}

bool ExampleInstance::IsUDP() {
  return !udp_socket_.is_null();
}

void ExampleInstance::Connect(const std::string& host, bool tcp) {
  if (IsConnected()) {
    PostMessage("Already connected.");
    return;
  }

  if (tcp) {
    if (!pp::TCPSocket::IsAvailable()) {
      PostMessage("TCPSocket not available");
      return;
    }

    tcp_socket_ = pp::TCPSocket(this);
    if (tcp_socket_.is_null()) {
      PostMessage("Error creating TCPSocket.");
      return;
    }
  } else {
    if (!pp::UDPSocket::IsAvailable()) {
      PostMessage("UDPSocket not available");
      return;
    }

    udp_socket_ = pp::UDPSocket(this);
    if (udp_socket_.is_null()) {
      PostMessage("Error creating UDPSocket.");
      return;
    }
  }

  if (!pp::HostResolver::IsAvailable()) {
    PostMessage("HostResolver not available");
    return;
  }

  resolver_ = pp::HostResolver(this);
  if (resolver_.is_null()) {
    PostMessage("Error creating HostResolver.");
    return;
  }

  int port = 80;
  std::string hostname = host;
  size_t pos = host.rfind(':');
  if (pos != std::string::npos) {
    hostname = host.substr(0, pos);
    port = atoi(host.substr(pos+1).c_str());
  }

  pp::CompletionCallback callback =
      callback_factory_.NewCallback(&ExampleInstance::OnResolveCompletion);
  PP_HostResolver_Hint hint = { PP_NETADDRESS_FAMILY_UNSPECIFIED, 0 };
  resolver_.Resolve(hostname.c_str(), port, hint, callback);
  PostMessage("Resolving ...");
}

void ExampleInstance::OnResolveCompletion(int32_t result) {
  if (result != PP_OK) {
    PostMessage("Resolve failed.");
    return;
  }

  pp::NetAddress addr = resolver_.GetNetAddress(0);
  PostMessage(std::string("Resolved: ") +
              addr.DescribeAsString(true).AsString());

  pp::CompletionCallback callback =
      callback_factory_.NewCallback(&ExampleInstance::OnConnectCompletion);

  if (IsUDP()) {
    PostMessage("Binding ...");
    remote_host_ = addr;
    PP_NetAddress_IPv4 ipv4_addr = { 0, { 0 } };
    udp_socket_.Bind(pp::NetAddress(this, ipv4_addr), callback);
  } else {
    PostMessage("Connecting ...");
    tcp_socket_.Connect(addr, callback);
  }
}

void ExampleInstance::Close() {
  if (!IsConnected()) {
    PostMessage("Not connected.");
    return;
  }

  if (tcp_socket_.is_null()) {
    udp_socket_.Close();
    udp_socket_ = pp::UDPSocket();
  } else {
    tcp_socket_.Close();
    tcp_socket_ = pp::TCPSocket();
  }

  PostMessage("Closed connection.");
}

void ExampleInstance::Send(const std::string& message) {
  if (!IsConnected()) {
    PostMessage("Not connected.");
    return;
  }

  if (send_outstanding_) {
    PostMessage("Already sending.");
    return;
  }

  uint32_t size = message.size();
  const char* data = message.c_str();
  pp::CompletionCallback callback =
      callback_factory_.NewCallback(&ExampleInstance::OnSendCompletion);
  int32_t result;
  if (IsUDP())
     result = udp_socket_.SendTo(data, size, remote_host_, callback);
  else
     result = tcp_socket_.Write(data, size, callback);
  std::ostringstream status;
  if (result < 0) {
    if (result == PP_OK_COMPLETIONPENDING) {
      status << "Sending bytes: " << size;
      PostMessage(status.str());
      send_outstanding_ = true;
    } else {
      status << "Send returned error: " << result;
      PostMessage(status.str());
    }
  } else {
    status << "Sent bytes synchronously: " << result;
    PostMessage(status.str());
  }
}

void ExampleInstance::Receive() {
  memset(receive_buffer_, 0, kBufferSize);
  if (IsUDP()) {
    pp::CompletionCallbackWithOutput<pp::NetAddress> callback =
        callback_factory_.NewCallbackWithOutput(
            &ExampleInstance::OnReceiveFromCompletion);
    udp_socket_.RecvFrom(receive_buffer_, kBufferSize, callback);
  } else {
    pp::CompletionCallback callback =
        callback_factory_.NewCallback(&ExampleInstance::OnReceiveCompletion);
    tcp_socket_.Read(receive_buffer_, kBufferSize, callback);
  }
}

void ExampleInstance::OnConnectCompletion(int32_t result) {
  if (result != PP_OK) {
    std::ostringstream status;
    status << "Connection failed: " << result;
    PostMessage(status.str());
    return;
  }

  if (IsUDP()) {
    pp::NetAddress addr = udp_socket_.GetBoundAddress();
    PostMessage(std::string("Bound to: ") +
                addr.DescribeAsString(true).AsString());
  } else {
    PostMessage("Connected");
  }

  Receive();
}

void ExampleInstance::OnReceiveFromCompletion(int32_t result,
                                              pp::NetAddress source) {
  OnReceiveCompletion(result);
}

void ExampleInstance::OnReceiveCompletion(int32_t result) {
  if (result < 0) {
    std::ostringstream status;
    status << "Receive failed with: " << result;
    PostMessage(status.str());
    return;
  }

  PostMessage(std::string("Received: ") + std::string(receive_buffer_, result));
  Receive();
}

void ExampleInstance::OnSendCompletion(int32_t result) {
  std::ostringstream status;
  if (result < 0) {
    status << "Send failed with: " << result;
  } else {
    status << "Sent bytes: " << result;
  }
  send_outstanding_ = false;
  PostMessage(status.str());
}

// The ExampleModule provides an implementation of pp::Module that creates
// ExampleInstance objects when invoked.
class ExampleModule : public pp::Module {
 public:
  ExampleModule() : pp::Module() {}
  virtual ~ExampleModule() {}

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

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

/* [<][>][^][v][top][bottom][index][help] */