top

Sockets in C++

This tutorial performs a step-by-step decomposition of the "NaCl Sockets" demo application in C++.

Introduction

This article will discuss the structure of the basic socket-using NaCl application written in the C++ programming language. The source code can be found in Tizen Studio or you can click here. The downloaded zip file can be imported into Tizen Studio or extracted and compiled using a separately downloaded NaCl toolchain. Build instructions are available in the demo package.

Note

Please note that you need at least pepper_42 toolchain version to compile this sample application.

We recommend to go through this tutorial along with analyzing the downloaded source code. This analysis will only focus on the native part of the NaCl application, the web part (.html and .js code) will be omitted because it’s trivial and almost identical for every demo presented on this website. If you want to learn more about NaCl or how to implement a basic NaCl application from scratch read Getting started with NaCl and How to create a NaCl App. We also suggest to see Hello World in C++.

There is also a cheat sheet for this demo available in Tizen Studio that explains the application functionality by highlighting most important code fragments. Cheat sheets are available from the Help menu.



The analyzed application demonstrates usage of the NaCl TCP and UDP sockets. The simple server sends back every message it receives.

GUI

The application holds 4 different functionalities and therefore is divided into 4 modules:

  • TCP client,
  • TCP server,
  • UDP client,
  • UDP server.

These functionalities are hidden behind a simple web page.

The TCP or UDP server can be created using the Listen button in the server section of the page. This server automatically starts listening for incoming connections.


Then the client section of the page can be used to connect to the server.

The logs are presented in the NaCl messages box:

Although the usage looks the same for TCP and UDP connections it is different because of the specifics of the used sockets.

Code Analysis

On the main class’, SocketInstance, new instance creation there are created all four needed modules:

 /**
   * TCP client used for connecting, sending and
   * receiving messages.
   */
  SimpleTCPClient tcp_client_;

  /**
   * TCP server used for handling incoming connections and
   * sending/receiving messages from them.
   */
  SimpleTCPServer tcp_server_;

  /**
   * UDP client used for sending and receiving
   * messages.
   */
  SimpleUDPClient udp_client_;

  /**
   * UDP server used for binding to local address and then
   * receiving/sending messages from/to remote hosts.
   */
  SimpleUDPServer udp_server_;

During the initialization of the instance all these objects are polled to check if the interfaces used by these objects are available.

Important

The interfaces may be unavailable if there are insufficient permissions in the manifest file or if the platform doesn’t support this interface.

if (!SimpleTCPClient::InterfacesAreAvailable()) {
    Logger::Error("SocketsInstance: Failed to initialize SimpleTCPClient");
    return false;
  }

The InterfacesAreAvailable()static method checks if isAvailable() method returns true for each interface used in the class:

bool SimpleTCPClient::InterfacesAreAvailable() {
  // Check if TCPSocket interface is available.
  if (!pp::TCPSocket::IsAvailable()) {
    Logger::Error("TCPSocket not available");
    return false;
  }

  // Check if HostResolver interface is available.
  if (!pp::HostResolver::IsAvailable()) {
    Logger::Error("HostResolver not available");
    return false;
  }

  return true;
}

Messages

Only log messages are sent from the NaCl plugin to JavaScript. In the opposite direction there are 5 possible message types:

  • create a client UDP socket and connect to specified host,
  • create a client TCP socket and connect to specified host,
  • close the client TCP or UDP socket,
  • listen on a server UDP or TCP socket,
  • send a message from client socket to server.

TCP Server

The simple server based on pp::TCPSocket handles just one connection at a time. It uses two sockets - one for listening and one for saving the incoming connection.

 /**
   * Socket used as a server.
   */
  pp::TCPSocket listening_socket_;

  /**
   * Socket used for saving incoming connections to server.
   */
  pp::TCPSocket incoming_socket_;

Listening for Connections

The TCP server starts working when user clicks the Listen button. Then the old server instance is reset and new callbacks are created:

 // Create callbacks which will be called upon accepting
  // of incoming connection and receiving message.
  pp::CompletionCallbackWithOutput<std::string> on_server_receive_callback =
      callback_factory_.NewCallbackWithOutput(
          &SocketsInstance::OnTCPServerReceiveCallback);
  pp::CompletionCallbackWithOutput<std::string> on_server_accept_callback =
      callback_factory_.NewCallbackWithOutput(
          &SocketsInstance::OnTCPServerAcceptCallback);

  // If previous server existed, then after this call it will close
  // all existing connections on it.
  tcp_server_.Reset();

  // Start listening on specified port.
  tcp_server_.Listen(port, on_server_accept_callback,
                     on_server_receive_callback);

The Listen() function for the TCP server initializes the listening socket. After performing a successful Bind() operation the actual listening is started.

