network.cpp

Go to the documentation of this file.
00001 /* $Id: network.cpp 26456 2008-05-08 12:02:24Z suokko $ */
00002 /*
00003    Copyright (C) 2003 - 2008 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 version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file network.cpp
00016 //! Networking
00017 
00018 #include "global.hpp"
00019 
00020 #include "serialization/binary_wml.hpp"
00021 #include "config.hpp"
00022 #include "gettext.hpp"
00023 #include "log.hpp"
00024 #include "network.hpp"
00025 #include "network_worker.hpp"
00026 #include "thread.hpp"
00027 
00028 #include "SDL_net.h"
00029 
00030 #include <algorithm>
00031 #include <cassert>
00032 #include <cerrno>
00033 #include <queue>
00034 #include <iostream>
00035 #include <set>
00036 #include <vector>
00037 #include <ctime>
00038 
00039 #include <signal.h>
00040 #include <string.h>
00041 #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
00042 #undef INADDR_ANY
00043 #undef INADDR_BROADCAST
00044 #undef INADDR_NONE
00045 #include <windows.h>
00046 #else
00047 #include <sys/types.h>
00048 #include <sys/socket.h>
00049 #include <netinet/in.h>
00050 #include <netinet/tcp.h>  // for TCP_NODELAY
00051 #ifdef __BEOS__
00052 #include <socket.h>
00053 #else
00054 #include <fcntl.h>
00055 #endif
00056 #define SOCKET int
00057 #endif
00058 
00059 #define DBG_NW LOG_STREAM(debug, network)
00060 #define LOG_NW LOG_STREAM(info, network)
00061 #define WRN_NW LOG_STREAM(warn, network)
00062 #define ERR_NW LOG_STREAM(err, network)
00063 // Only warnings and not errors to avoid DoS by log flooding
00064 
00065 namespace {
00066 
00067 // We store the details of a connection in a map that must be looked up by its handle.
00068 // This allows a connection to be disconnected and then recovered,
00069 // but the handle remains the same, so it's all seamless to the user.
00070 struct connection_details {
00071     connection_details(TCPsocket sock, const std::string& host, int port)
00072         : sock(sock), host(host), port(port), remote_handle(0),
00073           connected_at(SDL_GetTicks())
00074     {}
00075 
00076     TCPsocket sock;
00077     std::string host;
00078     int port;
00079 
00080     // The remote handle is the handle assigned to this connection by the remote host.
00081     // Is 0 before a handle has been assigned.
00082     int remote_handle;
00083 
00084     int connected_at;
00085 };
00086 
00087 typedef std::map<network::connection,connection_details> connection_map;
00088 connection_map connections;
00089 
00090 network::connection connection_id = 1;
00091 
00092 //! Stores the time of the last server ping we received.
00093 time_t last_ping, last_ping_check = 0;
00094 
00095 } // end anon namespace
00096 
00097 static int create_connection(TCPsocket sock, const std::string& host, int port)
00098 {
00099     connections.insert(std::pair<network::connection,connection_details>(connection_id,connection_details(sock,host,port)));
00100     return connection_id++;
00101 }
00102 
00103 static connection_details& get_connection_details(network::connection handle)
00104 {
00105     const connection_map::iterator i = connections.find(handle);
00106     if(i == connections.end()) {
00107         throw network::error(_("invalid network handle"));
00108     }
00109 
00110     return i->second;
00111 }
00112 
00113 static TCPsocket get_socket(network::connection handle)
00114 {
00115     return get_connection_details(handle).sock;
00116 }
00117 
00118 static void remove_connection(network::connection handle)
00119 {
00120     connections.erase(handle);
00121 }
00122 
00123 static bool is_pending_remote_handle(network::connection handle)
00124 {
00125     const connection_details& details = get_connection_details(handle);
00126     return details.host != "" && details.remote_handle == 0;
00127 }
00128 
00129 static void set_remote_handle(network::connection handle, int remote_handle)
00130 {
00131     get_connection_details(handle).remote_handle = remote_handle;
00132 }
00133 
00134 static void check_error()
00135 {
00136     const TCPsocket sock = network_worker_pool::detect_error();
00137     if(sock) {
00138         for(connection_map::const_iterator i = connections.begin(); i != connections.end(); ++i) {
00139             if(i->second.sock == sock) {
00140                 throw network::error(_("Client disconnected"),i->first);
00141             }
00142         }
00143     }
00144 }
00145 
00146 //! Check whether too much time since the last server ping has passed and we
00147 //! timed out. If the last check is too long ago reset the last_ping to 'now'.
00148 //! This happens when we "freeze" the client one way or another or we just
00149 //! didn't try to receive data.
00150 static void check_timeout()
00151 {
00152     if (network::nconnections() == 0) {
00153         LOG_NW << "No network connections but last_ping is: " << last_ping;
00154         last_ping = 0;
00155         return;
00156     }
00157     const time_t& now = time(NULL);
00158     DBG_NW << "Last ping: '" << last_ping << "' Current time: '" << now
00159             << "' Time since last ping: " << now - last_ping << "s\n";
00160     // Reset last_ping if we didn't check for the last 10s.
00161     if (last_ping_check + 10 <= now) last_ping = now;
00162     if (static_cast<time_t>(last_ping + network::ping_timeout) <= now) {
00163 
00164         int timeout = now - last_ping;
00165         ERR_NW << "No server ping since " << timeout
00166                 << " seconds. Connection timed out.\n";
00167         utils::string_map symbols;
00168         symbols["timeout"] = lexical_cast<std::string>(timeout);
00169         throw network::error(vngettext("No server ping since $timeout second. "
00170                 "Connection timed out.", "No server ping since $timeout seconds. "
00171                 "Connection timed out.", timeout, symbols));
00172     }
00173     last_ping_check = now;
00174 }
00175 
00176 
00177 namespace {
00178 
00179 SDLNet_SocketSet socket_set = 0;
00180 std::set<network::connection> waiting_sockets;
00181 typedef std::vector<network::connection> sockets_list;
00182 sockets_list sockets;
00183 
00184 
00185 struct partial_buffer {
00186     partial_buffer() : upto(0) {}
00187     std::vector<char> buf;
00188     size_t upto;
00189 };
00190 
00191 TCPsocket server_socket;
00192 
00193 std::deque<network::connection> disconnection_queue;
00194 std::set<network::connection> bad_sockets;
00195 
00196 network_worker_pool::manager* worker_pool_man = NULL;
00197 
00198 } // end anon namespace
00199 
00200 namespace network {
00201 
00202 //! Amount of seconds after the last server ping when we assume to have timed out.
00203 //! When set to '0' ping timeout isn't checked.
00204 //! Gets set in preferences::manager according to the preferences file.
00205 unsigned int ping_timeout = 0;
00206 
00207 connection_stats::connection_stats(int sent, int received, int connected_at)
00208        : bytes_sent(sent), bytes_received(received), time_connected(SDL_GetTicks() - connected_at)
00209 {}
00210 
00211 connection_stats get_connection_stats(connection connection_num)
00212 {
00213     connection_details& details = get_connection_details(connection_num);
00214     return connection_stats(get_send_stats(connection_num).total,get_receive_stats(connection_num).total,details.connected_at);
00215 }
00216 
00217 error::error(const std::string& msg, connection sock) : message(msg), socket(sock)
00218 {
00219     if(socket) {
00220         bad_sockets.insert(socket);
00221     }
00222 }
00223 
00224 void error::disconnect()
00225 {
00226     if(socket) network::disconnect(socket);
00227 }
00228 
00229 pending_statistics get_pending_stats()
00230 {
00231     return network_worker_pool::get_pending_stats();
00232 }
00233 
00234 manager::manager(size_t min_threads, size_t max_threads) : free_(true)
00235 {
00236     // If the network is already being managed
00237     if(socket_set) {
00238         free_ = false;
00239         return;
00240     }
00241 
00242     // On Unix-based systems, set sigpipe to be ignored
00243 #if !(defined(_WIN32) || defined(__APPLE__) || defined(__AMIGAOS4__))
00244     WRN_NW << "ignoring SIGPIPE\n";
00245     signal(SIGPIPE,SIG_IGN);
00246 #endif
00247 
00248     if(SDLNet_Init() == -1) {
00249         ERR_NW << "could not initialize SDLNet; throwing error...\n";
00250         throw error(SDL_GetError());
00251     }
00252 
00253     socket_set = SDLNet_AllocSocketSet(512);
00254 
00255     worker_pool_man = new network_worker_pool::manager(min_threads, max_threads);
00256 }
00257 
00258 manager::~manager()
00259 {
00260     if(free_) {
00261         disconnect();
00262         delete worker_pool_man;
00263         worker_pool_man = NULL;
00264         SDLNet_FreeSocketSet(socket_set);
00265         socket_set = 0;
00266         waiting_sockets.clear();
00267         SDLNet_Quit();
00268     }
00269 }
00270 
00271 void set_raw_data_only()
00272 {
00273     network_worker_pool::set_raw_data_only();
00274 }
00275 
00276 server_manager::server_manager(int port, CREATE_SERVER create_server) : free_(false)
00277 {
00278     if(create_server != NO_SERVER && !server_socket) {
00279         try {
00280             server_socket = get_socket(connect("",port));
00281         } catch(network::error& e) {
00282             if(create_server == MUST_CREATE_SERVER) {
00283                 throw e;
00284             } else {
00285                 return;
00286             }
00287         }
00288 
00289         DBG_NW << "server socket initialized: " << server_socket << "\n";
00290         free_ = true;
00291     }
00292 }
00293 
00294 server_manager::~server_manager()
00295 {
00296     stop();
00297 }
00298 
00299 void server_manager::stop()
00300 {
00301     if(free_) {
00302         SDLNet_TCP_Close(server_socket);
00303         server_socket = 0;
00304         free_ = false;
00305     }
00306 }
00307 
00308 bool server_manager::is_running() const
00309 {
00310     return server_socket != NULL;
00311 }
00312 
00313 size_t nconnections()
00314 {
00315     return sockets.size();
00316 }
00317 
00318 bool is_server()
00319 {
00320     return server_socket != 0;
00321 }
00322 
00323 namespace {
00324 
00325 class connect_operation : public threading::async_operation
00326 {
00327 public:
00328     connect_operation(const std::string& host, int port) : host_(host), port_(port), error_(NULL), connect_(0)
00329     {}
00330 
00331     void check_error();
00332     void run();
00333 
00334     network::connection result() const { return connect_; }
00335 
00336 private:
00337     std::string host_;
00338     int port_;
00339     const char* error_;
00340     network::connection connect_;
00341 };
00342 
00343 void connect_operation::check_error()
00344 {
00345     if(error_ != NULL) {
00346         throw error(error_);
00347     }
00348 }
00349 
00350 namespace {
00351     struct _TCPsocket {
00352         int ready;
00353         SOCKET channel;
00354         IPaddress remoteAddress;
00355         IPaddress localAddress;
00356         int sflag;
00357     };
00358 } // end namespace
00359 
00360 void connect_operation::run()
00361 {
00362     char* const hostname = host_.empty() ? NULL : const_cast<char*>(host_.c_str());
00363     IPaddress ip;
00364     if(SDLNet_ResolveHost(&ip,hostname,port_) == -1) {
00365         error_ = "Could not connect to host";
00366         return;
00367     }
00368 
00369     TCPsocket sock = SDLNet_TCP_Open(&ip);
00370     if(!sock) {
00371         error_ = hostname == NULL ? "Could not bind to port" :
00372                                     "Could not connect to host";
00373         return;
00374     }
00375 
00376 #ifdef TCP_NODELAY
00377     //set TCP_NODELAY to 0 because SDL_Net turns it off, causing packet
00378     //flooding!
00379     {
00380     _TCPsocket* raw_sock = reinterpret_cast<_TCPsocket*>(sock);
00381     int no = 0;
00382     setsockopt(raw_sock->channel, IPPROTO_TCP, TCP_NODELAY, (char*)&no, sizeof(no));
00383     }
00384 #endif
00385 
00386 // Use non blocking IO
00387 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
00388     {
00389         unsigned long mode = 1;
00390         ioctlsocket (((_TCPsocket*)sock)->channel, FIONBIO, &mode);
00391     }
00392 #elif !defined(__BEOS__)
00393     int flags;
00394     flags = fcntl((reinterpret_cast<_TCPsocket*>(sock))->channel, F_GETFL, 0);
00395 #if defined(O_NONBLOCK)
00396     flags |= O_NONBLOCK;
00397 #elif defined(O_NDELAY)
00398     flags |= O_NDELAY;
00399 #elif defined(FNDELAY)
00400     flags |= FNDELAY;
00401 #endif
00402     if(fcntl((reinterpret_cast<_TCPsocket*>(sock))->channel, F_SETFL, flags) == -1) {
00403         error_ = ("Could not make socket non-blocking: " + std::string(strerror(errno))).c_str();
00404         return;
00405     }
00406 #else
00407     int on = 1;
00408     if(setsockopt(((_TCPsocket*)sock)->channel, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0) {
00409         error_ = ("Could not make socket non-blocking: " + std::string(strerror(errno))).c_str();
00410         return;
00411     }
00412 #endif
00413 
00414     // If this is a server socket
00415     if(hostname == NULL) {
00416         const threading::lock l(get_mutex());
00417         connect_ = create_connection(sock,"",port_);
00418         return;
00419     }
00420 
00421     // Send data telling the remote host that this is a new connection
00422     char buf[4];
00423     SDLNet_Write32(0,buf);
00424     const int nbytes = SDLNet_TCP_Send(sock,buf,4);
00425     if(nbytes != 4) {
00426         SDLNet_TCP_Close(sock);
00427         error_ = "Could not send initial handshake";
00428         return;
00429     }
00430 
00431     // No blocking operations from here on
00432     const threading::lock l(get_mutex());
00433     DBG_NW << "sent handshake...\n";
00434 
00435     if(is_aborted()) {
00436         DBG_NW << "connect operation aborted by calling thread\n";
00437         SDLNet_TCP_Close(sock);
00438         return;
00439     }
00440 
00441     // Allocate this connection a connection handle
00442     connect_ = create_connection(sock,host_,port_);
00443 
00444     const int res = SDLNet_TCP_AddSocket(socket_set,sock);
00445     if(res == -1) {
00446         SDLNet_TCP_Close(sock);
00447         error_ = "Could not add socket to socket set";
00448         return;
00449     }
00450 
00451     waiting_sockets.insert(connect_);
00452 
00453     sockets.push_back(connect_);
00454 
00455     while(!notify_finished()) {};
00456 }
00457 
00458 
00459 } // end namespace
00460 
00461 
00462 connection connect(const std::string& host, int port)
00463 {
00464     connect_operation op(host,port);
00465     op.run();
00466     op.check_error();
00467     return op.result();
00468 }
00469 
00470 connection connect(const std::string& host, int port, threading::waiter& waiter)
00471 {
00472     const threading::async_operation_holder<connect_operation> op(new connect_operation(host,port));
00473     const connect_operation::RESULT res = op.operation().execute(waiter);
00474     if(res == connect_operation::ABORTED) {
00475         return 0;
00476     }
00477 
00478     op.operation().check_error();
00479     return op.operation().result();
00480 }
00481 
00482 connection accept_connection()
00483 {
00484     if(!server_socket) {
00485         return 0;
00486     }
00487 
00488     // A connection isn't considered 'accepted' until it has sent its initial handshake.
00489     // The initial handshake is a 4 byte value, which is 0 for a new connection,
00490     // or the handle of the connection if it's trying to recover a lost connection.
00491 
00492     //! A list of all the sockets which have connected,
00493     //! but haven't had their initial handshake received.
00494     static std::vector<TCPsocket> pending_sockets;
00495     static SDLNet_SocketSet pending_socket_set = 0;
00496 
00497     const TCPsocket sock = SDLNet_TCP_Accept(server_socket);
00498     if(sock) {
00499         DBG_NW << "received connection. Pending handshake...\n";
00500         pending_sockets.push_back(sock);
00501         if(pending_socket_set == 0) {
00502             pending_socket_set = SDLNet_AllocSocketSet(512);
00503         }
00504 
00505         if(pending_socket_set != 0) {
00506             SDLNet_TCP_AddSocket(pending_socket_set,sock);
00507         }
00508     }
00509 
00510     if(pending_socket_set == 0) {
00511         return 0;
00512     }
00513 
00514     const int set_res = SDLNet_CheckSockets(pending_socket_set,0);
00515     if(set_res <= 0) {
00516         return 0;
00517     }
00518 
00519     DBG_NW << "pending socket activity...\n";
00520 
00521     for(std::vector<TCPsocket>::iterator i = pending_sockets.begin(); i != pending_sockets.end(); ++i) {
00522         if(!SDLNet_SocketReady(*i)) {
00523             continue;
00524         }
00525 
00526         // Receive the 4 bytes telling us if they're a new connection
00527         // or trying to recover a connection
00528         char buf[4];
00529 
00530         const TCPsocket sock = *i;
00531         SDLNet_TCP_DelSocket(pending_socket_set,sock);
00532         pending_sockets.erase(i);
00533 
00534         DBG_NW << "receiving data from pending socket...\n";
00535 
00536         const int len = SDLNet_TCP_Recv(sock,buf,4);
00537         if(len != 4) {
00538             WRN_NW << "pending socket disconnected\n";
00539             SDLNet_TCP_Close(sock);
00540             return 0;
00541         }
00542 
00543         const int handle = SDLNet_Read32(buf);
00544 
00545         DBG_NW << "received handshake from client: '" << handle << "'\n";
00546 
00547         const int res = SDLNet_TCP_AddSocket(socket_set,sock);
00548         if(res == -1) {
00549             SDLNet_TCP_Close(sock);
00550 
00551             throw network::error(_("Could not add socket to socket set"));
00552         }
00553 
00554 
00555         const connection connect = create_connection(sock,"",0);
00556 
00557         // Send back their connection number
00558         SDLNet_Write32(connect,buf);
00559         const int nbytes = SDLNet_TCP_Send(sock,buf,4);
00560         if(nbytes != 4) {
00561             SDLNet_TCP_DelSocket(socket_set,sock);
00562             SDLNet_TCP_Close(sock);
00563             remove_connection(connect);
00564             throw network::error(_("Could not send initial handshake"));
00565         }
00566 
00567         waiting_sockets.insert(connect);
00568         sockets.push_back(connect);
00569         return connect;
00570     }
00571 
00572     return 0;
00573 }
00574 
00575 bool disconnect(connection s, bool force)
00576 {
00577     if(s == 0) {
00578         while(sockets.empty() == false) {
00579             assert(sockets.back() != 0);
00580 //          disconnect(sockets.back(), true);
00581             while(disconnect(sockets.back()) == false) {
00582                 SDL_Delay(1);
00583             }
00584         }
00585         return true;
00586     }
00587     if (!is_server()) last_ping = 0;
00588 
00589     const connection_map::iterator info = connections.find(s);
00590     if(info != connections.end()) {
00591         if (!network_worker_pool::close_socket(info->second.sock, force)) {
00592             return false;
00593         }
00594     }
00595 
00596     bad_sockets.erase(s);
00597 
00598     std::deque<network::connection>::iterator dqi = std::find(disconnection_queue.begin(),disconnection_queue.end(),s);
00599     if(dqi != disconnection_queue.end()) {
00600         disconnection_queue.erase(dqi);
00601     }
00602 
00603     const sockets_list::iterator i = std::find(sockets.begin(),sockets.end(),s);
00604     if(i != sockets.end()) {
00605         sockets.erase(i);
00606 
00607         const TCPsocket sock = get_socket(s);
00608 
00609         waiting_sockets.erase(s);
00610         SDLNet_TCP_DelSocket(socket_set,sock);
00611         SDLNet_TCP_Close(sock);
00612 
00613         remove_connection(s);
00614     } else {
00615         if(sockets.size() == 1) {
00616             DBG_NW << "valid socket: " << static_cast<int>(*sockets.begin()) << "\n";
00617         }
00618     }
00619 
00620     return true;
00621 }
00622 
00623 void queue_disconnect(network::connection sock)
00624 {
00625     disconnection_queue.push_back(sock);
00626 }
00627 
00628 connection receive_data(config& cfg, connection connection_num, unsigned int timeout)
00629 {
00630     unsigned int start_ticks = SDL_GetTicks();
00631     while(true) {
00632         const connection res = receive_data(cfg,connection_num);
00633         if(res != 0) {
00634             return res;
00635         }
00636 
00637         if(timeout > SDL_GetTicks() - start_ticks) {
00638             SDL_Delay(1);
00639         }
00640         else
00641         {
00642             break;
00643         }
00644 
00645     }
00646 
00647     return 0;
00648 }
00649 
00650 connection receive_data(config& cfg, connection connection_num)
00651 {
00652     if(!socket_set) {
00653         return 0;
00654     }
00655 
00656     check_error();
00657 
00658     if(disconnection_queue.empty() == false) {
00659         const network::connection sock = disconnection_queue.front();
00660         disconnection_queue.pop_front();
00661         throw error("",sock);
00662     }
00663 
00664     if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
00665         return 0;
00666     }
00667 
00668     if(sockets.empty()) {
00669         return 0;
00670     }
00671 
00672     const int res = SDLNet_CheckSockets(socket_set,0);
00673 
00674     for(std::set<network::connection>::iterator i = waiting_sockets.begin(); res != 0 && i != waiting_sockets.end(); ) {
00675         connection_details& details = get_connection_details(*i);
00676         const TCPsocket sock = details.sock;
00677         if(SDLNet_SocketReady(sock)) {
00678 
00679             // See if this socket is still waiting for it to be assigned its remote handle.
00680             // If it is, then the first 4 bytes must be the remote handle.
00681             if(is_pending_remote_handle(*i)) {
00682                 char buf[4];
00683                 int len = SDLNet_TCP_Recv(sock,buf,4);
00684                 if(len != 4) {
00685                     throw error("Remote host disconnected",*i);
00686                 }
00687 
00688                 const int remote_handle = SDLNet_Read32(buf);
00689                 set_remote_handle(*i,remote_handle);
00690 
00691                 continue;
00692             }
00693 
00694             waiting_sockets.erase(i++);
00695             SDLNet_TCP_DelSocket(socket_set,sock);
00696             network_worker_pool::receive_data(sock);
00697         } else {
00698             ++i;
00699         }
00700     }
00701 
00702 
00703     TCPsocket sock = connection_num == 0 ? 0 : get_socket(connection_num);
00704     TCPsocket s = sock;
00705     sock = network_worker_pool::get_received_data(sock,cfg);
00706     if (sock == NULL) {
00707         if (!is_server() && last_ping != 0 && ping_timeout != 0)
00708         {
00709             if (connection_num == 0)
00710             {
00711                 s = get_socket(sockets.back());
00712             }
00713             if (!network_worker_pool::is_locked(s))
00714             {
00715                 check_timeout();
00716             }
00717         }
00718         return 0;
00719     }
00720 
00721     SDLNet_TCP_AddSocket(socket_set,sock);
00722 
00723     connection result = 0;
00724     for(connection_map::const_iterator j = connections.begin(); j != connections.end(); ++j) {
00725         if(j->second.sock == sock) {
00726             result = j->first;
00727             break;
00728         }
00729     }
00730     if(!cfg.empty()) {
00731         DBG_NW << "RECEIVED from: " << result << ": " << cfg; 
00732     }
00733 
00734     assert(result != 0);
00735     waiting_sockets.insert(result);
00736     if(!is_server()) {
00737         const time_t& now = time(NULL);
00738         const string_map::const_iterator ping = cfg.values.find("ping");
00739         if (ping != cfg.values.end()) {
00740             LOG_NW << "Lag: " << (now - lexical_cast<time_t>(cfg["ping"])) << "\n";
00741             last_ping = now;
00742         } else if (last_ping != 0) {
00743             last_ping = now;
00744         }           
00745     }
00746     return result;
00747 }
00748 
00749 connection receive_data(std::vector<char>& buf)
00750 {
00751     if(!socket_set) {
00752         return 0;
00753     }
00754 
00755     check_error();
00756 
00757     if(disconnection_queue.empty() == false) {
00758         const network::connection sock = disconnection_queue.front();
00759         disconnection_queue.pop_front();
00760         throw error("",sock);
00761     }
00762 
00763     if(bad_sockets.count(0)) {
00764         return 0;
00765     }
00766 
00767     if(sockets.empty()) {
00768         return 0;
00769     }
00770 
00771     const int res = SDLNet_CheckSockets(socket_set,0);
00772 
00773     for(std::set<network::connection>::iterator i = waiting_sockets.begin(); res != 0 && i != waiting_sockets.end(); ) {
00774         connection_details& details = get_connection_details(*i);
00775         const TCPsocket sock = details.sock;
00776         if(SDLNet_SocketReady(sock)) {
00777 
00778             // See if this socket is still waiting for it to be assigned its remote handle.
00779             // If it is, then the first 4 bytes must be the remote handle.
00780             if(is_pending_remote_handle(*i)) {
00781                 char buf[4];
00782                 int len = SDLNet_TCP_Recv(sock,buf,4);
00783                 if(len != 4) {
00784                     throw error("Remote host disconnected",*i);
00785                 }
00786 
00787                 const int remote_handle = SDLNet_Read32(buf);
00788                 set_remote_handle(*i,remote_handle);
00789 
00790                 continue;
00791             }
00792 
00793             waiting_sockets.erase(i++);
00794             SDLNet_TCP_DelSocket(socket_set,sock);
00795             network_worker_pool::receive_data(sock);
00796         } else {
00797             ++i;
00798         }
00799     }
00800 
00801 
00802     TCPsocket sock = network_worker_pool::get_received_data(buf);
00803     if (sock == NULL) {
00804         return 0;
00805     }
00806 
00807     SDLNet_TCP_AddSocket(socket_set,sock);
00808 
00809     connection result = 0;
00810     for(connection_map::const_iterator j = connections.begin(); j != connections.end(); ++j) {
00811         if(j->second.sock == sock) {
00812             result = j->first;
00813             break;
00814         }
00815     }
00816 
00817     assert(result != 0);
00818     waiting_sockets.insert(result);
00819     return result;
00820 }
00821 
00822 //! @todo Note the gzipped parameter should be removed later, we want to send
00823 //! all data gzipped. This can be done once the campaign server is also updated
00824 //! to work with gzipped data.
00825 void send_data(const config& cfg, connection connection_num, const bool gzipped)
00826 {
00827     DBG_NW << "in send_data()...\n";
00828     
00829     if(cfg.empty()) {
00830         return;
00831     }
00832 
00833     if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
00834         return;
00835     }
00836 
00837 //  log_scope2(network, "sending data");
00838     if(!connection_num) {
00839         DBG_NW << "sockets: " << sockets.size() << "\n";
00840         for(sockets_list::const_iterator i = sockets.begin();
00841             i != sockets.end(); ++i) {
00842             DBG_NW << "server socket: " << server_socket << "\ncurrent socket: " << *i << "\n";
00843             send_data(cfg,*i, gzipped);
00844         }
00845         return;
00846     }
00847 
00848     const connection_map::iterator info = connections.find(connection_num);
00849     if (info == connections.end()) {
00850         ERR_NW << "Error: socket: " << connection_num
00851             << "\tnot found in connection_map. Not sending...\n";
00852         return;
00853     }
00854 
00855     LOG_NW << "SENDING to: " << connection_num << ": " << cfg;
00856     network_worker_pool::queue_data(info->second.sock, cfg, gzipped);
00857 }
00858 
00859 void send_raw_data(const char* buf, int len, connection connection_num)
00860 {
00861     if(len == 0) {
00862         return;
00863     }
00864 
00865     if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
00866         return;
00867     }
00868 
00869     if(!connection_num) {
00870         for(sockets_list::const_iterator i = sockets.begin();
00871             i != sockets.end(); ++i) {
00872             send_raw_data(buf, len, connection_num);
00873         }
00874         return;
00875     }
00876 
00877     const connection_map::iterator info = connections.find(connection_num);
00878     if (info == connections.end()) {
00879         ERR_NW << "Error: socket: " << connection_num
00880             << "\tnot found in connection_map. Not sending...\n";
00881         return;
00882     }
00883 
00884     network_worker_pool::queue_raw_data(info->second.sock, buf, len);
00885 }
00886 
00887 void process_send_queue(connection, size_t)
00888 {
00889     check_error();
00890 }
00891 
00892 //! @todo Note the gzipped parameter should be removed later.
00893 void send_data_all_except(const config& cfg, connection connection_num, const bool gzipped)
00894 {
00895     for(sockets_list::const_iterator i = sockets.begin(); i != sockets.end(); ++i) {
00896         if(*i == connection_num) {
00897             continue;
00898         }
00899 
00900         send_data(cfg,*i, gzipped);
00901     }
00902 }
00903 
00904 std::string ip_address(connection connection_num)
00905 {
00906     std::stringstream str;
00907     const IPaddress* const ip = SDLNet_TCP_GetPeerAddress(get_socket(connection_num));
00908     if(ip != NULL) {
00909         const unsigned char* buf = reinterpret_cast<const unsigned char*>(&ip->host);
00910         for(int i = 0; i != sizeof(ip->host); ++i) {
00911             str << int(buf[i]);
00912             if(i+1 != sizeof(ip->host)) {
00913                 str << '.';
00914             }
00915         }
00916 
00917     }
00918 
00919     return str.str();
00920 }
00921 
00922 statistics get_send_stats(connection handle)
00923 {
00924     return network_worker_pool::get_current_transfer_stats(handle == 0 ? get_socket(sockets.back()) : get_socket(handle)).first;
00925 }
00926 statistics get_receive_stats(connection handle)
00927 {
00928     return network_worker_pool::get_current_transfer_stats(handle == 0 ? get_socket(sockets.back()) : get_socket(handle)).second;
00929 }
00930 
00931 } // end namespace network

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs