From 22e136d88817b64ec904a7e7232a2cf9e80e74bd Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Sun, 23 Mar 2008 18:12:56 +0000 Subject: improved network connection handling, keep alive and time out --- src/core/application.cc | 5 +---- src/core/gameconnection.cc | 2 ++ src/core/gameinterface.cc | 21 ++++++------------ src/core/gameserver.cc | 3 +++ src/core/net.h | 4 ++++ src/core/netclient.cc | 30 ++++++++++++++++++++------ src/core/netclient.h | 3 +++ src/core/netconnection.cc | 53 +++++++++++++++++++++++++++++++++------------- src/core/netconnection.h | 10 +++++++-- src/core/netserver.cc | 47 ++++++++++++++++++++++++++++++++++------ src/core/player.cc | 16 ++++++++------ src/core/player.h | 3 +++ 12 files changed, 142 insertions(+), 55 deletions(-) (limited to 'src/core') diff --git a/src/core/application.cc b/src/core/application.cc index 5394b92..efa7bca 100644 --- a/src/core/application.cc +++ b/src/core/application.cc @@ -229,12 +229,9 @@ void Application::connect(std::string const &host) if (host.size()) { application_game = new GameConnection(host); - if (application_game->running()) { - con_print << "Connected to '" << host << "'\n"; - } else { + if (!application_game->running()) { delete application_game; application_game = 0; - con_warn << "Could not connect to '" << host << "'!\n"; } } else { application_game = new GameServer(); diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc index 634a6ec..923d9a3 100644 --- a/src/core/gameconnection.cc +++ b/src/core/gameconnection.cc @@ -124,6 +124,8 @@ void GameConnection::frame(float seconds) } if (localplayer()->dirty()) { + localplayer()->update_info(); + std::ostringstream osstream; osstream << "pif "; localplayer()->serialize_client_update(osstream); diff --git a/src/core/gameinterface.cc b/src/core/gameinterface.cc index 9f4dd29..a739eb2 100644 --- a/src/core/gameinterface.cc +++ b/src/core/gameinterface.cc @@ -42,23 +42,14 @@ GameInterface::GameInterface() { clear(); - if (Cvar::sv_dedicated->value()) + game_localplayer.clear(); + if (Cvar::sv_dedicated->value()) { game_localplayer.player_name.assign("Console"); - else { - game_localplayer.clear(); - Cvar *cl_name = Cvar::find("cl_name"); - if (cl_name) { - game_localplayer.player_name = cl_name->str(); - } - - Cvar *cl_color = Cvar::find("cl_color"); - math::Color color(1.0, 1.0, 1.0, 1.0); - if (cl_color && cl_color->value()) { - std::istringstream is(cl_color->str()); - is >> color; - } - game_localplayer.player_color = color; + } else { + game_localplayer.player_name.assign("Player"); + game_localplayer.update_info(); } + core::Func::add("list_model", (core::FuncPtr) func_list_model); } diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index 211bb96..1aa57f0 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -252,6 +252,9 @@ void GameServer::frame(float seconds) } } + if (localplayer()->dirty()) + localplayer()->update_info(); + // run a time frame on each entity std::map::iterator it; for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { diff --git a/src/core/net.h b/src/core/net.h index dd81580..150bbc1 100644 --- a/src/core/net.h +++ b/src/core/net.h @@ -20,6 +20,10 @@ const unsigned int MAXPENDINGCONNECTIONS = 32; /// default network port const unsigned int DEFAULTPORT = 8042; + +/// network timeout in seconds +const float NETTIMEOUT = 15; + } #include "core/netserver.h" diff --git a/src/core/netclient.cc b/src/core/netclient.cc index c7186f5..c910e26 100644 --- a/src/core/netclient.cc +++ b/src/core/netclient.cc @@ -9,6 +9,7 @@ #include "sys/sys.h" #include "core/net.h" +#include "core/application.h" namespace core { @@ -38,6 +39,9 @@ NetClient::NetClient(std::string host, int port) : sendq.clear(); messageblock.clear(); + client_keepalive = application()->time(); + client_timeout = application()->time(); + client_error = false; } @@ -85,12 +89,15 @@ void NetClient::receive(char *data) std::string datablock; datablock.assign(data); + if (!datablock.size()) + return; + while(datablock.size() > 0 ) { // scan the datablock for enters if (datablock[0] == '\n' || datablock[0] == '\r') { // TODO detect "begin binary block" message for zlib compression if (messageblock.size() > 0 ) { - recvq.push_back(messageblock); + recvq.push_back(messageblock); messageblock.clear(); } } else { @@ -103,6 +110,8 @@ void NetClient::receive(char *data) } datablock.erase(0,1); } + + client_timeout = application()->time(); } void NetClient::send(std::string const &msg) @@ -112,16 +121,23 @@ void NetClient::send(std::string const &msg) void NetClient::transmit(int serverfd) { - if (sendq.size() >= BLOCKSIZE) { - con_warn << "Outgoing message exceeds " << BLOCKSIZE << " bytes!\n"; - sendq.clear(); - return; + + if (!sendq.size()) { + if (client_keepalive + NETTIMEOUT/2 < application()->time()) { + sendq.assign("ping\n"); + } else { + return; + } + } else if (sendq.size() >= FRAMESIZE) { + con_warn << "Outgoing message exceeds " << FRAMESIZE << " bytes!\n"; + //sendq.clear(); + //return; } ssize_t bytes_sent = 0; while (sendq.size() && !error()) { - bytes_sent = sendto(serverfd, sendq.c_str(), sendq.size(), 0, + bytes_sent = ::sendto(serverfd, sendq.c_str(), sendq.size(), 0, (struct sockaddr *)&client_addr, sizeof(client_addr)); if (bytes_sent < 0) { @@ -132,6 +148,8 @@ void NetClient::transmit(int serverfd) sendq.erase(0, bytes_sent); } sendq.clear(); + + client_keepalive = application()->time(); } } diff --git a/src/core/netclient.h b/src/core/netclient.h index 1a7c716..49f509c 100644 --- a/src/core/netclient.h +++ b/src/core/netclient.h @@ -66,6 +66,9 @@ public: inline State state() const { return client_state; } State client_state; + + float client_timeout; + float client_keepalive; private: struct sockaddr_in client_addr; diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc index e3b4c69..b640595 100644 --- a/src/core/netconnection.cc +++ b/src/core/netconnection.cc @@ -17,7 +17,8 @@ namespace core NetConnection::NetConnection() { - timeout = core::application()->time(); + connection_timeout = core::application()->time(); + connection_state = Connecting; } NetConnection::~NetConnection() @@ -34,6 +35,7 @@ void NetConnection::connect(std::string const &to_host, int to_port) { connection_error = false; connection_fd = -1; + connection_state = Connecting; if (valid()) return; @@ -47,8 +49,6 @@ void NetConnection::connect(std::string const &to_host, int to_port) return; } - con_print << "Connecting to " << inet_ntoa(*((struct in_addr *)serverhostent->h_addr)) << ":" << to_port << "..." << std::endl; - // Get a socket file descriptor connection_fd = socket(PF_INET, SOCK_DGRAM, 0); if (connection_fd == -1) { @@ -69,7 +69,6 @@ void NetConnection::connect(std::string const &to_host, int to_port) return; } - connection_host = to_host; connection_port = to_port; connection_error = false; @@ -77,8 +76,12 @@ void NetConnection::connect(std::string const &to_host, int to_port) FD_ZERO(&clientset); FD_SET(fd(), &clientset); - timeout = core::application()->time(); + connection_timeout = application()->time(); + connection_keepalive = application()->time(); + game()->localplayer()->player_dirty = true; + + con_print << "Connecting to " << inet_ntoa(*((struct in_addr *)serverhostent->h_addr)) << ":" << to_port << "..." << std::endl; } void NetConnection::disconnect() @@ -96,6 +99,7 @@ void NetConnection::disconnect() connection_error = false; connection_host.clear(); connection_port = 0; + connection_state = Connecting; } bool NetConnection::has_messages() const @@ -124,7 +128,7 @@ void NetConnection::receive() memset(recvbuf, '\0', BLOCKSIZE); bytes_received = ::recv(connection_fd, recvbuf, BLOCKSIZE-1, 0); - timeout = core::application()->time(); + connection_timeout = core::application()->time(); if (bytes_received == 0) { con_print << "Disconnected."; @@ -168,12 +172,10 @@ void NetConnection::frame(float seconds) int nb = select(fd()+1, &readset, NULL, NULL, &timeout); if (nb == 0) { - /* - if (timout + TIMEOUT < core::application()->time()) { - con_error << "Connection time out\n"; + if (connection_timeout + NETTIMEOUT < core::application()->time()) { + con_error << "Connection timeout!\n"; abort(); } - */ return; } if (nb == -1) { @@ -213,8 +215,14 @@ void NetConnection::transmit() if (error() || invalid()) return; - - if (sendq.size() > FRAMESIZE) { + + if (!sendq.size()) { + if (connection_keepalive + NETTIMEOUT /2 < application()->time()) { + sendq.assign("ping\n"); + } else { + return; + } + } else if (sendq.size() > FRAMESIZE) { con_warn << "Outgoing message exceeds " << FRAMESIZE << " bytes!\n"; //sendq.clear(); //return; @@ -234,14 +242,16 @@ void NetConnection::transmit() // assert (bytes_sent <= sendbuf.size()); sendq.erase(0, bytes_sent); } - - sendq.clear(); + + connection_keepalive = application()->time(); } // parse incoming client messages /** * The following incoming messages are parsed; * + * connect + * disconnect * msg info * msg public * die @@ -271,6 +281,19 @@ void NetConnection::parse_incoming_message(const std::string & message) } } + } else if (command == "connect") { + + connection_state = Connected; + con_print << "Connected." << std::endl; + return; + + } else if (command == "disconnect") { + + con_error << "Server disconnected!" << std::endl; + abort(); + + } else if (command == "ping") { + } else if (command == "die") { unsigned int id; msgstream >> id; @@ -312,9 +335,9 @@ void NetConnection::parse_incoming_message(const std::string & message) } else entity->recieve_server_update(msgstream); } - return; } else if (command == "pif") { + connection()->localplayer()->recieve_server_update(msgstream); } diff --git a/src/core/netconnection.h b/src/core/netconnection.h index dca85c7..08536de 100644 --- a/src/core/netconnection.h +++ b/src/core/netconnection.h @@ -67,6 +67,12 @@ public: inline bool connected() const { return ((connection_fd != -1) && !connection_error); } + enum State {Connecting=0, Connected=1}; + + inline State state() const { return connection_state; } + + State connection_state; + protected: /// receive incoming data and store messages void receive(); @@ -86,14 +92,14 @@ private: std::string sendq; fd_set clientset; + float connection_timeout; + float connection_keepalive; int connection_fd; bool connection_error; std::string connection_host; int connection_port; struct sockaddr_in server_addr; char recvbuf[BLOCKSIZE]; - - float timeout; }; } diff --git a/src/core/netserver.cc b/src/core/netserver.cc index 16beee0..c9870c5 100644 --- a/src/core/netserver.cc +++ b/src/core/netserver.cc @@ -86,6 +86,8 @@ NetServer::~NetServer() { con_print << "Shutting down network server..." << std::endl; + std::string netmsg("disconnect\n"); + // delete all clients std::list:: iterator it; for (it = clients.begin(); it != clients.end(); it++) { @@ -94,6 +96,9 @@ NetServer::~NetServer() if ((*it)->state() == NetClient::Connected) server()->player_disconnect((*it)->player()); + (*it)->send(netmsg); + (*it)->transmit(fd()); + delete (*it); } clients.clear(); @@ -108,6 +113,24 @@ void NetServer::reap() for (std::list:: iterator it = clients.begin(); it != clients.end(); it++) { NetClient *client = *it; + if (client->client_timeout + NETTIMEOUT < application()->time()) { + // client timed out, send a disconnect + std::string netmsg("disconnect\n"); + (*it)->send(netmsg); + (*it)->transmit(fd()); + (*it)->abort(); + + // print a message + std::string message(client->player()->name()); + message.append(" timed out."); + + if (client->state() == NetClient::Connected) { + server()->broadcast(message, client->player()); + } else { + con_print << message << std::endl; + } + } + if (client->error()) { // notify the game server @@ -234,15 +257,15 @@ NetClient * NetServer::client_connect(std::string const host, int const port) clients.push_back(client); + // send welcome message std::ostringstream netmsg; netmsg.str(""); netmsg << "msg info Receiving data from remote server...\n"; client->send(netmsg.str()); - transmit(); + client->transmit(fd()); // send entities std::map::iterator it; - for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { netmsg.str(""); switch ((*it).second->type()) { @@ -260,8 +283,7 @@ NetClient * NetServer::client_connect(std::string const host, int const port) break; } } - - transmit(); + client->transmit(fd()); client->player()->player_dirty = false; @@ -303,6 +325,7 @@ NetClient *NetServer::find_client(Player const *player) * cmd * cup * pif + * ping * say * */ @@ -331,13 +354,23 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me if (client->state() == NetClient::Connecting) { client->client_state = NetClient::Connected; server()->player_connect(client->player()); + + std::string netmsg("connect\n"); + client->send(netmsg); + } else if ((client->state() == NetClient::Connected) && (client->player()->name() != oldname)) { - oldname.append(" renamed to "); - oldname.append(client->player()->name()); - server()->broadcast(oldname); + + std::string netmsg(oldname); + netmsg.append(" renamed to "); + netmsg.append(client->player()->name()); + server()->broadcast(netmsg); } } + if (command == "ping") { + return; + } + if (client->state() != NetClient::Connected) return; diff --git a/src/core/player.cc b/src/core/player.cc index b89da99..71ac8de 100644 --- a/src/core/player.cc +++ b/src/core/player.cc @@ -44,21 +44,25 @@ void Player::clear_assets() player_control = 0; } -void Player::serialize_client_update(std::ostream & os) +void Player::update_info() { Cvar *cl_name = Cvar::find("cl_name"); if (cl_name) { - player_name = cl_name->str(); + if (cl_name->str().size()) + player_name = cl_name->str(); } - + Cvar *cl_color = Cvar::find("cl_color"); math::Color color(1.0, 1.0, 1.0, 1.0); if (cl_color) { std::istringstream is(cl_color->str()); - is >> color; - player_color = color; + if (is >> color) + player_color = color; } - +} + +void Player::serialize_client_update(std::ostream & os) +{ os << " " << player_color << " \"" << player_name << "\""; } diff --git a/src/core/player.h b/src/core/player.h index 59b18c1..08abac2 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -74,6 +74,9 @@ public: /// remove an asset void remove_asset(unsigned int id); + /// update player info from client variables + void update_info(); + /* -- should actually not be public --*/ // dirty state -- cgit v1.2.3