void SimpleTCPServer::Listen(uint16_t port,
  // ...
  pp::CompletionCallback callback =
      callback_factory_.NewCallback(&SimpleTCPServer::OnBindCompletion);

  // We are binding to the specific address (this operation is
  // optional).
  int32_t rtn = listening_socket_.Bind(addr, callback);
  // ...
}

void SimpleTCPServer::OnBindCompletion(int32_t result) {
  // ...
  pp::CompletionCallback callback =
      callback_factory_.NewCallback(&SimpleTCPServer::OnListenCompletion);
  // After successful Bind, we start automatically listening.
  int32_t rtn = listening_socket_.Listen(kBacklog, callback);
  // ...
}

Handling a Connection

Upon a client connection a listening callback is launched. It tries to accept a connection and, if it succeeds, a previously defined on_server_accept_callback_ callback is run. In this application the callback just logs that a connection was accepted from a specific IP address. The newly created socket connected to a client socket is saved as incoming_socket_. Then a read is performed on the opened connection. The TryRead() function awaits for any message from the client socket. If a message is successfully read and saved in the receive_buffer_variable, the previously defined on_server_receive_callback_callback is run. From here the main program can decide what to do. In this case it should behave as an echo server, so upon a receiving a message, a server sends it back to the client.

// This callback will be run when we receive incoming message on
// server socket.
void SocketsInstance::OnTCPServerReceiveCallback(int32_t result,
                                                 const std::string& message) {
  // ...
  // We respond to sender with message which we received from him
  tcp_server_.Write(message,
                    callback_factory_.NewCallbackWithOutput(
                        &SocketsInstance::OnTCPServerReceiveCallback));
}

The Write() procedure simply writes to a socket connected to the client and returns for waiting for incoming messages.

When the client disconnects, the read fails and the failure message is passed to the on_server_receive_callback_callback. There the client disconnection case is extracted and listening for connections is restarted.

TCP Client

The client TCP side is much simpler. It just uses one pp::TCPSocketobject. After the application initializes, the TCP socket client can connect to a server. It can be a server provided in this plugin or an external one.

Connecting to a Server

When the user clicks the Connect button a connection procedure is started. The new TCP socket is created and the host address is resolved. This is performed using the pp::HostResolver interface and results in transforming an address from a string to a pp::NetAddress object.

void SimpleTCPClient::Connect(const std::string& host,
    pp::CompletionCallbackWithOutput<std::string> receive_callback) {
  // ...
  // Create new TCPSocket which will be used for
  // the future connection.
  tcp_socket_ = pp::TCPSocket(instance_handle_);
  // ...
  // Create new HostResolver to resolve IP from
  // the given host name.
  resolver_ = pp::HostResolver(instance_handle_);
  // ...
  pp::CompletionCallback callback =
      callback_factory_.NewCallback(&SimpleTCPClient::OnResolveCompletion);
  // We resolve the hostname to the IP format.
  resolver_.Resolve(hostname.c_str(), port, hint, callback);
}

void SimpleTCPClient::OnResolveCompletion(int32_t result) {
  // ...

  // We will use the resolved IP address from
  // the provided host name in Connect method.
  pp::NetAddress addr = resolver_.GetNetAddress(0);

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

  // We invoke Connect with the resolved IP address.
  Logger::Log("TCPClient: Connecting ...");
  tcp_socket_.Connect(addr, callback);
}

Then the connection is established and if it succeeds the socket waits for incoming messages using the Receive() function. This does not prevent sending something, as all socket operations are performed asynchronously.

Sending a Message

When a Send button is clicked the text message is written to a socket:

 uint32_t size = message.size();
  const char* data = message.c_str();

  // We write a message to socket.
  result = tcp_socket_.Write(data, size, callback);

As a receive callback is all the time waiting for messages there is no need to explicitly receive a server answer.

The procedure uses three methods - the SimpleTCPClient::Receive()method, that tries to read from a socket, and two callbacks: SimpleTCPClient::OnReceiveCompletion()and SocketsInstance::OnTCPClientReceiveCallback(). When a message from a server arrives OnReceiveCompletion() callback is launched and it invokes OnTCPClientReceiveCallback(). Based on this the OnTCPClientReceiveCallback() method from the main class decides what to do next. In this example application it calls the Receive() method again and the loop closes.

void SimpleTCPClient::Receive(
    pp::CompletionCallbackWithOutput<std::string> on_receive_callback) {
  // We remember on_receive_callback callback
  // so it can be called up after receiving message.
  on_receive_callback_ = on_receive_callback;
  memset(receive_buffer_, 0, kBufferSize);
  pp::CompletionCallback callback =
      callback_factory_.NewCallback(&SimpleTCPClient::OnReceiveCompletion);

  // We invoke reading method on the socket.
  // The receive_buffer_ variable will be filled with
  // the received message, after successful callback completion.
  tcp_socket_.Read(receive_buffer_, kBufferSize, callback);
}

