// 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 "echo_server.h" #include #include #include #include "ppapi/c/pp_errors.h" #include "ppapi/cpp/var.h" #include "ppapi/utility/completion_callback_factory.h" #ifdef WIN32 #undef PostMessage #endif // Number of connections to queue up on the listening // socket before new ones get "Connection Refused" static const int kBacklog = 10; // Implement htons locally. Even though this is provided by // nacl_io we don't want to include nacl_io in this simple // example. static uint16_t Htons(uint16_t hostshort) { uint8_t result_bytes[2]; result_bytes[0] = (uint8_t) ((hostshort >> 8) & 0xFF); result_bytes[1] = (uint8_t) (hostshort & 0xFF); uint16_t result; memcpy(&result, result_bytes, 2); return result; } void EchoServer::Start(uint16_t port) { if (!pp::TCPSocket::IsAvailable()) { Log("TCPSocket not available"); return; } listening_socket_ = pp::TCPSocket(instance_); if (listening_socket_.is_null()) { Log("Error creating TCPSocket."); return; } std::ostringstream status; status << "Starting server on port: " << port; Log(status.str().c_str()); // Attempt to listen on all interfaces (0.0.0.0) // on the given port number. PP_NetAddress_IPv4 ipv4_addr = { Htons(port), { 0 } }; pp::NetAddress addr(instance_, ipv4_addr); pp::CompletionCallback callback = callback_factory_.NewCallback(&EchoServer::OnBindCompletion); int32_t rtn = listening_socket_.Bind(addr, callback); if (rtn != PP_OK_COMPLETIONPENDING) { Log("Error binding listening socket."); return; } } void EchoServer::OnBindCompletion(int32_t result) { if (result != PP_OK) { std::ostringstream status; status << "server: Bind failed with: " << result; Log(status.str().c_str()); return; } pp::CompletionCallback callback = callback_factory_.NewCallback(&EchoServer::OnListenCompletion); int32_t rtn = listening_socket_.Listen(kBacklog, callback); if (rtn != PP_OK_COMPLETIONPENDING) { Log("server: Error listening on server socket."); return; } } void EchoServer::OnListenCompletion(int32_t result) { std::ostringstream status; if (result != PP_OK) { status << "server: Listen failed with: " << result; Log(status.str().c_str()); return; } pp::NetAddress addr = listening_socket_.GetLocalAddress(); status << "server: Listening on: " << addr.DescribeAsString(true).AsString(); Log(status.str().c_str()); ready_ = true; if (ready_cond_) { pthread_mutex_lock(ready_lock_); pthread_cond_signal(ready_cond_); pthread_mutex_unlock(ready_lock_); } TryAccept(); } void EchoServer::OnAcceptCompletion(int32_t result, pp::TCPSocket socket) { std::ostringstream status; if (result != PP_OK) { status << "server: Accept failed: " << result; Log(status.str().c_str()); return; } pp::NetAddress addr = socket.GetLocalAddress(); status << "server: New connection from: "; status << addr.DescribeAsString(true).AsString(); Log(status.str().c_str()); incoming_socket_ = socket; TryRead(); } void EchoServer::OnReadCompletion(int32_t result) { std::ostringstream status; if (result <= 0) { if (result == 0) status << "server: client disconnected"; else status << "server: Read failed: " << result; Log(status.str().c_str()); // Remove the current incoming socket and try // to accept the next one. incoming_socket_.Close(); incoming_socket_ = pp::TCPSocket(); TryAccept(); return; } status << "server: Read " << result << " bytes"; Log(status.str().c_str()); // Echo the bytes back to the client pp::CompletionCallback callback = callback_factory_.NewCallback(&EchoServer::OnWriteCompletion); result = incoming_socket_.Write(receive_buffer_, result, callback); if (result != PP_OK_COMPLETIONPENDING) { status << "server: Write failed: " << result; Log(status.str().c_str()); } } void EchoServer::OnWriteCompletion(int32_t result) { std::ostringstream status; if (result < 0) { status << "server: Write failed: " << result; Log(status.str().c_str()); return; } status << "server: Wrote " << result << " bytes"; Log(status.str().c_str()); // Try and read more bytes from the client TryRead(); } void EchoServer::TryRead() { pp::CompletionCallback callback = callback_factory_.NewCallback(&EchoServer::OnReadCompletion); incoming_socket_.Read(receive_buffer_, kBufferSize, callback); } void EchoServer::TryAccept() { pp::CompletionCallbackWithOutput callback = callback_factory_.NewCallbackWithOutput( &EchoServer::OnAcceptCompletion); listening_socket_.Accept(callback); }