/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "io_net/io_net_server.h" #include "metafile/nml.h" #include "async/task_loop.h" #include "platform.h" #include "log/log.h" using namespace GS::IO; // #define VERBOSE_LOG // @FIXME Rewrite communication with the controller thread using AsyncCallQueue. //------------------------------------------------------------------------------ NetServer::Client *NetServer::GetClient(void *peer) { ListForeachPtr(Client *, client, clients) if (client->peer == peer) return client; return NULL; } int NetServer::GetClientFreeHandleIndex(const Client &client) const { int free_id = 0; ListForeachPtr(ClientHandleInfo *, i, client.handles) if (i->id >= free_id) free_id = i->id + 1; return free_id; } Handle *NetServer::GetClientHandle(const Client &client, int id) const { ListForeachPtr(ClientHandleInfo *, i, client.handles) if (i->id == id) return i->handle; return NULL; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool NetServer::ProcessCloseCommand(Client &client, GS::StringList &args) // Close,handle_id { if (args.GetCount() != 2) return false; int id = args[1].Integer(); ClientHandleInfo *hi = NULL; ListForeachPtr(ClientHandleInfo *, i, client.handles) if (i->id == id) { hi = i; break; } if (hi == NULL) return false; client.handles.Remove(hi); #ifdef VERBOSE_LOG __LOG__ << "IO::NetServer: Close handle " << id << " (client total: " << client.handles.GetCount() << ").\n"; #endif return SendString(client.peer, "Success"); } bool NetServer::ProcessReadCommand(Client &client, GS::StringList &args) // Read,size,handle_id { if (args.GetCount() != 3) return false; Handle *h = GetClientHandle(client, args[2].Integer()); if (h == NULL) return false; // Read data. size_t size = size_t(args[1].Integer()); Array data(size); size_t read_size = h->Read((void *)data, size); // Format answer data (FIXME two allocations are not required for this). String answer = String::Format("Success,%d,", read_size); Array answer_data(answer.Len() + read_size); Memory::Copy(answer_data.c_ptr(), answer.c_str(), answer.Len()); Memory::Copy(answer_data.c_ptr() + answer.Len(), data.c_ptr(), read_size); return Send(client.peer, (void *)answer_data.c_ptr(), answer_data.GetSize()); } bool NetServer::ProcessSeekCommand(Client &client, GS::StringList &args) // Seek,offset_from_start,ref,handle_id { if (args.GetCount() != 4) return false; Handle *h = GetClientHandle(client, args[3].Integer()); if (h == NULL) return false; ptrdiff_t offset = ptrdiff_t(args[1].Integer()); Base::SeekRef seek_ref; if (args[2] == "Start") seek_ref = Base::SeekStart; else if (args[2] == "Current") seek_ref = Base::SeekCurrent; else if (args[2] == "End") seek_ref = Base::SeekEnd; else return false; size_t r = h->Seek(offset, seek_ref); return SendString(client.peer, String::Format("Success,%d", r)); } bool NetServer::ProcessTellCommand(Client &client, GS::StringList &args) // Tell,handle_id { if (args.GetCount() != 2) return false; Handle *h = GetClientHandle(client, args[1].Integer()); if (h == NULL) return false; return SendString(client.peer, String::Format("Success,%d", h->Tell())); } bool NetServer::ProcessOpenCommand(Client &client, GS::StringList &args) // Open,path { if (args.GetCount() != 2) __ERR__(__LOG_E__ << "NetServer::ProcessOpenCommand(): incorrect argument count.\n", false) int index = GetClientFreeHandleIndex(client); AutoPtr i(new ClientHandleInfo); i->id = index; i->name = args[1]; i->handle = basefs->Open(i->name); if (i->handle.IsNull()) return false; // __ERR__(__LOG_E__ << "NetServer::ProcessOpenCommand(): failed to open '" << i->name << "' on base filesystem.\n", false) client.handles.Add(i.Detach()); #ifdef VERBOSE_LOG __LOG__ << "IO::NetServer: Open handle '" << args[1] << "' => " << index << " (client total: " << client.handles.GetCount() << ").\n"; #endif return SendString(client.peer, String::Format("Success,%d", index)); } bool NetServer::ProcessHashCommand(Client &client, GS::StringList &args) // Hash,path { if (args.GetCount() != 2) return false; String hash = basefs->Hash(args[1]); if (hash.IsEmpty()) return false; return SendString(client.peer, String::Format("Success,%s", hash.c_str())); } bool NetServer::ProcessClientRequest(Client &client, const GS::String &data) { StringList args; data.Split(",", args); bool r = false; // Command dispatch. if (args[0] == "Open") r = ProcessOpenCommand(client, args); else if (args[0] == "Seek") r = ProcessSeekCommand(client, args); else if (args[0] == "Tell") r = ProcessTellCommand(client, args); else if (args[0] == "Read") r = ProcessReadCommand(client, args); else if (args[0] == "Close") r = ProcessCloseCommand(client, args); else if (args[0] == "Hash") r = ProcessHashCommand(client, args); if (!r) SendString(client.peer, "Failed"); return r; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void NetServer::GetStatistics(Statistics &stats) { Time t = Platform::Get().GetTime(); stats.connected = asbool(clients.GetCount()); Network::Enet::Statistics enet_stats; Network::Enet::GetStatistics(enet_stats); if ((t - bandwidth_measure.time).toSec() > 2) { bandwidth = int((enet_stats.sent_data - bandwidth_measure.value) / (t - bandwidth_measure.time).toSec()); bandwidth_measure.time = t; bandwidth_measure.value = enet_stats.sent_data; } stats.sent_data = enet_stats.sent_data; stats.bandwidth = bandwidth; stats.packet_loss = 0; if (clients.GetCount() > 0) { ListForeachPtr(Client *, client, clients) stats.packet_loss += GetPeerPacketLossRatio(client->peer); stats.packet_loss /= clients.GetCount(); } // Handle statistics. int handle_count = 0; ListForeachPtr(Client *, client, clients) handle_count += client->handles.GetCount(); if (stats.handles.Allocate(handle_count)) { handle_count = 0; ListForeachPtr(Client *, client, clients) ListForeachPtr(ClientHandleInfo *, i, client->handles) { Statistics::Handle *h = &stats.handles[handle_count++]; h->name = i->name; } } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void NetServer::OnPeerConnection(void *peer) { __LOG_H__ << "IO::NetServer: OnPeerConnection\n"; __ASSERT__(GetClient(peer) == NULL); clients.Add(new Client(peer)); SetPeerTimeout(peer, TimeoutVeryLong); } void NetServer::OnPacketReceived(void *peer, const void *data, size_t size) { Client *client = GetClient(peer); __ASSERT__(client != NULL); if (size > 512) { SendString(peer, "DataLengthError"); return; // invalid } String command((char *)data, (char *)data + size); if (client->handshake_step == -1) ProcessClientRequest(*client, command); else { switch (client->handshake_step) { case 0: if (command == "RequestIOAccess") { __LOG_V__ << "Granting client access.\n"; client->handshake_step = -1; SendString(peer, "Granted"); } else Disconnect(peer); break; } } } void NetServer::OnConnectionClosed(void *peer) { __LOG_H__ << "IO::NetServer: OnConnectionClosed\n"; Client *client = GetClient(peer); __ASSERT__(client != NULL); clients.Remove(client); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool NetServer::Start(const char *ip, int port) { __LOG_H__ << "IO::NetServer: Starting on " << ip << " port " << port << ".\n"; return OpenServer(ip, port); } void NetServer::Stop() { __LOG_H__ << "IO::NetServer: Shutting down.\n"; // Disconnect all clients. ListForeachPtr(Client *, c, clients) Disconnect(c->peer); StartTaskLoop(clients.GetCount() > 0, 2000) { UpdateHost(); Platform::Get().Sleep(1); } EndTaskLoop Close(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ NetServer::NetServer(Base *fs) : basefs(fs), bandwidth(0) {} NetServer::~NetServer() { Stop(); } //------------------------------------------------------------------------------