41 #include <boost/exception/get_error_info.hpp>
42 #include <boost/exception/info.hpp>
45 #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
47 #undef INADDR_BROADCAST
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <netinet/tcp.h>
59 #pragma GCC diagnostic ignored "-Wold-style-cast"
62 #define DBG_NW LOG_STREAM(debug, log_network)
63 #define LOG_NW LOG_STREAM(info, log_network)
64 #define WRN_NW LOG_STREAM(warn, log_network)
65 #define ERR_NW LOG_STREAM(err, log_network)
73 struct connection_details {
74 connection_details(TCPsocket sock,
const std::string& host,
int port)
75 : sock(sock), host(host), port(port), remote_handle(0),
76 connected_at(SDL_GetTicks())
90 typedef std::map<network::connection,connection_details> connection_map;
91 connection_map connections;
96 time_t last_ping, last_ping_check = 0;
102 connections.insert(std::pair<network::connection,connection_details>(connection_id,connection_details(sock,host,port)));
103 return connection_id++;
109 if(i == connections.end()) {
123 connections.erase(handle);
129 return details.host !=
"" && details.remote_handle == 0;
141 for(connection_map::const_iterator
i = connections.begin();
i != connections.end(); ++
i) {
142 if(
i->second.sock == sock) {
158 LOG_NW <<
"No network connections but last_ping is: " << last_ping;
162 const time_t& now = time(
nullptr);
163 DBG_NW <<
"Last ping: '" << last_ping <<
"' Current time: '" << now
164 <<
"' Time since last ping: " << now - last_ping <<
"s\n";
166 if (last_ping_check + 10 <= now) last_ping = now;
172 time_t
timeout = now - last_ping;
173 ERR_NW <<
"No server ping since " << timeout
174 <<
" seconds. Connection timed out.\n";
176 symbols[
"timeout"] = std::to_string(timeout);
177 throw network::error(
"No server ping since " + std::to_string(timeout) +
" second. "
178 "Connection timed out.");
180 last_ping_check = now;
186 SDLNet_SocketSet socket_set = 0;
187 std::set<network::connection> waiting_sockets;
188 typedef std::vector<network::connection> sockets_list;
189 sockets_list sockets;
192 struct partial_buffer {
199 std::vector<char>
buf;
203 TCPsocket server_socket;
205 std::deque<network::connection> disconnection_queue;
206 std::set<network::connection> bad_sockets;
222 : bytes_sent(sent), bytes_received(received), time_connected(SDL_GetTicks() - connected_at)
234 bad_sockets.insert(
socket);
250 DBG_NW <<
"NETWORK MANAGER CALLED!\n";
257 if(SDLNet_Init() == -1) {
258 ERR_NW <<
"could not initialize SDLNet; throwing error..." << std::endl;
259 throw error(SDL_GetError());
262 socket_set = SDLNet_AllocSocketSet(512);
271 delete worker_pool_man;
272 worker_pool_man =
nullptr;
273 SDLNet_FreeSocketSet(socket_set);
275 waiting_sockets.clear();
288 throw std::runtime_error(
"Proxy not available while using SDL_net. Use ANA instead.");
292 throw std::runtime_error(
"Proxy not available while using SDL_net. Use ANA instead.");
297 throw std::runtime_error(
"Proxy not available while using SDL_net. Use ANA instead.");
302 throw std::runtime_error(
"Proxy not available while using SDL_net. Use ANA instead.");
307 throw std::runtime_error(
"Proxy not available while using SDL_net. Use ANA instead.");
313 if(create_server !=
NO_SERVER && !server_socket) {
325 DBG_NW <<
"server socket initialized: " << server_socket <<
"\n";
338 SDLNet_TCP_Close(server_socket);
347 return server_socket !=
nullptr;
352 return sockets.size();
357 return server_socket != 0;
397 void connect_operation::run()
399 char*
const hostname =
host_.empty() ?
nullptr :
const_cast<char*
>(
host_.c_str());
401 if(SDLNet_ResolveHost(&ip,hostname,
port_) == -1) {
402 error_ =
N_(
"Could not connect to host.");
406 TCPsocket sock = SDLNet_TCP_Open(&ip);
408 error_ = hostname ==
nullptr
409 ?
"Could not bind to port"
410 :
N_(
"Could not connect to host.");
413 _TCPsocket* raw_sock =
reinterpret_cast<_TCPsocket*
>(sock);
419 setsockopt(raw_sock->channel, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&no),
sizeof(no));
424 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
426 unsigned long mode = 1;
427 ioctlsocket (raw_sock->channel, FIONBIO, &mode);
431 flags = fcntl(raw_sock->channel, F_GETFL, 0);
432 #if defined(O_NONBLOCK)
434 #elif defined(O_NDELAY)
436 #elif defined(FNDELAY)
439 if (fcntl(raw_sock->channel, F_SETFL, flags) == -1) {
441 SDLNet_TCP_Close(sock);
447 if(hostname ==
nullptr) {
459 SDLNet_Write32(0, &
buf);
460 const int nbytes = SDLNet_TCP_Send(sock,&
buf,4);
462 SDLNet_TCP_Close(sock);
463 error_ =
"Could not send initial handshake";
469 DBG_NW <<
"sent handshake...\n";
472 DBG_NW <<
"connect operation aborted by calling thread\n";
473 SDLNet_TCP_Close(sock);
480 const int res = SDLNet_TCP_AddSocket(socket_set,sock);
482 SDLNet_TCP_Close(sock);
483 error_ =
"Could not add socket to socket set";
491 while(!notify_finished()) {};
500 connect_operation op(host,port);
509 const connect_operation::RESULT res = op->execute(op, waiter);
510 if(res == connect_operation::ABORTED) {
514 static_cast<connect_operation*
>(op.get())->
check_error();
515 return static_cast<connect_operation*
>(op.get())->
result();
520 connection accept_connection_pending(std::vector<TCPsocket>& pending_sockets,
521 SDLNet_SocketSet& pending_socket_set)
523 DBG_NW <<
"pending socket activity...\n";
526 while (i != pending_sockets.end() && !SDLNet_SocketReady(*i)) ++i;
528 if (i == pending_sockets.end())
return 0;
538 const TCPsocket psock = *
i;
539 SDLNet_TCP_DelSocket(pending_socket_set,psock);
540 pending_sockets.erase(i);
542 DBG_NW <<
"receiving data from pending socket...\n";
544 const int len = SDLNet_TCP_Recv(psock,&
buf,4);
546 WRN_NW <<
"pending socket disconnected" << std::endl;
547 SDLNet_TCP_Close(psock);
553 DBG_NW <<
"received handshake from client: '" << handle <<
"'\n";
555 const int res = SDLNet_TCP_AddSocket(socket_set,psock);
557 ERR_NW <<
"SDLNet_GetError(): " << SDLNet_GetError() << std::endl;
558 SDLNet_TCP_Close(psock);
566 SDLNet_Write32(connect, &
buf);
567 const int nbytes = SDLNet_TCP_Send(psock,&
buf,4);
569 SDLNet_TCP_DelSocket(socket_set,psock);
570 SDLNet_TCP_Close(psock);
575 waiting_sockets.insert(connect);
576 sockets.push_back(connect);
596 static std::vector<TCPsocket> pending_sockets;
597 static SDLNet_SocketSet pending_socket_set = 0;
599 const TCPsocket sock = SDLNet_TCP_Accept(server_socket);
601 #if !defined(_WIN32) && !defined(__WIN32__) && !defined (WIN32)
602 _TCPsocket* raw_sock =
reinterpret_cast<_TCPsocket*
>(sock);
603 int fd_flags = fcntl(raw_sock->channel, F_GETFD, 0);
604 fd_flags |= FD_CLOEXEC;
605 if (fcntl(raw_sock->channel, F_SETFD, fd_flags) == -1) {
606 WRN_NW <<
"could not make socket " << sock <<
" close-on-exec: " << strerror(errno);
608 DBG_NW <<
"made socket " << sock <<
" close-on-exec\n";
612 DBG_NW <<
"received connection. Pending handshake...\n";
614 if(pending_socket_set == 0) {
615 pending_socket_set = SDLNet_AllocSocketSet(32);
618 if(pending_socket_set != 0) {
619 int res = SDLNet_TCP_AddSocket(pending_socket_set,sock);
622 pending_sockets.push_back(sock);
624 ERR_NW <<
"Pending socket set is full! Disconnecting " << sock <<
" connection" << std::endl;
625 ERR_NW <<
"SDLNet_GetError(): " << SDLNet_GetError() << std::endl;
627 SDLNet_TCP_Close(sock);
630 ERR_NW <<
"Error in SDLNet_AllocSocketSet" << std::endl;
634 if(pending_socket_set == 0) {
638 const int set_res = SDLNet_CheckSockets(pending_socket_set,0);
643 return accept_connection_pending(pending_sockets, pending_socket_set);
649 while(sockets.empty() ==
false) {
650 assert(sockets.back() != 0);
660 if(info != connections.end()) {
661 if (info->second.sock == server_socket)
670 bad_sockets.erase(s);
673 if(dqi != disconnection_queue.end()) {
674 disconnection_queue.erase(dqi);
678 if(i != sockets.end()) {
683 waiting_sockets.erase(s);
684 SDLNet_TCP_DelSocket(socket_set,sock);
685 SDLNet_TCP_Close(sock);
689 if(sockets.size() == 1) {
690 DBG_NW <<
"valid socket: " <<
static_cast<int>(*sockets.begin()) <<
"\n";
699 disconnection_queue.push_back(sock);
704 unsigned int start_ticks = SDL_GetTicks();
707 cfg,connection_num, bandwidth_in);
712 if(timeout > SDL_GetTicks() - start_ticks) {
733 if(disconnection_queue.empty() ==
false) {
735 disconnection_queue.pop_front();
736 throw error(
"",sock);
739 if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
743 if(sockets.empty()) {
747 const int res = SDLNet_CheckSockets(socket_set,0);
751 const TCPsocket sock = details.sock;
752 if(SDLNet_SocketReady(sock)) {
760 int len = SDLNet_TCP_Recv(sock,&
buf,4);
762 throw error(
"Remote host disconnected",*i);
765 const int remote_handle = SDLNet_Read32(&
buf);
771 waiting_sockets.erase(i++);
772 SDLNet_TCP_DelSocket(socket_set,sock);
780 TCPsocket sock = connection_num == 0 ? 0 :
get_socket(connection_num);
785 bandwidth_in = &temp;
790 TCPsocket
const * err_sock = boost::get_error_info<tcpsocket_info>(
e);
791 if(err_sock ==
nullptr)
794 for(connection_map::const_iterator i = connections.begin(); i != connections.end(); ++
i) {
795 if(i->second.sock == *err_sock) {
796 err_connection = i->first;
804 if (sock ==
nullptr) {
805 if (!
is_server() && last_ping != 0 && ping_timeout != 0)
807 if (connection_num == 0)
819 int set_res = SDLNet_TCP_AddSocket(socket_set,sock);
822 ERR_NW <<
"Socket set is full! Disconnecting " << sock <<
" connection" << std::endl;
823 SDLNet_TCP_Close(sock);
828 for(connection_map::const_iterator j = connections.begin(); j != connections.end(); ++j) {
829 if(j->second.sock == sock) {
835 DBG_NW <<
"RECEIVED from: " << result <<
": " << cfg;
839 waiting_sockets.insert(result);
841 const time_t& now = time(
nullptr);
845 }
else if (last_ping != 0) {
860 if(disconnection_queue.empty() ==
false) {
862 disconnection_queue.pop_front();
863 throw error(
"",sock);
866 if(bad_sockets.count(0)) {
870 if(sockets.empty()) {
874 const int res = SDLNet_CheckSockets(socket_set,0);
878 const TCPsocket sock = details.sock;
879 if(SDLNet_SocketReady(sock)) {
887 int len = SDLNet_TCP_Recv(sock,&buf,4);
889 throw error(
"Remote host disconnected",*i);
892 const int remote_handle = SDLNet_Read32(&buf);
898 waiting_sockets.erase(i++);
899 SDLNet_TCP_DelSocket(socket_set,sock);
908 if (sock ==
nullptr) {
916 bandwidth_in = &temp;
918 const int headers = 4;
922 int set_res = SDLNet_TCP_AddSocket(socket_set,sock);
926 ERR_NW <<
"Socket set is full! Disconnecting " << sock <<
" connection" << std::endl;
927 SDLNet_TCP_Close(sock);
931 for(connection_map::const_iterator j = connections.begin(); j != connections.end(); ++j) {
932 if(j->second.sock == sock) {
939 waiting_sockets.insert(result);
969 time_t now = time(0);
970 struct tm * timeinfo = localtime(&now);
971 int hour = timeinfo->tm_hour;
972 int day = timeinfo->tm_mday;
973 assert(hour < 24 && hour >= 0);
974 std::pair<bandwidth_map::iterator,bool> insertion =
hour_stats[hour].insert(std::make_pair(packet_type,
bandwidth_stats()));
976 if (!insertion.second && day != inserted->second.day)
982 inserted = insertion.first;
985 inserted->second.day = day;
1017 for (
int hour = 0; hour < 24; ++hour)
1026 time_t now = time(0);
1027 struct tm * timeinfo = localtime(&now);
1028 int hour = timeinfo->tm_hour - 1;
1036 assert(hour < 24 && hour >= 0);
1037 std::stringstream ss;
1055 itor->second.out_bytes +=
len;
1056 ++(itor->second.out_packets);
1062 itor->second.in_bytes +=
len;
1063 ++(itor->second.in_packets);
1073 assert(connection_num > 0);
1074 if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
1079 if (info == connections.end()) {
1080 ERR_NW <<
"Error: socket: " << connection_num
1081 <<
"\tnot found in connection_map. Not sending...\n";
1088 ERR_NW <<
"Could not determine size of file " << filename <<
", not sending." << std::endl;
1092 const int packet_headers = 4;
1100 DBG_NW <<
"in send_data()...\n";
1106 if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
1111 if(!connection_num) {
1112 DBG_NW <<
"sockets: " << sockets.size() <<
"\n";
1114 for(sockets_list::const_iterator i = sockets.begin();
1115 i != sockets.end(); ++
i) {
1116 DBG_NW <<
"server socket: " << server_socket <<
"\ncurrent socket: " << *i <<
"\n";
1123 if (info == connections.end()) {
1124 ERR_NW <<
"Error: socket: " << connection_num
1125 <<
"\tnot found in connection_map. Not sending...\n";
1129 LOG_NW <<
"SENDING to: " << connection_num <<
": " << cfg;
1139 if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
1143 if(!connection_num) {
1144 for(sockets_list::const_iterator i = sockets.begin();
1145 i != sockets.end(); ++
i) {
1152 if (info == connections.end()) {
1153 ERR_NW <<
"Error: socket: " << connection_num
1154 <<
"\tnot found in connection_map. Not sending...\n";
1157 const int packet_headers = 4;
1170 for(sockets_list::const_iterator i = sockets.begin(); i != sockets.end(); ++
i) {
1171 if(*i == connection_num) {
1181 std::stringstream str;
1182 const IPaddress*
const ip = SDLNet_TCP_GetPeerAddress(
get_socket(connection_num));
1184 const unsigned char*
buf =
reinterpret_cast<const unsigned char*
>(&ip->host);
1185 for(
int i = 0; i !=
sizeof(ip->host); ++
i) {
1187 if(i+1 !=
sizeof(ip->host)) {
High level network layer for config object transport.
static void set_remote_handle(network::connection handle, int remote_handle)
void operator()(const bandwidth_map::value_type &stats)
void receive_data(TCPsocket sock)
Function to asynchronously received data to the given socket.
static hour_stats_vector hour_stats(24)
const int ping_interval
Minimum interval between pings.
static lg::log_domain log_network("network")
void send_data_all_except(const config &cfg, connection connection_num, const std::string &packet_type)
Function to send data to all peers except 'connection_num'.
void add_bandwidth_in(const std::string &packet_type, size_t len)
void queue_disconnect(network::connection sock)
Function to queue a disconnection.
void set_proxy_user(const std::string &)
Set the user to authenticate with the proxy.
static l_noret error(LoadState *S, const char *why)
static TCPsocket get_socket(network::connection handle)
void queue_raw_data(TCPsocket sock, const char *buf, int len)
static void check_timeout()
Check whether too much time since the last server ping has passed and we timed out.
static bandwidth_map::iterator add_bandwidth_entry(const std::string &packet_type)
connection receive_data(config &cfg, connection connection_num, unsigned int timeout, bandwidth_in_ptr *bandwidth_in)
void process_send_queue(connection, size_t)
Function to send any data that is in a connection's send_queue, up to a maximum of 'max_size' bytes �...
boost::shared_ptr< bandwidth_stats > bandwidth_stats_ptr
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
To lexical_cast(From value)
Lexical cast converts one type to another.
Definitions for the interface to Wesnoth Markup Language (WML).
void enable_connection_through_proxy()
Attempt to connect through a proxy (as opposed to directly.)
static connection_details & get_connection_details(network::connection handle)
void set_proxy_address(const std::string &)
Set the address of the proxy.
size_t queue_data(TCPsocket sock, const config &buf, const std::string &packet_type)
bool is_locked(const TCPsocket sock)
TCPsocket get_received_data(TCPsocket sock, config &cfg, network::bandwidth_in_ptr &bandwidth_in)
void send_file(const std::string &filename, connection connection_num, const std::string &packet_type)
static UNUSEDNOWARN std::string _(const char *str)
std::vector< bandwidth_map > hour_stats_vector
#define ALIGN_4
Aligns a variable on a 4 byte boundary.
bandwidth_stats_output(std::stringstream &ss)
std::map< std::string, t_string > string_map
static int create_connection(TCPsocket sock, const std::string &host, int port)
void set_proxy_port(const std::string &)
Set the port of the proxy.
network::connection connect_
manager(size_t min_threads=1, size_t max_threads=0)
pending_statistics get_pending_stats()
void queue_file(TCPsocket sock, const std::string &filename)
GLboolean GLboolean GLboolean GLboolean a
GLenum GLuint GLsizei const char * buf
unsigned int ping_timeout
Amount of seconds after the last server ping when we assume to have timed out.
void set_proxy_password(const std::string &)
Set the password to authenticate with the proxy.
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
Templates and utility-routines for strings and numbers.
cl_event GLbitfield flags
bandwidth_stats_ptr totals_
connection_stats(int sent, int received, int connected_at)
void send_raw_data(const char *buf, int len, connection connection_num, const std::string &packet_type)
network::pending_statistics get_pending_stats()
CREATE_SERVER
Parameter to pass to the constructor.
statistics get_send_stats(connection handle)
Function to see the number of bytes being processed on the current socket.
size_t send_data(const config &cfg, connection connection_num, const std::string &packet_type)
Function to send data down a given connection, or broadcast to all peers if connection_num is 0...
bandwidth_stats & operator+=(const bandwidth_stats &a)
std::map< std::string, tfilter >::iterator itor
GLbitfield GLuint64 timeout
Will throw exception on failure.
static void remove_connection(network::connection handle)
std::map< const std::string, bandwidth_stats > bandwidth_map
server_manager(int port, CREATE_SERVER create_server=MUST_CREATE_SERVER)
connection accept_connection()
Function to accept a connection from a remote host.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
static const size_t type_width
Declarations for File-IO.
static const size_t packet_width
static void check_error()
bool disconnect(connection s)
Function to disconnect from a certain host, or close all connections if connection_num is 0...
int file_size(const std::string &fname)
Returns the size of a file, or -1 if the file doesn't exist.
size_t nconnections()
The number of peers we are connected to.
boost::error_info< struct tag_connum, connection > connection_info
connection connect(const std::string &host, int port)
Function to attempt to connect to a remote host.
bool has_attribute(const std::string &key) const
std::string ip_address(connection connection_num)
Function to get the remote ip address of a socket.
static const size_t bytes_width
bool find(E event, F functor)
Tests whether an event handler is available.
Standard logging facilities (interface).
std::string get_bandwidth_stats_all()
bool is_server()
If we are currently accepting connections.
std::string get_bandwidth_stats()
A config object defines a single node in a WML file, with access to child nodes.
statistics get_receive_stats(connection handle)
bool close_socket(TCPsocket sock)
GLsizei const GLcharARB ** string
static bool is_pending_remote_handle(network::connection handle)
connection_stats get_connection_stats(connection connection_num)
boost::shared_ptr< halo_record > handle
std::pair< network::statistics, network::statistics > get_current_transfer_stats(TCPsocket sock)
void add_bandwidth_out(const std::string &packet_type, size_t len)