void SimpleTCPClient::OnReceiveCompletion(int32_t result) {
  // ...
  // The receive_buffer_ variable contains our received message.
  std::string message(receive_buffer_, result);
  // We set our message as a parameter, which will be passed to
  // the callback function.
  *on_receive_callback_.output() = message;
  // We run the callback together with result.
  on_receive_callback_.Run(result);
}

// This callback will be run when we receive incoming message on
// client socket.
void SocketsInstance::OnTCPClientReceiveCallback(int32_t result,
                                                 const std::string& message) {
  // ...
  tcp_client_.Receive(
      callback_factory_.NewCallbackWithOutput(
          &SocketsInstance::OnTCPClientReceiveCallback));
}

Closing a Connection

When the user chooses to Close the connection the currently used pp::TCPSocket is closed and reset by creating a new object:

 // Close the connected socket.
  tcp_socket_.Close();
  tcp_socket_ = pp::TCPSocket();

UDP Server

Unlike TCP sockets, UDP sockets server doesn’t need an additional socket for monitoring incoming connections, so message receiving can be performed at once after binding to a listening port.

  /**
   * Socket used as a server.
   */
  pp::UDPSocket udp_listening_socket_;

Listening for Messages

Unlike TCP sockets, UDP sockets don’t need a one-to-one connection. One listening socket can handle messages from multiple sources, so message receiving can be performed at once after binding to a listening port.

void SimpleUDPServer::RecvFrom(uint16_t port,
    pp::CompletionCallbackWithOutput<struct MessageInfo> on_server_receive_from_callback) {
  udp_listening_socket_ = pp::UDPSocket(instance_handle_);
  // ...
  pp::CompletionCallback callback =
      callback_factory_.NewCallback(&SimpleUDPServer::OnBindCompletion, port);
      
  // We are binding to the specific address.
  int32_t rtn = udp_listening_socket_.Bind(addr, callback);
  // ...
}

void SimpleUDPServer::OnBindCompletion(int32_t result, uint16_t port) {
  // ...
  pp::CompletionCallbackWithOutput<pp::NetAddress> callback =
      callback_factory_.NewCallbackWithOutput(
          &SimpleUDPServer::OnRecvFromCompletion);

  // We start receiving data on the socket.
  udp_listening_socket_.RecvFrom(receive_buffer_, kBufferSize, callback);
}

Handling a Message

When a message is received, similarly as in the TCP socket server, an OnUDPServerReceiveCallback() function from SocketInstance is called. Like in the TCP server, it sends back the received message:

void SimpleUDPServer::OnRecvFromCompletion(int32_t result,
                                           pp::NetAddress address) {
  // ...
  std::string message(receive_buffer_, result);
  struct MessageInfo ext_message(address, message);
  // We set our message as a parameter, which will be
  // passed to the callback function.
  *on_server_receive_from_callback_.output() = ext_message;
  on_server_receive_from_callback_.Run(result);
}

// This callback will be run when we receive incoming message on
// server socket.
void SocketsInstance::OnUDPServerReceiveCallback(
    int32_t result, struct MessageInfo extended_message) {
  // ...
  // We respond to a sender with a message which we received from him
  udp_server_.SendTo(extended_message.message, extended_message.remoteAddress,
                     callback_factory_.NewCallbackWithOutput(
                         &SocketsInstance::OnUDPServerReceiveCallback));
}

The sending procedure is trivial, the one step worth mentioning is calling the RecvFrom() socket method upon a successful send. This concludes the receiving-sending loop.

pp::CompletionCallbackWithOutput<pp::NetAddress> callback =
      callback_factory_.NewCallbackWithOutput(
          &SimpleUDPServer::OnRecvFromCompletion);
  // Try to read bytes from the socket.
  udp_listening_socket_.RecvFrom(receive_buffer_, kBufferSize, callback);

UDP Client

The UDP client is almost the same as the TCP one, except for the connection procedure:

  • the socket is created,
  • the address is resolved,
  • the Bind()procedure is invoked,
  • upon a successful binding, waiting for messages starts.

The algorithm for handling a received message is exactly the same as in TCP socket:

  • the OnReceiveFromCompletion()callback is run,
  • the OnUDPClientReceiveFromCallback() callback from the SocketInstance main class is run,
  • the Receive() method is called.

The procedures for sending a message and closing a socket are also identical to the ones in the TCP client.

Summary

This application can be compiled using Tizen Studio or manually with the make program. The manually compiled application can be run in Google Chrome. To run it on Smart TV Emulator you have to build it with Tizen Studio.

After a successful load you should be presented with a webpage that looks like the one below. The NaCl module status field text will change to “Loaded successfully.” and you should be able to create a TCP/UDP server and client and exchange messages between them.