network.cpp

Go to the documentation of this file.
00001 /* $Id: network.cpp 53856 2012-04-08 08:17:49Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Networking
00019  */
00020 
00021 #include "global.hpp"
00022 
00023 #include "gettext.hpp"
00024 #include "log.hpp"
00025 #include "network_worker.hpp"
00026 #include "serialization/string_utils.hpp"
00027 #include "thread.hpp"
00028 #include "util.hpp"
00029 #include "config.hpp"
00030 
00031 #include "filesystem.hpp"
00032 
00033 
00034 #include <cerrno>
00035 #include <queue>
00036 #include <iomanip>
00037 #include <set>
00038 #include <cstring>
00039 #include <stdexcept>
00040 
00041 #include <signal.h>
00042 #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
00043 #undef INADDR_ANY
00044 #undef INADDR_BROADCAST
00045 #undef INADDR_NONE
00046 #include <windows.h>
00047 #else
00048 #include <sys/types.h>
00049 #include <sys/socket.h>
00050 #include <netinet/in.h>
00051 #include <netinet/tcp.h>  // for TCP_NODELAY
00052 #ifdef __BEOS__
00053 #include <socket.h>
00054 #else
00055 #include <fcntl.h>
00056 #endif
00057 #define SOCKET int
00058 #endif
00059 
00060 static lg::log_domain log_network("network");
00061 #define DBG_NW LOG_STREAM(debug, log_network)
00062 #define LOG_NW LOG_STREAM(info, log_network)
00063 #define WRN_NW LOG_STREAM(warn, log_network)
00064 #define ERR_NW LOG_STREAM(err, log_network)
00065 // Only warnings and not errors to avoid DoS by log flooding
00066 
00067 namespace {
00068 
00069 // We store the details of a connection in a map that must be looked up by its handle.
00070 // This allows a connection to be disconnected and then recovered,
00071 // but the handle remains the same, so it's all seamless to the user.
00072 struct connection_details {
00073     connection_details(TCPsocket sock, const std::string& host, int port)
00074         : sock(sock), host(host), port(port), remote_handle(0),
00075           connected_at(SDL_GetTicks())
00076     {}
00077 
00078     TCPsocket sock;
00079     std::string host;
00080     int port;
00081 
00082     // The remote handle is the handle assigned to this connection by the remote host.
00083     // Is 0 before a handle has been assigned.
00084     int remote_handle;
00085 
00086     int connected_at;
00087 };
00088 
00089 typedef std::map<network::connection,connection_details> connection_map;
00090 connection_map connections;
00091 
00092 network::connection connection_id = 1;
00093 
00094 /** Stores the time of the last server ping we received. */
00095 time_t last_ping, last_ping_check = 0;
00096 
00097 } // end anon namespace
00098 
00099 static int create_connection(TCPsocket sock, const std::string& host, int port)
00100 {
00101     connections.insert(std::pair<network::connection,connection_details>(connection_id,connection_details(sock,host,port)));
00102     return connection_id++;
00103 }
00104 
00105 static connection_details& get_connection_details(network::connection handle)
00106 {
00107     const connection_map::iterator i = connections.find(handle);
00108     if(i == connections.end()) {
00109         throw network::error(_("invalid network handle"));
00110     }
00111 
00112     return i->second;
00113 }
00114 
00115 static TCPsocket get_socket(network::connection handle)
00116 {
00117     return get_connection_details(handle).sock;
00118 }
00119 
00120 static void remove_connection(network::connection handle)
00121 {
00122     connections.erase(handle);
00123 }
00124 
00125 static bool is_pending_remote_handle(network::connection handle)
00126 {
00127     const connection_details& details = get_connection_details(handle);
00128     return details.host != "" && details.remote_handle == 0;
00129 }
00130 
00131 static void set_remote_handle(network::connection handle, int remote_handle)
00132 {
00133     get_connection_details(handle).remote_handle = remote_handle;
00134 }
00135 
00136 static void check_error()
00137 {
00138     const TCPsocket sock = network_worker_pool::detect_error();
00139     if(sock) {
00140         for(connection_map::const_iterator i = connections.begin(); i != connections.end(); ++i) {
00141             if(i->second.sock == sock) {
00142                 throw network::error(_("Client disconnected"),i->first);
00143             }
00144         }
00145     }
00146 }
00147 
00148 /**
00149  * Check whether too much time since the last server ping has passed and we
00150  * timed out. If the last check is too long ago reset the last_ping to 'now'.
00151  * This happens when we "freeze" the client one way or another or we just
00152  * didn't try to receive data.
00153  */
00154 static void check_timeout()
00155 {
00156     if (network::nconnections() == 0) {
00157         LOG_NW << "No network connections but last_ping is: " << last_ping;
00158         last_ping = 0;
00159         return;
00160     }
00161     const time_t& now = time(NULL);
00162     DBG_NW << "Last ping: '" << last_ping << "' Current time: '" << now
00163             << "' Time since last ping: " << now - last_ping << "s\n";
00164     // Reset last_ping if we didn't check for the last 10s.
00165     if (last_ping_check + 10 <= now) last_ping = now;
00166     if (static_cast<time_t>(last_ping + network::ping_interval + network::ping_timeout) <= now) {
00167 
00168         time_t timeout = now - last_ping;
00169         ERR_NW << "No server ping since " << timeout
00170                 << " seconds. Connection timed out.\n";
00171         utils::string_map symbols;
00172         symbols["timeout"] = lexical_cast<std::string>(timeout);
00173         throw network::error(std::string("No server ping since " + lexical_cast<std::string>(timeout) + " second. "
00174                 "Connection timed out."));
00175     }
00176     last_ping_check = now;
00177 }
00178 
00179 
00180 namespace {
00181 
00182 SDLNet_SocketSet socket_set = 0;
00183 std::set<network::connection> waiting_sockets;
00184 typedef std::vector<network::connection> sockets_list;
00185 sockets_list sockets;
00186 
00187 
00188 struct partial_buffer {
00189     partial_buffer() :
00190         buf(),
00191         upto(0)
00192     {
00193     }
00194 
00195     std::vector<char> buf;
00196     size_t upto;
00197 };
00198 
00199 TCPsocket server_socket;
00200 
00201 std::deque<network::connection> disconnection_queue;
00202 std::set<network::connection> bad_sockets;
00203 
00204 network_worker_pool::manager* worker_pool_man = NULL;
00205 
00206 } // end anon namespace
00207 
00208 namespace network {
00209 
00210 /**
00211  * Amount of seconds after the last server ping when we assume to have timed out.
00212  * When set to '0' ping timeout isn't checked.
00213  * Gets set in preferences::manager according to the preferences file.
00214  */
00215 unsigned int ping_timeout = 0;
00216 
00217 connection_stats::connection_stats(int sent, int received, int connected_at)
00218        : bytes_sent(sent), bytes_received(received), time_connected(SDL_GetTicks() - connected_at)
00219 {}
00220 
00221 connection_stats get_connection_stats(connection connection_num)
00222 {
00223     connection_details& details = get_connection_details(connection_num);
00224     return connection_stats(get_send_stats(connection_num).total,get_receive_stats(connection_num).total,details.connected_at);
00225 }
00226 
00227 error::error(const std::string& msg, connection sock) : game::error(msg), socket(sock)
00228 {
00229     if(socket) {
00230         bad_sockets.insert(socket);
00231     }
00232 }
00233 
00234 void error::disconnect()
00235 {
00236     if(socket) network::disconnect(socket);
00237 }
00238 
00239 pending_statistics get_pending_stats()
00240 {
00241     return network_worker_pool::get_pending_stats();
00242 }
00243 
00244 manager::manager(size_t min_threads, size_t max_threads) : free_(true)
00245 {
00246     DBG_NW << "NETWORK MANAGER CALLED!\n";
00247     // If the network is already being managed
00248     if(socket_set) {
00249         free_ = false;
00250         return;
00251     }
00252 
00253     if(SDLNet_Init() == -1) {
00254         ERR_NW << "could not initialize SDLNet; throwing error...\n";
00255         throw error(SDL_GetError());
00256     }
00257 
00258     socket_set = SDLNet_AllocSocketSet(512);
00259 
00260     worker_pool_man = new network_worker_pool::manager(min_threads, max_threads);
00261 }
00262 
00263 manager::~manager()
00264 {
00265     if(free_) {
00266         disconnect();
00267         delete worker_pool_man;
00268         worker_pool_man = NULL;
00269         SDLNet_FreeSocketSet(socket_set);
00270         socket_set = 0;
00271         waiting_sockets.clear();
00272         SDLNet_Quit();
00273     }
00274 }
00275 
00276 void set_raw_data_only()
00277 {
00278     network_worker_pool::set_raw_data_only();
00279 }
00280 
00281 // --- Proxy methods
00282 void enable_connection_through_proxy()
00283 {
00284     throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
00285 }
00286 void set_proxy_address ( const std::string& )
00287 {
00288     throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
00289 }
00290 
00291 void set_proxy_port    ( const std::string& )
00292 {
00293     throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
00294 }
00295 
00296 void set_proxy_user    ( const std::string& )
00297 {
00298     throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
00299 }
00300 
00301 void set_proxy_password( const std::string& )
00302 {
00303     throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
00304 }
00305 // --- End Proxy methods
00306 
00307 server_manager::server_manager(int port, CREATE_SERVER create_server) : free_(false), connection_(0)
00308 {
00309     if(create_server != NO_SERVER && !server_socket) {
00310         try {
00311             connection_ = connect("",port);
00312             server_socket = get_socket(connection_);
00313         } catch(network::error&) {
00314             if(create_server == MUST_CREATE_SERVER) {
00315                 throw;
00316             } else {
00317                 return;
00318             }
00319         }
00320 
00321         DBG_NW << "server socket initialized: " << server_socket << "\n";
00322         free_ = true;
00323     }
00324 }
00325 
00326 server_manager::~server_manager()
00327 {
00328     stop();
00329 }
00330 
00331 void server_manager::stop()
00332 {
00333     if(free_) {
00334         SDLNet_TCP_Close(server_socket);
00335         remove_connection(connection_);
00336         server_socket = 0;
00337         free_ = false;
00338     }
00339 }
00340 
00341 bool server_manager::is_running() const
00342 {
00343     return server_socket != NULL;
00344 }
00345 
00346 size_t nconnections()
00347 {
00348     return sockets.size();
00349 }
00350 
00351 bool is_server()
00352 {
00353     return server_socket != 0;
00354 }
00355 
00356 namespace {
00357 
00358 class connect_operation : public threading::async_operation
00359 {
00360 public:
00361     connect_operation(const std::string& host, int port) : host_(host), port_(port), error_(), connect_(0)
00362     {}
00363 
00364     void check_error();
00365     void run();
00366 
00367     network::connection result() const { return connect_; }
00368 
00369 private:
00370     std::string host_;
00371     int port_;
00372     std::string error_;
00373     network::connection connect_;
00374 };
00375 
00376 void connect_operation::check_error()
00377 {
00378     if(!error_.empty()) {
00379         throw error(error_);
00380     }
00381 }
00382 
00383 namespace {
00384     struct _TCPsocket {
00385         int ready;
00386         SOCKET channel;
00387         IPaddress remoteAddress;
00388         IPaddress localAddress;
00389         int sflag;
00390     };
00391 } // end namespace
00392 
00393 void connect_operation::run()
00394 {
00395     char* const hostname = host_.empty() ? NULL : const_cast<char*>(host_.c_str());
00396     IPaddress ip;
00397     if(SDLNet_ResolveHost(&ip,hostname,port_) == -1) {
00398         error_ = N_("Could not connect to host.");
00399         return;
00400     }
00401 
00402     TCPsocket sock = SDLNet_TCP_Open(&ip);
00403     if(!sock) {
00404         error_ = hostname == NULL
00405                 ? "Could not bind to port"
00406                 : N_("Could not connect to host.");
00407         return;
00408     }
00409     _TCPsocket* raw_sock = reinterpret_cast<_TCPsocket*>(sock);
00410 #ifdef TCP_NODELAY
00411     //set TCP_NODELAY to 0 because SDL_Net turns it off, causing packet
00412     //flooding!
00413     {
00414         int no = 0;
00415         setsockopt(raw_sock->channel, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&no), sizeof(no));
00416     }
00417 #endif
00418 
00419 // Use non blocking IO
00420 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
00421     {
00422         unsigned long mode = 1;
00423         ioctlsocket (raw_sock->channel, FIONBIO, &mode);
00424     }
00425 #elif !defined(__BEOS__)
00426     int flags;
00427     flags = fcntl(raw_sock->channel, F_GETFL, 0);
00428 #if defined(O_NONBLOCK)
00429     flags |= O_NONBLOCK;
00430 #elif defined(O_NDELAY)
00431     flags |= O_NDELAY;
00432 #elif defined(FNDELAY)
00433     flags |= FNDELAY;
00434 #endif
00435     if (fcntl(raw_sock->channel, F_SETFL, flags) == -1) {
00436         error_ = "Could not make socket non-blocking: " + std::string(strerror(errno));
00437         SDLNet_TCP_Close(sock);
00438         return;
00439     }
00440 #else
00441     int on = 1;
00442     if (setsockopt(raw_sock->channel, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0) {
00443         error_ = "Could not make socket non-blocking: " + std::string(strerror(errno));
00444         SDLNet_TCP_Close(sock);
00445         return;
00446     }
00447 
00448     int fd_flags = fcntl(raw_sock->channel, F_GETFD, 0);
00449     fd_flags |= FD_CLOEXEC;
00450     if (fcntl(raw_sock->channel, F_SETFD, fd_flags) == -1) {
00451         WRN_NW << "could not make socket " << sock << " close-on-exec: " << strerror(errno);
00452     } else {
00453         DBG_NW << "made socket " << sock << " close-on-exec\n";
00454     }
00455 #endif
00456 
00457     // If this is a server socket
00458     if(hostname == NULL) {
00459         const threading::lock l(get_mutex());
00460         connect_ = create_connection(sock,"",port_);
00461         return;
00462     }
00463 
00464     // Send data telling the remote host that this is a new connection
00465     union
00466     {
00467     char data[4] ALIGN_4;
00468     Uint32 num;
00469     } buf;
00470     SDLNet_Write32(0, &buf);
00471     const int nbytes = SDLNet_TCP_Send(sock,&buf,4);
00472     if(nbytes != 4) {
00473         SDLNet_TCP_Close(sock);
00474         error_ = "Could not send initial handshake";
00475         return;
00476     }
00477 
00478     // No blocking operations from here on
00479     const threading::lock l(get_mutex());
00480     DBG_NW << "sent handshake...\n";
00481 
00482     if(is_aborted()) {
00483         DBG_NW << "connect operation aborted by calling thread\n";
00484         SDLNet_TCP_Close(sock);
00485         return;
00486     }
00487 
00488     // Allocate this connection a connection handle
00489     connect_ = create_connection(sock,host_,port_);
00490 
00491     const int res = SDLNet_TCP_AddSocket(socket_set,sock);
00492     if(res == -1) {
00493         SDLNet_TCP_Close(sock);
00494         error_ = "Could not add socket to socket set";
00495         return;
00496     }
00497 
00498     waiting_sockets.insert(connect_);
00499 
00500     sockets.push_back(connect_);
00501 
00502     while(!notify_finished()) {};
00503 }
00504 
00505 
00506 } // end namespace
00507 
00508 
00509 connection connect(const std::string& host, int port)
00510 {
00511     connect_operation op(host,port);
00512     op.run();
00513     op.check_error();
00514     return op.result();
00515 }
00516 
00517 connection connect(const std::string& host, int port, threading::waiter& waiter)
00518 {
00519     const threading::async_operation_ptr op(new connect_operation(host,port));
00520     const connect_operation::RESULT res = op->execute(op, waiter);
00521     if(res == connect_operation::ABORTED) {
00522         return 0;
00523     }
00524 
00525     static_cast<connect_operation*>(op.get())->check_error();
00526     return static_cast<connect_operation*>(op.get())->result();
00527 }
00528 
00529 namespace {
00530 
00531 connection accept_connection_pending(std::vector<TCPsocket>& pending_sockets,
00532     SDLNet_SocketSet& pending_socket_set)
00533 {
00534     DBG_NW << "pending socket activity...\n";
00535 
00536     std::vector<TCPsocket>::iterator i = pending_sockets.begin();
00537     while (i != pending_sockets.end() && !SDLNet_SocketReady(*i)) ++i;
00538 
00539     if (i == pending_sockets.end()) return 0;
00540 
00541     // Receive the 4 bytes telling us if they're a new connection
00542     // or trying to recover a connection
00543     union
00544     {
00545     char data[4] ALIGN_4;
00546     Uint32 num;
00547     } buf;
00548 
00549     const TCPsocket psock = *i;
00550     SDLNet_TCP_DelSocket(pending_socket_set,psock);
00551     pending_sockets.erase(i);
00552 
00553     DBG_NW << "receiving data from pending socket...\n";
00554 
00555     const int len = SDLNet_TCP_Recv(psock,&buf,4);
00556     if(len != 4) {
00557         WRN_NW << "pending socket disconnected\n";
00558         SDLNet_TCP_Close(psock);
00559         return 0;
00560     }
00561 
00562     const int handle = SDLNet_Read32(&buf);
00563 
00564     DBG_NW << "received handshake from client: '" << handle << "'\n";
00565 
00566     const int res = SDLNet_TCP_AddSocket(socket_set,psock);
00567     if(res == -1) {
00568         ERR_NW << "SDLNet_GetError() is " << SDLNet_GetError() << "\n";
00569         SDLNet_TCP_Close(psock);
00570 
00571         throw network::error(_("Could not add socket to socket set"));
00572     }
00573 
00574     const connection connect = create_connection(psock,"",0);
00575 
00576     // Send back their connection number
00577     SDLNet_Write32(connect, &buf);
00578     const int nbytes = SDLNet_TCP_Send(psock,&buf,4);
00579     if(nbytes != 4) {
00580         SDLNet_TCP_DelSocket(socket_set,psock);
00581         SDLNet_TCP_Close(psock);
00582         remove_connection(connect);
00583         throw network::error(_("Could not send initial handshake"));
00584     }
00585 
00586     waiting_sockets.insert(connect);
00587     sockets.push_back(connect);
00588     return connect;
00589 }
00590 
00591 } //anon namespace
00592 
00593 connection accept_connection()
00594 {
00595     if(!server_socket) {
00596         return 0;
00597     }
00598 
00599     // A connection isn't considered 'accepted' until it has sent its initial handshake.
00600     // The initial handshake is a 4 byte value, which is 0 for a new connection,
00601     // or the handle of the connection if it's trying to recover a lost connection.
00602 
00603     /**
00604      * A list of all the sockets which have connected,
00605      * but haven't had their initial handshake received.
00606      */
00607     static std::vector<TCPsocket> pending_sockets;
00608     static SDLNet_SocketSet pending_socket_set = 0;
00609 
00610     const TCPsocket sock = SDLNet_TCP_Accept(server_socket);
00611     if(sock) {
00612 #if !defined(_WIN32) && !defined(__WIN32__) && !defined (WIN32)
00613         _TCPsocket* raw_sock = reinterpret_cast<_TCPsocket*>(sock);
00614         int fd_flags = fcntl(raw_sock->channel, F_GETFD, 0);
00615         fd_flags |= FD_CLOEXEC;
00616         if (fcntl(raw_sock->channel, F_SETFD, fd_flags) == -1) {
00617             WRN_NW << "could not make socket " << sock << " close-on-exec: " << strerror(errno);
00618         } else {
00619             DBG_NW << "made socket " << sock << " close-on-exec\n";
00620         }
00621 #endif
00622 
00623         DBG_NW << "received connection. Pending handshake...\n";
00624 
00625         if(pending_socket_set == 0) {
00626             pending_socket_set = SDLNet_AllocSocketSet(32);
00627         }
00628 
00629         if(pending_socket_set != 0) {
00630             int res = SDLNet_TCP_AddSocket(pending_socket_set,sock);
00631 
00632             if (res != -1) {
00633                 pending_sockets.push_back(sock);
00634             } else {
00635                 ERR_NW << "Pending socket set is full! Disconnecting " << sock << " connection\n";
00636                 ERR_NW << "SDLNet_GetError() is " << SDLNet_GetError() << "\n";
00637 
00638                 SDLNet_TCP_Close(sock);
00639             }
00640         } else {
00641             ERR_NW << "Error in SDLNet_AllocSocketSet\n";
00642         }
00643     }
00644 
00645     if(pending_socket_set == 0) {
00646         return 0;
00647     }
00648 
00649     const int set_res = SDLNet_CheckSockets(pending_socket_set,0);
00650     if(set_res <= 0) {
00651         return 0;
00652     }
00653 
00654     return accept_connection_pending(pending_sockets, pending_socket_set);
00655 }
00656 
00657 bool disconnect(connection s)
00658 {
00659     if(s == 0) {
00660         while(sockets.empty() == false) {
00661             assert(sockets.back() != 0);
00662             while(disconnect(sockets.back()) == false) {
00663                 SDL_Delay(1);
00664             }
00665         }
00666         return true;
00667     }
00668     if (!is_server()) last_ping = 0;
00669 
00670     const connection_map::iterator info = connections.find(s);
00671     if(info != connections.end()) {
00672         if (info->second.sock == server_socket)
00673         {
00674             return true;
00675         }
00676         if (!network_worker_pool::close_socket(info->second.sock)) {
00677             return false;
00678         }
00679     }
00680 
00681     bad_sockets.erase(s);
00682 
00683     std::deque<network::connection>::iterator dqi = std::find(disconnection_queue.begin(),disconnection_queue.end(),s);
00684     if(dqi != disconnection_queue.end()) {
00685         disconnection_queue.erase(dqi);
00686     }
00687 
00688     const sockets_list::iterator i = std::find(sockets.begin(),sockets.end(),s);
00689     if(i != sockets.end()) {
00690         sockets.erase(i);
00691 
00692         const TCPsocket sock = get_socket(s);
00693 
00694         waiting_sockets.erase(s);
00695         SDLNet_TCP_DelSocket(socket_set,sock);
00696         SDLNet_TCP_Close(sock);
00697 
00698         remove_connection(s);
00699     } else {
00700         if(sockets.size() == 1) {
00701             DBG_NW << "valid socket: " << static_cast<int>(*sockets.begin()) << "\n";
00702         }
00703     }
00704 
00705     return true;
00706 }
00707 
00708 void queue_disconnect(network::connection sock)
00709 {
00710     disconnection_queue.push_back(sock);
00711 }
00712 
00713 connection receive_data(config& cfg, connection connection_num, unsigned int timeout, bandwidth_in_ptr* bandwidth_in)
00714 {
00715     unsigned int start_ticks = SDL_GetTicks();
00716     while(true) {
00717         const connection res = receive_data(
00718                 cfg,connection_num, bandwidth_in);
00719         if(res != 0) {
00720             return res;
00721         }
00722 
00723         if(timeout > SDL_GetTicks() - start_ticks) {
00724             SDL_Delay(1);
00725         }
00726         else
00727         {
00728             break;
00729         }
00730 
00731     }
00732 
00733     return 0;
00734 }
00735 
00736 connection receive_data(config& cfg, connection connection_num, bandwidth_in_ptr* bandwidth_in)
00737 {
00738     if(!socket_set) {
00739         return 0;
00740     }
00741 
00742     check_error();
00743 
00744     if(disconnection_queue.empty() == false) {
00745         const network::connection sock = disconnection_queue.front();
00746         disconnection_queue.pop_front();
00747         throw error("",sock);
00748     }
00749 
00750     if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
00751         return 0;
00752     }
00753 
00754     if(sockets.empty()) {
00755         return 0;
00756     }
00757 
00758     const int res = SDLNet_CheckSockets(socket_set,0);
00759 
00760     for(std::set<network::connection>::iterator i = waiting_sockets.begin(); res != 0 && i != waiting_sockets.end(); ) {
00761         connection_details& details = get_connection_details(*i);
00762         const TCPsocket sock = details.sock;
00763         if(SDLNet_SocketReady(sock)) {
00764 
00765             // See if this socket is still waiting for it to be assigned its remote handle.
00766             // If it is, then the first 4 bytes must be the remote handle.
00767             if(is_pending_remote_handle(*i)) {
00768                 union {
00769                 char data[4] ALIGN_4;
00770                 } buf;
00771                 int len = SDLNet_TCP_Recv(sock,&buf,4);
00772                 if(len != 4) {
00773                     throw error("Remote host disconnected",*i);
00774                 }
00775 
00776                 const int remote_handle = SDLNet_Read32(&buf);
00777                 set_remote_handle(*i,remote_handle);
00778 
00779                 continue;
00780             }
00781 
00782             waiting_sockets.erase(i++);
00783             SDLNet_TCP_DelSocket(socket_set,sock);
00784             network_worker_pool::receive_data(sock);
00785         } else {
00786             ++i;
00787         }
00788     }
00789 
00790 
00791     TCPsocket sock = connection_num == 0 ? 0 : get_socket(connection_num);
00792     TCPsocket s = sock;
00793     bandwidth_in_ptr temp;
00794     if (!bandwidth_in)
00795     {
00796         bandwidth_in = &temp;
00797     }
00798     sock = network_worker_pool::get_received_data(sock,cfg, *bandwidth_in);
00799     if (sock == NULL) {
00800         if (!is_server() && last_ping != 0 && ping_timeout != 0)
00801         {
00802             if (connection_num == 0)
00803             {
00804                 s = get_socket(sockets.back());
00805             }
00806             if (!network_worker_pool::is_locked(s))
00807             {
00808                 check_timeout();
00809             }
00810         }
00811         return 0;
00812     }
00813 
00814     int set_res = SDLNet_TCP_AddSocket(socket_set,sock);
00815     if (set_res == -1)
00816     {
00817         ERR_NW << "Socket set is full! Disconnecting " << sock << " connection\n";
00818         SDLNet_TCP_Close(sock);
00819         return 0;
00820     }
00821 
00822     connection result = 0;
00823     for(connection_map::const_iterator j = connections.begin(); j != connections.end(); ++j) {
00824         if(j->second.sock == sock) {
00825             result = j->first;
00826             break;
00827         }
00828     }
00829     if(!cfg.empty()) {
00830         DBG_NW << "RECEIVED from: " << result << ": " << cfg;
00831     }
00832 
00833     assert(result != 0);
00834     waiting_sockets.insert(result);
00835     if(!is_server()) {
00836         const time_t& now = time(NULL);
00837         if (cfg.has_attribute("ping")) {
00838             LOG_NW << "Lag: " << (now - lexical_cast<time_t>(cfg["ping"])) << "\n";
00839             last_ping = now;
00840         } else if (last_ping != 0) {
00841             last_ping = now;
00842         }
00843     }
00844     return result;
00845 }
00846 
00847 connection receive_data(std::vector<char>& buf, bandwidth_in_ptr* bandwidth_in)
00848 {
00849     if(!socket_set) {
00850         return 0;
00851     }
00852 
00853     check_error();
00854 
00855     if(disconnection_queue.empty() == false) {
00856         const network::connection sock = disconnection_queue.front();
00857         disconnection_queue.pop_front();
00858         throw error("",sock);
00859     }
00860 
00861     if(bad_sockets.count(0)) {
00862         return 0;
00863     }
00864 
00865     if(sockets.empty()) {
00866         return 0;
00867     }
00868 
00869     const int res = SDLNet_CheckSockets(socket_set,0);
00870 
00871     for(std::set<network::connection>::iterator i = waiting_sockets.begin(); res != 0 && i != waiting_sockets.end(); ) {
00872         connection_details& details = get_connection_details(*i);
00873         const TCPsocket sock = details.sock;
00874         if(SDLNet_SocketReady(sock)) {
00875 
00876             // See if this socket is still waiting for it to be assigned its remote handle.
00877             // If it is, then the first 4 bytes must be the remote handle.
00878             if(is_pending_remote_handle(*i)) {
00879                 union {
00880                 char data[4] ALIGN_4;
00881                 } buf;
00882                 int len = SDLNet_TCP_Recv(sock,&buf,4);
00883                 if(len != 4) {
00884                     throw error("Remote host disconnected",*i);
00885                 }
00886 
00887                 const int remote_handle = SDLNet_Read32(&buf);
00888                 set_remote_handle(*i,remote_handle);
00889 
00890                 continue;
00891             }
00892 
00893             waiting_sockets.erase(i++);
00894             SDLNet_TCP_DelSocket(socket_set,sock);
00895             network_worker_pool::receive_data(sock);
00896         } else {
00897             ++i;
00898         }
00899     }
00900 
00901 
00902     TCPsocket sock = network_worker_pool::get_received_data(buf);
00903     if (sock == NULL) {
00904         return 0;
00905     }
00906 
00907     {
00908         bandwidth_in_ptr temp;
00909         if (!bandwidth_in)
00910         {
00911             bandwidth_in = &temp;
00912         }
00913         const int headers = 4;
00914         bandwidth_in->reset(new network::bandwidth_in(buf.size() + headers));
00915     }
00916 
00917     int set_res = SDLNet_TCP_AddSocket(socket_set,sock);
00918 
00919     if (set_res == -1)
00920     {
00921         ERR_NW << "Socket set is full! Disconnecting " << sock << " connection\n";
00922         SDLNet_TCP_Close(sock);
00923         return 0;
00924     }
00925     connection result = 0;
00926     for(connection_map::const_iterator j = connections.begin(); j != connections.end(); ++j) {
00927         if(j->second.sock == sock) {
00928             result = j->first;
00929             break;
00930         }
00931     }
00932 
00933     assert(result != 0);
00934     waiting_sockets.insert(result);
00935     return result;
00936 }
00937 struct bandwidth_stats {
00938     int out_packets;
00939     int out_bytes;
00940     int in_packets;
00941     int in_bytes;
00942     int day;
00943     const static size_t type_width = 16;
00944     const static size_t packet_width = 7;
00945     const static size_t bytes_width = 10;
00946     bandwidth_stats& operator+=(const bandwidth_stats& a)
00947     {
00948         out_packets += a.out_packets;
00949         out_bytes += a.out_bytes;
00950         in_packets += a.in_packets;
00951         in_bytes += a.in_bytes;
00952 
00953         return *this;
00954     }
00955 };
00956 typedef std::map<const std::string, bandwidth_stats> bandwidth_map;
00957 typedef std::vector<bandwidth_map> hour_stats_vector;
00958 hour_stats_vector hour_stats(24);
00959 
00960 
00961 
00962 static bandwidth_map::iterator add_bandwidth_entry(const std::string& packet_type)
00963 {
00964     time_t now = time(0);
00965     struct tm * timeinfo = localtime(&now);
00966     int hour = timeinfo->tm_hour;
00967     int day = timeinfo->tm_mday;
00968     assert(hour < 24 && hour >= 0);
00969     std::pair<bandwidth_map::iterator,bool> insertion = hour_stats[hour].insert(std::make_pair(packet_type, bandwidth_stats()));
00970     bandwidth_map::iterator inserted = insertion.first;
00971     if (!insertion.second && day != inserted->second.day)
00972     {
00973         // clear previuos day stats
00974         hour_stats[hour].clear();
00975         //insert again to cleared map
00976         insertion = hour_stats[hour].insert(std::make_pair(packet_type, bandwidth_stats()));
00977         inserted = insertion.first;
00978     }
00979 
00980     inserted->second.day = day;
00981     return inserted;
00982 }
00983 
00984 typedef boost::shared_ptr<bandwidth_stats> bandwidth_stats_ptr;
00985 
00986 
00987 struct bandwidth_stats_output {
00988     bandwidth_stats_output(std::stringstream& ss) : ss_(ss), totals_(new bandwidth_stats())
00989     {}
00990     void operator()(const bandwidth_map::value_type& stats)
00991     {
00992         // name
00993         ss_ << " " << std::setw(bandwidth_stats::type_width) <<  stats.first << "| "
00994             << std::setw(bandwidth_stats::packet_width)<< stats.second.out_packets << "| "
00995             << std::setw(bandwidth_stats::bytes_width) << stats.second.out_bytes/1024 << "| "
00996             << std::setw(bandwidth_stats::packet_width)<< stats.second.in_packets << "| "
00997             << std::setw(bandwidth_stats::bytes_width) << stats.second.in_bytes/1024 << "\n";
00998         *totals_ += stats.second;
00999     }
01000     void output_totals()
01001     {
01002         (*this)(std::make_pair(std::string("total"), *totals_));
01003     }
01004     private:
01005     std::stringstream& ss_;
01006     bandwidth_stats_ptr totals_;
01007 };
01008 
01009 std::string get_bandwidth_stats_all()
01010 {
01011     std::string result;
01012     for (int hour = 0; hour < 24; ++hour)
01013     {
01014         result += get_bandwidth_stats(hour);
01015     }
01016     return result;
01017 }
01018 
01019 std::string get_bandwidth_stats()
01020 {
01021     time_t now = time(0);
01022     struct tm * timeinfo = localtime(&now);
01023     int hour = timeinfo->tm_hour - 1;
01024     if (hour < 0)
01025         hour = 23;
01026     return get_bandwidth_stats(hour);
01027 }
01028 
01029 std::string get_bandwidth_stats(int hour)
01030 {
01031     assert(hour < 24 && hour >= 0);
01032     std::stringstream ss;
01033 
01034     ss << "Hour stat starting from " << hour << "\n " << std::left << std::setw(bandwidth_stats::type_width) <<  "Type of packet" << "| "
01035         << std::setw(bandwidth_stats::packet_width)<< "out #"  << "| "
01036         << std::setw(bandwidth_stats::bytes_width) << "out KiB" << "| "
01037         << std::setw(bandwidth_stats::packet_width)<< "in #"  << "| "
01038         << std::setw(bandwidth_stats::bytes_width) << "in KiB" << "\n";
01039 
01040     bandwidth_stats_output outputer(ss);
01041     std::for_each(hour_stats[hour].begin(), hour_stats[hour].end(), outputer);
01042 
01043     outputer.output_totals();
01044     return ss.str();
01045 }
01046 
01047 void add_bandwidth_out(const std::string& packet_type, size_t len)
01048 {
01049     bandwidth_map::iterator itor = add_bandwidth_entry(packet_type);
01050     itor->second.out_bytes += len;
01051     ++(itor->second.out_packets);
01052 }
01053 
01054 void add_bandwidth_in(const std::string& packet_type, size_t len)
01055 {
01056     bandwidth_map::iterator itor = add_bandwidth_entry(packet_type);
01057     itor->second.in_bytes += len;
01058     ++(itor->second.in_packets);
01059 }
01060 
01061     bandwidth_in::~bandwidth_in()
01062     {
01063         add_bandwidth_in(type_, len_);
01064     }
01065 
01066 void send_file(const std::string& filename, connection connection_num, const std::string& packet_type)
01067 {
01068     assert(connection_num > 0);
01069     if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
01070         return;
01071     }
01072 
01073     const connection_map::iterator info = connections.find(connection_num);
01074     if (info == connections.end()) {
01075         ERR_NW << "Error: socket: " << connection_num
01076             << "\tnot found in connection_map. Not sending...\n";
01077         return;
01078     }
01079 
01080     const int packet_headers = 4;
01081     add_bandwidth_out(packet_type, file_size(filename) + packet_headers);
01082     network_worker_pool::queue_file(info->second.sock, filename);
01083 
01084 }
01085 
01086 size_t send_data(const config& cfg, connection connection_num, const std::string& packet_type)
01087 {
01088     DBG_NW << "in send_data()...\n";
01089 
01090     if(cfg.empty()) {
01091         return 0;
01092     }
01093 
01094     if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
01095         return 0;
01096     }
01097 
01098 //  log_scope2(network, "sending data");
01099     if(!connection_num) {
01100         DBG_NW << "sockets: " << sockets.size() << "\n";
01101         size_t size = 0;
01102         for(sockets_list::const_iterator i = sockets.begin();
01103             i != sockets.end(); ++i) {
01104             DBG_NW << "server socket: " << server_socket << "\ncurrent socket: " << *i << "\n";
01105             size = send_data(cfg,*i, packet_type);
01106         }
01107         return size;
01108     }
01109 
01110     const connection_map::iterator info = connections.find(connection_num);
01111     if (info == connections.end()) {
01112         ERR_NW << "Error: socket: " << connection_num
01113             << "\tnot found in connection_map. Not sending...\n";
01114         return 0;
01115     }
01116 
01117     LOG_NW << "SENDING to: " << connection_num << ": " << cfg;
01118     return network_worker_pool::queue_data(info->second.sock, cfg, packet_type);
01119 }
01120 
01121 void send_raw_data(const char* buf, int len, connection connection_num, const std::string& packet_type)
01122 {
01123     if(len == 0) {
01124         return;
01125     }
01126 
01127     if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
01128         return;
01129     }
01130 
01131     if(!connection_num) {
01132         for(sockets_list::const_iterator i = sockets.begin();
01133             i != sockets.end(); ++i) {
01134             send_raw_data(buf, len, *i, packet_type);
01135         }
01136         return;
01137     }
01138 
01139     const connection_map::iterator info = connections.find(connection_num);
01140     if (info == connections.end()) {
01141         ERR_NW << "Error: socket: " << connection_num
01142             << "\tnot found in connection_map. Not sending...\n";
01143         return;
01144     }
01145     const int packet_headers = 4;
01146     add_bandwidth_out(packet_type, len + packet_headers);
01147 
01148     network_worker_pool::queue_raw_data(info->second.sock, buf, len);
01149 }
01150 
01151 void process_send_queue(connection, size_t)
01152 {
01153     check_error();
01154 }
01155 
01156 void send_data_all_except(const config& cfg, connection connection_num, const std::string& packet_type)
01157 {
01158     for(sockets_list::const_iterator i = sockets.begin(); i != sockets.end(); ++i) {
01159         if(*i == connection_num) {
01160             continue;
01161         }
01162 
01163         send_data(cfg,*i, packet_type);
01164     }
01165 }
01166 
01167 std::string ip_address(connection connection_num)
01168 {
01169     std::stringstream str;
01170     const IPaddress* const ip = SDLNet_TCP_GetPeerAddress(get_socket(connection_num));
01171     if(ip != NULL) {
01172         const unsigned char* buf = reinterpret_cast<const unsigned char*>(&ip->host);
01173         for(int i = 0; i != sizeof(ip->host); ++i) {
01174             str << int(buf[i]);
01175             if(i+1 != sizeof(ip->host)) {
01176                 str << '.';
01177             }
01178         }
01179 
01180     }
01181 
01182     return str.str();
01183 }
01184 
01185 statistics get_send_stats(connection handle)
01186 {
01187     return network_worker_pool::get_current_transfer_stats(handle == 0 ? get_socket(sockets.back()) : get_socket(handle)).first;
01188 }
01189 statistics get_receive_stats(connection handle)
01190 {
01191     const statistics result = network_worker_pool::get_current_transfer_stats(handle == 0 ? get_socket(sockets.back()) : get_socket(handle)).second;
01192 
01193     return result;
01194 }
01195 
01196 } // end namespace network
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:07 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs