← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/refac-netcode into lp:widelands

 

Notabilis has proposed merging lp:~widelands-dev/widelands/refac-netcode into lp:widelands.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1689087 in widelands: "Implementing a relay server"
  https://bugs.launchpad.net/widelands/+bug/1689087

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/refac-netcode/+merge/323798

Hint: You don't need to know anything about networking to review this.

Moving some code around to hide most SDLNet specific calls inside two classes. This branch should not change anything on the functionality but is a preparation for a future replacement of SDLNet with boost.asio.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/refac-netcode into lp:widelands.
=== modified file 'src/network/CMakeLists.txt'
--- src/network/CMakeLists.txt	2017-02-28 12:59:39 +0000
+++ src/network/CMakeLists.txt	2017-05-09 19:19:42 +0000
@@ -6,6 +6,10 @@
     internet_gaming_messages.cc
     internet_gaming_messages.h
     internet_gaming_protocol.h
+    gameclient.cc
+    gameclient.h
+    gamehost.cc
+    gamehost.h
     netclient.cc
     netclient.h
     nethost.cc

=== renamed file 'src/network/netclient.cc' => 'src/network/gameclient.cc'
--- src/network/netclient.cc	2017-02-10 14:12:36 +0000
+++ src/network/gameclient.cc	2017-05-09 19:19:42 +0000
@@ -17,7 +17,7 @@
  *
  */
 
-#include "network/netclient.h"
+#include "network/gameclient.h"
 
 #include <memory>
 
@@ -53,19 +53,12 @@
 #include "wui/interactive_player.h"
 #include "wui/interactive_spectator.h"
 
-struct NetClientImpl {
+struct GameClientImpl {
 	GameSettings settings;
 
 	std::string localplayername;
 
-	/// The socket that connects us to the host
-	TCPsocket sock;
-
-	/// Socket set used for selection
-	SDLNet_SocketSet sockset;
-
-	/// Deserializer acts as a buffer for packets (reassembly/splitting up)
-	Deserializer deserializer;
+	std::unique_ptr<NetClient> net;
 
 	/// Currently active modal panel. Receives an end_modal on disconnect
 	UI::Panel* modal;
@@ -96,18 +89,16 @@
 	std::vector<ChatMessage> chatmessages;
 };
 
-NetClient::NetClient(IPaddress* const svaddr, const std::string& playername, bool internet)
-   : d(new NetClientImpl), internet_(internet) {
-	d->sock = SDLNet_TCP_Open(svaddr);
-	if (d->sock == nullptr)
+GameClient::GameClient(const std::string& host, const uint16_t port, const std::string& playername, bool internet)
+   : d(new GameClientImpl), internet_(internet) {
+	d->net = NetClient::connect(host, port);
+	if (!d->net || !d->net->is_connected()) {
 		throw WLWarning(_("Could not establish connection to host"),
 		                _("Widelands could not establish a connection to the given "
 		                  "address.\n"
 		                  "Either no Widelands server was running at the supposed port or\n"
 		                  "the server shut down as you tried to connect."));
-
-	d->sockset = SDLNet_AllocSocketSet(1);
-	SDLNet_TCP_AddSocket(d->sockset, d->sock);
+	}
 
 	d->settings.playernum = UserSettings::not_connected();
 	d->settings.usernum = -2;
@@ -122,22 +113,21 @@
 	d->settings.win_condition_script = d->settings.win_condition_scripts.front();
 }
 
-NetClient::~NetClient() {
-	if (d->sock != nullptr)
+GameClient::~GameClient() {
+	assert(d->net != nullptr);
+	if (d->net->is_connected())
 		disconnect("CLIENT_LEFT_GAME", "", true, false);
 
-	SDLNet_FreeSocketSet(d->sockset);
-
 	delete d;
 }
 
-void NetClient::run() {
+void GameClient::run() {
 	SendPacket s;
 	s.unsigned_8(NETCMD_HELLO);
 	s.unsigned_8(NETWORK_PROTOCOL_VERSION);
 	s.string(d->localplayername);
 	s.string(build_id());
-	s.send(d->sock);
+	d->net->send(s);
 
 	d->settings.multiplayer = true;
 
@@ -221,7 +211,7 @@
 	}
 }
 
-void NetClient::think() {
+void GameClient::think() {
 	handle_network();
 
 	if (d->game) {
@@ -242,7 +232,7 @@
 	}
 }
 
-void NetClient::send_player_command(Widelands::PlayerCommand& pc) {
+void GameClient::send_player_command(Widelands::PlayerCommand& pc) {
 	assert(d->game);
 	if (pc.sender() != d->settings.playernum + 1) {
 		delete &pc;
@@ -255,7 +245,7 @@
 	s.unsigned_8(NETCMD_PLAYERCOMMAND);
 	s.signed_32(d->game->get_gametime());
 	pc.serialize(s);
-	s.send(d->sock);
+	d->net->send(s);
 
 	d->lasttimestamp = d->game->get_gametime();
 	d->lasttimestamp_realtime = SDL_GetTicks();
@@ -263,15 +253,15 @@
 	delete &pc;
 }
 
-int32_t NetClient::get_frametime() {
+int32_t GameClient::get_frametime() {
 	return d->time.time() - d->game->get_gametime();
 }
 
-GameController::GameType NetClient::get_game_type() {
+GameController::GameType GameClient::get_game_type() {
 	return GameController::GameType::NETCLIENT;
 }
 
-void NetClient::report_result(uint8_t player_nr,
+void GameClient::report_result(uint8_t player_nr,
                               Widelands::PlayerEndResult result,
                               const std::string& info) {
 	// Send to game
@@ -285,54 +275,54 @@
 	d->game->player_manager()->add_player_end_status(pes);
 }
 
-const GameSettings& NetClient::settings() {
+const GameSettings& GameClient::settings() {
 	return d->settings;
 }
 
-void NetClient::set_scenario(bool) {
-}
-
-bool NetClient::can_change_map() {
-	return false;
-}
-
-bool NetClient::can_change_player_state(uint8_t const) {
-	return false;
-}
-
-bool NetClient::can_change_player_tribe(uint8_t number) {
+void GameClient::set_scenario(bool) {
+}
+
+bool GameClient::can_change_map() {
+	return false;
+}
+
+bool GameClient::can_change_player_state(uint8_t const) {
+	return false;
+}
+
+bool GameClient::can_change_player_tribe(uint8_t number) {
 	return can_change_player_team(number);
 }
 
-bool NetClient::can_change_player_team(uint8_t number) {
+bool GameClient::can_change_player_team(uint8_t number) {
 	return (number == d->settings.playernum) && !d->settings.scenario && !d->settings.savegame;
 }
 
-bool NetClient::can_change_player_init(uint8_t) {
-	return false;
-}
-
-bool NetClient::can_launch() {
-	return false;
-}
-
-void NetClient::set_player_state(uint8_t, PlayerSettings::State) {
-	// client is not allowed to do this
-}
-
-void NetClient::set_player_ai(uint8_t, const std::string&, bool const /* random_ai */) {
-	// client is not allowed to do this
-}
-
-void NetClient::next_player_state(uint8_t) {
-	// client is not allowed to do this
-}
-
-void NetClient::set_map(const std::string&, const std::string&, uint32_t, bool) {
-	// client is not allowed to do this
-}
-
-void NetClient::set_player_tribe(uint8_t number,
+bool GameClient::can_change_player_init(uint8_t) {
+	return false;
+}
+
+bool GameClient::can_launch() {
+	return false;
+}
+
+void GameClient::set_player_state(uint8_t, PlayerSettings::State) {
+	// client is not allowed to do this
+}
+
+void GameClient::set_player_ai(uint8_t, const std::string&, bool const /* random_ai */) {
+	// client is not allowed to do this
+}
+
+void GameClient::next_player_state(uint8_t) {
+	// client is not allowed to do this
+}
+
+void GameClient::set_map(const std::string&, const std::string&, uint32_t, bool) {
+	// client is not allowed to do this
+}
+
+void GameClient::set_player_tribe(uint8_t number,
                                  const std::string& tribe,
                                  bool const random_tribe) {
 	if ((number != d->settings.playernum))
@@ -343,10 +333,10 @@
 	s.unsigned_8(number);
 	s.string(tribe);
 	s.unsigned_8(random_tribe ? 1 : 0);
-	s.send(d->sock);
+	d->net->send(s);
 }
 
-void NetClient::set_player_team(uint8_t number, Widelands::TeamNumber team) {
+void GameClient::set_player_team(uint8_t number, Widelands::TeamNumber team) {
 	if ((number != d->settings.playernum))
 		return;
 
@@ -354,14 +344,14 @@
 	s.unsigned_8(NETCMD_SETTING_CHANGETEAM);
 	s.unsigned_8(number);
 	s.unsigned_8(team);
-	s.send(d->sock);
+	d->net->send(s);
 }
 
-void NetClient::set_player_closeable(uint8_t, bool) {
+void GameClient::set_player_closeable(uint8_t, bool) {
 	//  client is not allowed to do this
 }
 
-void NetClient::set_player_shared(uint8_t number, uint8_t player) {
+void GameClient::set_player_shared(uint8_t number, uint8_t player) {
 	if ((number != d->settings.playernum))
 		return;
 
@@ -369,10 +359,10 @@
 	s.unsigned_8(NETCMD_SETTING_CHANGESHARED);
 	s.unsigned_8(number);
 	s.unsigned_8(player);
-	s.send(d->sock);
+	d->net->send(s);
 }
 
-void NetClient::set_player_init(uint8_t number, uint8_t) {
+void GameClient::set_player_init(uint8_t number, uint8_t) {
 	if ((number != d->settings.playernum))
 		return;
 
@@ -380,29 +370,29 @@
 	SendPacket s;
 	s.unsigned_8(NETCMD_SETTING_CHANGEINIT);
 	s.unsigned_8(number);
-	s.send(d->sock);
+	d->net->send(s);
 }
 
-void NetClient::set_player_name(uint8_t, const std::string&) {
+void GameClient::set_player_name(uint8_t, const std::string&) {
 	// until now the name is set before joining - if you allow a change in
 	// launchgame-menu, here properly should be a set_name function
 }
 
-void NetClient::set_player(uint8_t, const PlayerSettings&) {
+void GameClient::set_player(uint8_t, const PlayerSettings&) {
 	// do nothing here - the request for a positionchange is send in
 	// set_player_number(uint8_t) to the host.
 }
 
-std::string NetClient::get_win_condition_script() {
+std::string GameClient::get_win_condition_script() {
 	return d->settings.win_condition_script;
 }
 
-void NetClient::set_win_condition_script(const std::string&) {
+void GameClient::set_win_condition_script(const std::string&) {
 	// Clients are not allowed to change this
 	NEVER_HERE();
 }
 
-void NetClient::set_player_number(uint8_t const number) {
+void GameClient::set_player_number(uint8_t const number) {
 	// If the playernumber we want to switch to is our own, there is no need
 	// for sending a request to the host.
 	if (number == d->settings.playernum)
@@ -417,18 +407,18 @@
 	SendPacket s;
 	s.unsigned_8(NETCMD_SETTING_CHANGEPOSITION);
 	s.unsigned_8(number);
-	s.send(d->sock);
+	d->net->send(s);
 }
 
-uint32_t NetClient::real_speed() {
+uint32_t GameClient::real_speed() {
 	return d->realspeed;
 }
 
-uint32_t NetClient::desired_speed() {
+uint32_t GameClient::desired_speed() {
 	return d->desiredspeed;
 }
 
-void NetClient::set_desired_speed(uint32_t speed) {
+void GameClient::set_desired_speed(uint32_t speed) {
 	if (speed > std::numeric_limits<uint16_t>::max())
 		speed = std::numeric_limits<uint16_t>::max();
 
@@ -438,19 +428,19 @@
 		SendPacket s;
 		s.unsigned_8(NETCMD_SETSPEED);
 		s.unsigned_16(d->desiredspeed);
-		s.send(d->sock);
+		d->net->send(s);
 	}
 }
 
 // Network games cannot be paused
-bool NetClient::is_paused() {
+bool GameClient::is_paused() {
 	return false;
 }
 
-void NetClient::set_paused(bool /* paused */) {
+void GameClient::set_paused(bool /* paused */) {
 }
 
-void NetClient::receive_one_player(uint8_t const number, StreamRead& packet) {
+void GameClient::receive_one_player(uint8_t const number, StreamRead& packet) {
 	if (number >= d->settings.players.size())
 		throw DisconnectException("PLAYER_UPDATE_FOR_N_E_P");
 
@@ -466,7 +456,7 @@
 	player.shared_in = packet.unsigned_8();
 }
 
-void NetClient::receive_one_user(uint32_t const number, StreamRead& packet) {
+void GameClient::receive_one_user(uint32_t const number, StreamRead& packet) {
 	if (number > d->settings.users.size())
 		throw DisconnectException("USER_UPDATE_FOR_N_E_U");
 
@@ -485,18 +475,18 @@
 	}
 }
 
-void NetClient::send(const std::string& msg) {
+void GameClient::send(const std::string& msg) {
 	SendPacket s;
 	s.unsigned_8(NETCMD_CHAT);
 	s.string(msg);
-	s.send(d->sock);
+	d->net->send(s);
 }
 
-const std::vector<ChatMessage>& NetClient::get_messages() const {
+const std::vector<ChatMessage>& GameClient::get_messages() const {
 	return d->chatmessages;
 }
 
-void NetClient::send_time() {
+void GameClient::send_time() {
 	assert(d->game);
 
 	log("[Client]: sending timestamp: %i\n", d->game->get_gametime());
@@ -504,19 +494,20 @@
 	SendPacket s;
 	s.unsigned_8(NETCMD_TIME);
 	s.signed_32(d->game->get_gametime());
-	s.send(d->sock);
+	d->net->send(s);
 
 	d->lasttimestamp = d->game->get_gametime();
 	d->lasttimestamp_realtime = SDL_GetTicks();
 }
 
-void NetClient::syncreport() {
-	if (d->sock) {
+void GameClient::syncreport() {
+	assert(d->net != nullptr);
+	if (d->net->is_connected()) {
 		SendPacket s;
 		s.unsigned_8(NETCMD_SYNCREPORT);
 		s.signed_32(d->game->get_gametime());
 		s.data(d->game->get_sync_hash().data, 16);
-		s.send(d->sock);
+		d->net->send(s);
 	}
 }
 
@@ -525,7 +516,7 @@
  *
  * \note The caller must handle exceptions by closing the connection.
  */
-void NetClient::handle_packet(RecvPacket& packet) {
+void GameClient::handle_packet(RecvPacket& packet) {
 	uint8_t cmd = packet.unsigned_8();
 
 	if (cmd == NETCMD_DISCONNECT) {
@@ -555,7 +546,7 @@
 	case NETCMD_PING: {
 		SendPacket s;
 		s.unsigned_8(NETCMD_PONG);
-		s.send(d->sock);
+		d->net->send(s);
 
 		log("[Client] Pong!\n");
 		break;
@@ -611,7 +602,7 @@
 		// Yes we need the file!
 		SendPacket s;
 		s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
-		s.send(d->sock);
+		d->net->send(s);
 
 		if (file_)
 			delete file_;
@@ -645,7 +636,7 @@
 		s.unsigned_8(NETCMD_FILE_PART);
 		s.unsigned_32(part);
 		s.string(file_->md5sum);
-		s.send(d->sock);
+		d->net->send(s);
 
 		FilePart fp;
 
@@ -688,12 +679,12 @@
 				// Something went wrong! We have to rerequest the file.
 				s.reset();
 				s.unsigned_8(NETCMD_NEW_FILE_AVAILABLE);
-				s.send(d->sock);
+				d->net->send(s);
 				// Notify the players
 				s.reset();
 				s.unsigned_8(NETCMD_CHAT);
 				s.string(_("/me 's file failed md5 checksumming."));
-				s.send(d->sock);
+				d->net->send(s);
 				g_fs->fs_unlink(file_->filename);
 			}
 			// Check file for validity
@@ -722,7 +713,7 @@
 				s.unsigned_8(NETCMD_CHAT);
 				s.string(_("/me checked the received file. Although md5 check summing succeeded, "
 				           "I can not handle the file."));
-				s.send(d->sock);
+				d->net->send(s);
 			}
 		}
 		break;
@@ -867,25 +858,21 @@
 /**
  * Handle all incoming network traffic.
  */
-void NetClient::handle_network() {
+void GameClient::handle_network() {
 	// if this is an internet game, handle the metaserver network
 	if (internet_)
 		InternetGaming::ref().handle_metaserver_communication();
 	try {
-		while (d->sock != nullptr && SDLNet_CheckSockets(d->sockset, 0) > 0) {
-			// Perform only one read operation, then process all packets
-			// from this read. This ensures that we process DISCONNECT
-			// packets that are followed immediately by connection close.
-			if (!d->deserializer.read(d->sock)) {
-				disconnect("CONNECTION_LOST", "", false);
-				return;
-			}
-
-			// Process all the packets from the last read
-			while (d->sock && d->deserializer.avail()) {
-				RecvPacket packet(d->deserializer);
-				handle_packet(packet);
-			}
+		assert(d->net != nullptr);
+		// Check if the connection is still open
+		if (!d->net->is_connected()) {
+			disconnect("CONNECTION_LOST", "", false);
+			return;
+		}
+		// Process all available packets
+		RecvPacket packet;
+		while (d->net->try_receive(packet)) {
+			handle_packet(packet);
 		}
 	} catch (const DisconnectException& e) {
 		disconnect(e.what());
@@ -896,13 +883,14 @@
 	}
 }
 
-void NetClient::disconnect(const std::string& reason,
+void GameClient::disconnect(const std::string& reason,
                            const std::string& arg,
                            bool const sendreason,
                            bool const showmsg) {
 	log("[Client]: disconnect(%s, %s)\n", reason.c_str(), arg.c_str());
 
-	if (d->sock) {
+	assert(d->net != nullptr);
+	if (d->net->is_connected()) {
 		if (sendreason) {
 			SendPacket s;
 			s.unsigned_8(NETCMD_DISCONNECT);
@@ -911,12 +899,10 @@
 			if (!arg.empty()) {
 				s.string(arg);
 			}
-			s.send(d->sock);
+			d->net->send(s);
 		}
 
-		SDLNet_TCP_DelSocket(d->sockset, d->sock);
-		SDLNet_TCP_Close(d->sock);
-		d->sock = nullptr;
+		d->net->close();
 	}
 
 	bool const trysave = showmsg && d->game;

=== renamed file 'src/network/netclient.h' => 'src/network/gameclient.h'
--- src/network/netclient.h	2017-02-10 14:12:36 +0000
+++ src/network/gameclient.h	2017-05-09 19:19:42 +0000
@@ -17,30 +17,30 @@
  *
  */
 
-#ifndef WL_NETWORK_NETCLIENT_H
-#define WL_NETWORK_NETCLIENT_H
+#ifndef WL_NETWORK_GAMECLIENT_H
+#define WL_NETWORK_GAMECLIENT_H
 
 #include "chat/chat.h"
 #include "logic/game_controller.h"
 #include "logic/game_settings.h"
-#include "network/network.h"
+#include "network/netclient.h"
 
-struct NetClientImpl;
+struct GameClientImpl;
 
 // TODO(unknown): Use composition instead of inheritance
 /**
- * NetClient manages the lifetime of a network game in which this computer
+ * GameClient manages the lifetime of a network game in which this computer
  * participates as a client.
  *
  * This includes running the game setup screen and the actual game after
  * launch, as well as dealing with the actual network protocol.
  */
-struct NetClient : public GameController,
+struct GameClient : public GameController,
                    public GameSettingsProvider,
                    private SyncCallback,
                    public ChatProvider {
-	NetClient(IPaddress*, const std::string& playername, bool internet = false);
-	virtual ~NetClient();
+	GameClient(const std::string& host, const uint16_t port, const std::string& playername, bool internet = false);
+	virtual ~GameClient();
 
 	void run();
 
@@ -119,8 +119,8 @@
 	                bool showmsg = true);
 
 	NetTransferFile* file_;
-	NetClientImpl* d;
+	GameClientImpl* d;
 	bool internet_;
 };
 
-#endif  // end of include guard: WL_NETWORK_NETCLIENT_H
+#endif  // end of include guard: WL_NETWORK_GAMECLIENT_H

=== renamed file 'src/network/nethost.cc' => 'src/network/gamehost.cc'
--- src/network/nethost.cc	2017-02-28 08:20:01 +0000
+++ src/network/gamehost.cc	2017-05-09 19:19:42 +0000
@@ -17,7 +17,7 @@
  *
  */
 
-#include "network/nethost.h"
+#include "network/gamehost.h"
 
 #include <algorithm>
 #include <memory>
@@ -33,6 +33,7 @@
 #include "ai/computer_player.h"
 #include "base/i18n.h"
 #include "base/md5.h"
+#include "base/warning.h"
 #include "base/wexception.h"
 #include "build_info.h"
 #include "chat/chat.h"
@@ -49,6 +50,7 @@
 #include "map_io/widelands_map_loader.h"
 #include "network/constants.h"
 #include "network/internet_gaming.h"
+#include "network/nethost.h"
 #include "network/network_gaming_messages.h"
 #include "network/network_lan_promotion.h"
 #include "network/network_player_settings_backend.h"
@@ -64,7 +66,7 @@
 #include "wui/interactive_spectator.h"
 
 struct HostGameSettingsProvider : public GameSettingsProvider {
-	HostGameSettingsProvider(NetHost* const init_host) : host_(init_host), current_wincondition_(0) {
+	HostGameSettingsProvider(GameHost* const init_host) : host_(init_host), current_wincondition_(0) {
 	}
 	~HostGameSettingsProvider() {
 	}
@@ -273,13 +275,13 @@
 	}
 
 private:
-	NetHost* host_;
+	GameHost* host_;
 	int16_t current_wincondition_;
 	std::vector<std::string> wincondition_scripts_;
 };
 
 struct HostChatProvider : public ChatProvider {
-	HostChatProvider(NetHost* const init_host) : h(init_host), kickClient(0) {
+	HostChatProvider(GameHost* const init_host) : h(init_host), kickClient(0) {
 	}
 
 	void send(const std::string& msg) override {
@@ -455,7 +457,7 @@
 	}
 
 private:
-	NetHost* h;
+	GameHost* h;
 	std::vector<ChatMessage> messages;
 	std::string kickUser;
 	uint32_t kickClient;
@@ -463,8 +465,7 @@
 };
 
 struct Client {
-	TCPsocket sock;
-	Deserializer deserializer;
+	NetHost::ConId sock_id;
 	uint8_t playernum;
 	int16_t usernum;
 	std::string build_id;
@@ -479,7 +480,7 @@
 	time_t lastdelta;
 };
 
-struct NetHostImpl {
+struct GameHostImpl {
 	GameSettings settings;
 	std::string localplayername;
 	uint32_t localdesiredspeed;
@@ -488,8 +489,7 @@
 	NetworkPlayerSettingsBackend npsb;
 
 	LanGamePromoter* promoter;
-	TCPsocket svsock;
-	SDLNet_SocketSet sockset;
+	std::unique_ptr<NetHost> net;
 
 	/// List of connected clients. Note that clients are not in the same
 	/// order as players. In fact, a client must not be assigned to a player.
@@ -530,14 +530,13 @@
 	Md5Checksum syncreport;
 	bool syncreport_arrived;
 
-	NetHostImpl(NetHost* const h)
+	GameHostImpl(GameHost* const h)
 	   : localdesiredspeed(0),
 	     chat(h),
 	     hp(h),
 	     npsb(&hp),
 	     promoter(nullptr),
-	     svsock(nullptr),
-	     sockset(nullptr),
+	     net(),
 	     game(nullptr),
 	     pseudo_networktime(0),
 	     last_heartbeat(0),
@@ -552,8 +551,8 @@
 	}
 };
 
-NetHost::NetHost(const std::string& playername, bool internet)
-   : d(new NetHostImpl(this)), internet_(internet), forced_pause_(false) {
+GameHost::GameHost(const std::string& playername, bool internet)
+   : d(new GameHostImpl(this)), internet_(internet), forced_pause_(false) {
 	log("[Host]: starting up.\n");
 
 	if (internet) {
@@ -563,11 +562,13 @@
 	d->localplayername = playername;
 
 	// create a listening socket
-	IPaddress myaddr;
-	SDLNet_ResolveHost(&myaddr, nullptr, WIDELANDS_PORT);
-	d->svsock = SDLNet_TCP_Open(&myaddr);
-
-	d->sockset = SDLNet_AllocSocketSet(16);
+	d->net = NetHost::listen(WIDELANDS_PORT);
+	if (d->net == nullptr) {
+		// This might happen when the widelands socket is already in use
+		throw WLWarning(_("Failed to start the server!"),
+		                _("Widelands could not start a server.\n"
+		                  "Probably some other process is already running a server on our port."));
+	}
 	d->promoter = new LanGamePromoter();
 	d->game = nullptr;
 	d->pseudo_networktime = 0;
@@ -589,7 +590,7 @@
 	file_ = nullptr;  //  Initialize as 0 pointer - unfortunately needed in struct.
 }
 
-NetHost::~NetHost() {
+GameHost::~GameHost() {
 	clear_computer_players();
 
 	while (!d->clients.empty()) {
@@ -597,37 +598,33 @@
 		reaper();
 	}
 
-	SDLNet_FreeSocketSet(d->sockset);
-
 	// close all open sockets
-	if (d->svsock != nullptr)
-		SDLNet_TCP_Close(d->svsock);
-
+	d->net.reset();
 	delete d->promoter;
 	delete d;
 	delete file_;
 }
 
-const std::string& NetHost::get_local_playername() const {
+const std::string& GameHost::get_local_playername() const {
 	return d->localplayername;
 }
 
-int16_t NetHost::get_local_playerposition() {
+int16_t GameHost::get_local_playerposition() {
 	return d->settings.users.at(0).position;
 }
 
-void NetHost::clear_computer_players() {
+void GameHost::clear_computer_players() {
 	for (uint32_t i = 0; i < d->computerplayers.size(); ++i)
 		delete d->computerplayers.at(i);
 	d->computerplayers.clear();
 }
 
-void NetHost::init_computer_player(Widelands::PlayerNumber p) {
+void GameHost::init_computer_player(Widelands::PlayerNumber p) {
 	d->computerplayers.push_back(ComputerPlayer::get_implementation(d->game->get_player(p)->get_ai())
 	                                ->instantiate(*d->game, p));
 }
 
-void NetHost::init_computer_players() {
+void GameHost::init_computer_players() {
 	const Widelands::PlayerNumber nr_players = d->game->map().get_nrplayers();
 	iterate_players_existing_novar(p, nr_players, *d->game) {
 		if (p == d->settings.playernum + 1)
@@ -643,7 +640,7 @@
 	}
 }
 
-void NetHost::run() {
+void GameHost::run() {
 	// Fill the list of possible system messages
 	NetworkGamingMessages::fill_map();
 	FullscreenMenuLaunchMPG lm(&d->hp, this);
@@ -757,7 +754,7 @@
 	d->game = nullptr;
 }
 
-void NetHost::think() {
+void GameHost::think() {
 	handle_network();
 
 	if (d->game) {
@@ -794,7 +791,7 @@
 	}
 }
 
-void NetHost::send_player_command(Widelands::PlayerCommand& pc) {
+void GameHost::send_player_command(Widelands::PlayerCommand& pc) {
 	pc.set_duetime(d->committed_networktime + 1);
 
 	SendPacket s;
@@ -814,7 +811,7 @@
  * If it is a personal message it will only be send to the recipient and to
  * the sender (to show that the message was actually sent).
  */
-void NetHost::send(ChatMessage msg) {
+void GameHost::send(ChatMessage msg) {
 	if (msg.msg.empty())
 		return;
 
@@ -850,7 +847,7 @@
 				s.string(msg.msg);
 				s.unsigned_8(1);
 				s.string(msg.recipient);
-				s.send(d->clients.at(clientnum).sock);
+				d->net->send(d->clients.at(clientnum).sock_id, s);
 				log(
 				   "[Host]: personal chat: from %s to %s\n", msg.sender.c_str(), msg.recipient.c_str());
 			} else {
@@ -896,7 +893,7 @@
 					if (d->clients.at(j).usernum == static_cast<int16_t>(i))
 						break;
 				if (j < d->clients.size())
-					s.send(d->clients.at(j).sock);
+					d->net->send(d->clients.at(j).sock_id, s);
 				else
 					// Better no wexception it would break the whole game
 					log("WARNING: user was found but no client is connected to it!\n");
@@ -913,7 +910,7 @@
  *   -   -1 if no client was found
  *   -   -2 if the host is the client (has no client number)
  */
-int32_t NetHost::check_client(std::string name) {
+int32_t GameHost::check_client(std::string name) {
 	// Check if the client is the host him-/herself
 	if (d->localplayername == name) {
 		return -2;
@@ -943,13 +940,13 @@
 * If the host sends a chat message with formation /kick <name> <reason>
 * This function will handle this command and try to kick the user.
 */
-void NetHost::kick_user(uint32_t client, std::string reason) {
+void GameHost::kick_user(uint32_t client, std::string reason) {
 	disconnect_client(client, "KICKED", true, reason);
 }
 
 /// Split up a user entered string in "cmd", "arg1" and "arg2"
 /// \note the cmd must begin with "/"
-void NetHost::split_command_array(const std::string& cmdarray,
+void GameHost::split_command_array(const std::string& cmdarray,
                                   std::string& cmd,
                                   std::string& arg1,
                                   std::string& arg2) {
@@ -976,7 +973,7 @@
 		arg2 = "";
 }
 
-void NetHost::send_system_message_code(const std::string& code,
+void GameHost::send_system_message_code(const std::string& code,
                                        const std::string& a,
                                        const std::string& b,
                                        const std::string& c) {
@@ -998,19 +995,19 @@
 	d->chat.receive(msg);
 }
 
-int32_t NetHost::get_frametime() {
+int32_t GameHost::get_frametime() {
 	return d->time.time() - d->game->get_gametime();
 }
 
-GameController::GameType NetHost::get_game_type() {
+GameController::GameType GameHost::get_game_type() {
 	return GameController::GameType::NETHOST;
 }
 
-const GameSettings& NetHost::settings() {
+const GameSettings& GameHost::settings() {
 	return d->settings;
 }
 
-bool NetHost::can_launch() {
+bool GameHost::can_launch() {
 	if (d->settings.mapname.empty())
 		return false;
 	if (d->settings.players.size() < 1)
@@ -1036,7 +1033,7 @@
 	return one_not_closed;
 }
 
-void NetHost::set_map(const std::string& mapname,
+void GameHost::set_map(const std::string& mapname,
                       const std::string& mapfilename,
                       uint32_t const maxplayers,
                       bool const savegame) {
@@ -1144,7 +1141,7 @@
 		broadcast(s);
 }
 
-void NetHost::set_player_state(uint8_t const number,
+void GameHost::set_player_state(uint8_t const number,
                                PlayerSettings::State const state,
                                bool const host) {
 	if (number >= d->settings.players.size())
@@ -1202,7 +1199,7 @@
 	broadcast(s);
 }
 
-void NetHost::set_player_tribe(uint8_t const number,
+void GameHost::set_player_tribe(uint8_t const number,
                                const std::string& tribe,
                                bool const random_tribe) {
 	if (number >= d->settings.players.size())
@@ -1240,7 +1237,7 @@
 	log("Player %u attempted to change to tribe %s; not a valid tribe\n", number, tribe.c_str());
 }
 
-void NetHost::set_player_init(uint8_t const number, uint8_t const index) {
+void GameHost::set_player_init(uint8_t const number, uint8_t const index) {
 	if (number >= d->settings.players.size())
 		return;
 
@@ -1271,7 +1268,7 @@
 	NEVER_HERE();
 }
 
-void NetHost::set_player_ai(uint8_t number, const std::string& name, bool const random_ai) {
+void GameHost::set_player_ai(uint8_t number, const std::string& name, bool const random_ai) {
 	if (number >= d->settings.players.size())
 		return;
 
@@ -1287,7 +1284,7 @@
 	broadcast(s);
 }
 
-void NetHost::set_player_name(uint8_t const number, const std::string& name) {
+void GameHost::set_player_name(uint8_t const number, const std::string& name) {
 	if (number >= d->settings.players.size())
 		return;
 
@@ -1306,7 +1303,7 @@
 	broadcast(s);
 }
 
-void NetHost::set_player_closeable(uint8_t const number, bool closeable) {
+void GameHost::set_player_closeable(uint8_t const number, bool closeable) {
 	if (number >= d->settings.players.size())
 		return;
 
@@ -1321,7 +1318,7 @@
 	// uses it.
 }
 
-void NetHost::set_player_shared(uint8_t number, uint8_t shared) {
+void GameHost::set_player_shared(uint8_t number, uint8_t shared) {
 	if (number >= d->settings.players.size())
 		return;
 
@@ -1345,7 +1342,7 @@
 	broadcast(s);
 }
 
-void NetHost::set_player(uint8_t const number, const PlayerSettings& ps) {
+void GameHost::set_player(uint8_t const number, const PlayerSettings& ps) {
 	if (number >= d->settings.players.size())
 		return;
 
@@ -1360,11 +1357,12 @@
 	broadcast(s);
 }
 
-void NetHost::set_player_number(uint8_t const number) {
+void GameHost::set_player_number(uint8_t const number) {
 	switch_to_player(0, number);
 }
 
-void NetHost::set_win_condition_script(const std::string& wc) {
+
+void GameHost::set_win_condition_script(const std::string& wc) {
 	d->settings.win_condition_script = wc;
 
 	// Broadcast changes
@@ -1374,7 +1372,7 @@
 	broadcast(s);
 }
 
-void NetHost::switch_to_player(uint32_t user, uint8_t number) {
+void GameHost::switch_to_player(uint32_t user, uint8_t number) {
 	if (number < d->settings.players.size() &&
 	    (d->settings.players.at(number).state != PlayerSettings::stateOpen &&
 	     d->settings.players.at(number).state != PlayerSettings::stateHuman))
@@ -1422,7 +1420,7 @@
 	broadcast(s);
 }
 
-void NetHost::set_player_team(uint8_t number, Widelands::TeamNumber team) {
+void GameHost::set_player_team(uint8_t number, Widelands::TeamNumber team) {
 	if (number >= d->settings.players.size())
 		return;
 	d->settings.players.at(number).team = team;
@@ -1435,26 +1433,26 @@
 	broadcast(s);
 }
 
-void NetHost::set_multiplayer_game_settings() {
+void GameHost::set_multiplayer_game_settings() {
 	d->settings.scenario = false;
 	d->settings.multiplayer = true;
 }
 
-void NetHost::set_scenario(bool is_scenario) {
+void GameHost::set_scenario(bool is_scenario) {
 	d->settings.scenario = is_scenario;
 }
 
-uint32_t NetHost::real_speed() {
+uint32_t GameHost::real_speed() {
 	if (d->waiting)
 		return 0;
 	return d->networkspeed;
 }
 
-uint32_t NetHost::desired_speed() {
+uint32_t GameHost::desired_speed() {
 	return d->localdesiredspeed;
 }
 
-void NetHost::set_desired_speed(uint32_t const speed) {
+void GameHost::set_desired_speed(uint32_t const speed) {
 	if (speed != d->localdesiredspeed) {
 		d->localdesiredspeed = speed;
 		update_network_speed();
@@ -1462,30 +1460,31 @@
 }
 
 // Network games cannot be paused
-bool NetHost::is_paused() {
+bool GameHost::is_paused() {
 	return false;
 }
 
-void NetHost::set_paused(bool /* paused */) {
+void GameHost::set_paused(bool /* paused */) {
 }
 
 // Send the packet to all properly connected clients
-void NetHost::broadcast(SendPacket& packet) {
+void GameHost::broadcast(SendPacket& packet) {
 	for (const Client& client : d->clients) {
 		if (client.playernum != UserSettings::not_connected()) {
-			packet.send(client.sock);
+			assert(client.sock_id > 0);
+			d->net->send(client.sock_id, packet);
 		}
 	}
 }
 
-void NetHost::write_setting_map(SendPacket& packet) {
+void GameHost::write_setting_map(SendPacket& packet) {
 	packet.string(d->settings.mapname);
 	packet.string(d->settings.mapfilename);
 	packet.unsigned_8(d->settings.savegame ? 1 : 0);
 	packet.unsigned_8(d->settings.scenario ? 1 : 0);
 }
 
-void NetHost::write_setting_player(SendPacket& packet, uint8_t const number) {
+void GameHost::write_setting_player(SendPacket& packet, uint8_t const number) {
 	PlayerSettings& player = d->settings.players.at(number);
 	packet.unsigned_8(static_cast<uint8_t>(player.state));
 	packet.string(player.name);
@@ -1498,19 +1497,19 @@
 	packet.unsigned_8(player.shared_in);
 }
 
-void NetHost::write_setting_all_players(SendPacket& packet) {
+void GameHost::write_setting_all_players(SendPacket& packet) {
 	packet.unsigned_8(d->settings.players.size());
 	for (uint8_t i = 0; i < d->settings.players.size(); ++i)
 		write_setting_player(packet, i);
 }
 
-void NetHost::write_setting_user(SendPacket& packet, uint32_t const number) {
+void GameHost::write_setting_user(SendPacket& packet, uint32_t const number) {
 	packet.string(d->settings.users.at(number).name);
 	packet.signed_32(d->settings.users.at(number).position);
 	packet.unsigned_8(d->settings.users.at(number).ready ? 1 : 0);
 }
 
-void NetHost::write_setting_all_users(SendPacket& packet) {
+void GameHost::write_setting_all_users(SendPacket& packet) {
 	packet.unsigned_8(d->settings.users.size());
 	for (uint32_t i = 0; i < d->settings.users.size(); ++i)
 		write_setting_user(packet, i);
@@ -1521,7 +1520,7 @@
 *
 * \returns true if the data was written, else false
 */
-bool NetHost::write_map_transfer_info(SendPacket& s, std::string mapfilename) {
+bool GameHost::write_map_transfer_info(SendPacket& s, std::string mapfilename) {
 	// TODO(unknown): not yet able to handle directory type maps / savegames
 	if (g_fs->is_directory(mapfilename)) {
 		log("Map/Save is a directory! No way for making it available a.t.m.!\n");
@@ -1544,7 +1543,7 @@
  *
  * \return a name for the given player.
  */
-std::string NetHost::get_computer_player_name(uint8_t const playernum) {
+std::string GameHost::get_computer_player_name(uint8_t const playernum) {
 	std::string name;
 	uint32_t suffix = playernum;
 	do {
@@ -1559,7 +1558,7 @@
  * If \p ignoreplayer < UserSettings::highest_playernum(), the user with this
  * number will be ignored.
  */
-bool NetHost::has_user_name(const std::string& name, uint8_t ignoreplayer) {
+bool GameHost::has_user_name(const std::string& name, uint8_t ignoreplayer) {
 	for (uint32_t i = 0; i < d->settings.users.size(); ++i)
 		if (i != ignoreplayer && d->settings.users.at(i).name == name)
 			return true;
@@ -1576,13 +1575,13 @@
 }
 
 /// Respond to a client's Hello message.
-void NetHost::welcome_client(uint32_t const number, std::string& playername) {
+void GameHost::welcome_client(uint32_t const number, std::string& playername) {
 	assert(number < d->clients.size());
 
 	Client& client = d->clients.at(number);
 
 	assert(client.playernum == UserSettings::not_connected());
-	assert(client.sock);
+	assert(client.sock_id > 0);
 
 	// The client gets its own initial data set.
 	client.playernum = UserSettings::none();
@@ -1624,23 +1623,22 @@
 	s.unsigned_8(NETCMD_HELLO);
 	s.unsigned_8(NETWORK_PROTOCOL_VERSION);
 	s.unsigned_32(client.usernum);
-	s.send(client.sock);
-
+	d->net->send(client.sock_id, s);
 	// even if the network protocol is the same, the data might be different.
 	if (client.build_id != build_id())
 		send_system_message_code("DIFFERENT_WL_VERSION", effective_name, client.build_id, build_id());
-
 	// Send information about currently selected map / savegame
 	s.reset();
+
 	s.unsigned_8(NETCMD_SETTING_MAP);
 	write_setting_map(s);
-	s.send(client.sock);
+	d->net->send(client.sock_id, s);
 
 	// If possible, offer the map / savegame as transfer
 	if (file_) {
 		s.reset();
 		if (write_map_transfer_info(s, file_->filename))
-			s.send(client.sock);
+			d->net->send(client.sock_id, s);
 	}
 
 	//  Send the tribe information to the new client.
@@ -1654,22 +1652,22 @@
 		for (uint8_t j = 0; j < nr_initializations; ++j)
 			s.string(d->settings.tribes[i].initializations[j].script);
 	}
-	s.send(client.sock);
+	d->net->send(client.sock_id, s);
 
 	s.reset();
 	s.unsigned_8(NETCMD_SETTING_ALLPLAYERS);
 	write_setting_all_players(s);
-	s.send(client.sock);
+	d->net->send(client.sock_id, s);
 
 	s.reset();
 	s.unsigned_8(NETCMD_SETTING_ALLUSERS);
 	write_setting_all_users(s);
-	s.send(client.sock);
+	d->net->send(client.sock_id, s);
 
 	s.reset();
 	s.unsigned_8(NETCMD_WIN_CONDITION);
 	s.string(d->settings.win_condition_script);
-	s.send(client.sock);
+	d->net->send(client.sock_id, s);
 
 	// Broadcast new information about the player to everybody
 	s.reset();
@@ -1688,7 +1686,7 @@
 	send_system_message_code("CLIENT_HAS_JOINED_GAME", effective_name);
 }
 
-void NetHost::committed_network_time(int32_t const time) {
+void GameHost::committed_network_time(int32_t const time) {
 	assert(time - d->committed_networktime > 0);
 
 	d->committed_networktime = time;
@@ -1699,7 +1697,7 @@
 		request_sync_reports();
 }
 
-void NetHost::receive_client_time(uint32_t const number, int32_t const time) {
+void GameHost::receive_client_time(uint32_t const number, int32_t const time) {
 	assert(number < d->clients.size());
 
 	Client& client = d->clients.at(number);
@@ -1723,7 +1721,7 @@
 	}
 }
 
-void NetHost::check_hung_clients() {
+void GameHost::check_hung_clients() {
 	assert(d->game != nullptr);
 
 	int nrdelayed = 0;
@@ -1788,7 +1786,7 @@
 	}
 }
 
-void NetHost::broadcast_real_speed(uint32_t const speed) {
+void GameHost::broadcast_real_speed(uint32_t const speed) {
 	assert(speed <= std::numeric_limits<uint16_t>::max());
 
 	SendPacket s;
@@ -1802,7 +1800,7 @@
  * given the desired speed of all clients.
  *
  * This function is supposed to be the only code that ever changes
- * \ref NetHostImpl::networkspeed.
+ * \ref GameHostImpl::networkspeed.
  *
  * The current implementation picks the median, or the average of
  * lower and upper median.
@@ -1816,7 +1814,7 @@
  * network games, as sudden pauses would be distracting to other players. A
  * hard interruption of the game can be achieved with the forced pause.
  */
-void NetHost::update_network_speed() {
+void GameHost::update_network_speed() {
 	uint32_t const oldnetworkspeed = d->networkspeed;
 
 	// First check if a pause was forced by the host
@@ -1859,7 +1857,7 @@
 /**
  * Request sync reports from all clients at the next possible time.
  */
-void NetHost::request_sync_reports() {
+void GameHost::request_sync_reports() {
 	assert(!d->syncreport_pending);
 
 	d->syncreport_pending = true;
@@ -1884,7 +1882,7 @@
 /**
  * Check whether all sync reports have arrived, and if so, compare.
  */
-void NetHost::check_sync_reports() {
+void GameHost::check_sync_reports() {
 	assert(d->syncreport_pending);
 
 	if (!d->syncreport_arrived)
@@ -1925,7 +1923,7 @@
 	}
 }
 
-void NetHost::syncreport() {
+void GameHost::syncreport() {
 	assert(d->game->get_gametime() == static_cast<uint32_t>(d->syncreport_time));
 
 	d->syncreport = d->game->get_sync_hash();
@@ -1934,21 +1932,16 @@
 	check_sync_reports();
 }
 
-void NetHost::handle_network() {
-	TCPsocket sock;
+void GameHost::handle_network() {
 
 	if (d->promoter != nullptr)
 		d->promoter->run();
 
+
 	// Check for new connections.
-	while (d->svsock != nullptr && (sock = SDLNet_TCP_Accept(d->svsock)) != nullptr) {
-		log("[Host]: Received a connection request\n");
-
-		SDLNet_TCP_AddSocket(d->sockset, sock);
-
-		Client peer;
-
-		peer.sock = sock;
+	Client peer;
+	assert(d->net != nullptr);
+	while (d->net->try_accept(peer.sock_id)) {
 		peer.playernum = UserSettings::not_connected();
 		peer.syncreport_arrived = false;
 		peer.desiredspeed = 1000;
@@ -1969,32 +1962,25 @@
 			send(msgs.at(i));
 	}
 
+	for (size_t i = 0; i < d->clients.size(); ++i) {
+		if (!d->net->is_connected(d->clients.at(i).sock_id))
+			disconnect_client(i, "CONNECTION_LOST", false);
+	}
+
 	// Check if we hear anything from our clients
-	while (SDLNet_CheckSockets(d->sockset, 0) > 0) {
-		for (size_t i = 0; i < d->clients.size(); ++i) {
-			try {
-				Client& client = d->clients.at(i);
-
-				while (client.sock && SDLNet_SocketReady(client.sock)) {
-					if (!client.deserializer.read(client.sock)) {
-						disconnect_client(i, "CONNECTION_LOST", false);
-						break;
-					}
-
-					//  Handle all available packets immediately after each read, so
-					//  that we do not miss any commands (especially DISCONNECT...).
-					while (client.sock && client.deserializer.avail()) {
-						RecvPacket r(client.deserializer);
-						handle_packet(i, r);
-					}
-				}
-			} catch (const DisconnectException& e) {
-				disconnect_client(i, e.what());
-			} catch (const ProtocolException& e) {
-				disconnect_client(i, "PROTOCOL_EXCEPTION", true, e.what());
-			} catch (const std::exception& e) {
-				disconnect_client(i, "MALFORMED_COMMANDS", true, e.what());
+	RecvPacket packet;
+	for (size_t i = 0; i < d->clients.size(); ++i) {
+		try {
+			while (d->net->try_receive(d->clients.at(i).sock_id, packet)) {
+				handle_packet(i, packet);
 			}
+		// Thrown by handle_packet()
+		} catch (const DisconnectException& e) {
+			disconnect_client(i, e.what());
+		} catch (const ProtocolException& e) {
+			disconnect_client(i, "PROTOCOL_EXCEPTION", true, e.what());
+		} catch (const std::exception& e) {
+			disconnect_client(i, "MALFORMED_COMMANDS", true, e.what());
 		}
 	}
 
@@ -2019,7 +2005,7 @@
  * \param i the client number
  * \param r the received packet
  */
-void NetHost::handle_packet(uint32_t const i, RecvPacket& r) {
+void GameHost::handle_packet(uint32_t const i, RecvPacket& r) {
 	Client& client = d->clients.at(i);
 	uint8_t const cmd = r.unsigned_8();
 
@@ -2041,13 +2027,12 @@
 			// Send PING back
 			SendPacket s;
 			s.unsigned_8(NETCMD_METASERVER_PING);
-			s.send(client.sock);
+			d->net->send(client.sock_id, s);
 
 			// Remove metaserver from list of clients
 			client.playernum = UserSettings::not_connected();
-			SDLNet_TCP_DelSocket(d->sockset, client.sock);
-			SDLNet_TCP_Close(client.sock);
-			client.sock = nullptr;
+			d->net->close(client.sock_id);
+			client.sock_id = 0;
 			return;
 		}
 
@@ -2207,7 +2192,7 @@
 			throw DisconnectException("REQUEST_OF_N_E_FILE");
 		send_system_message_code(
 		   "STARTED_SENDING_FILE", file_->filename, d->settings.users.at(client.usernum).name);
-		send_file_part(client.sock, 0);
+		send_file_part(client.sock_id, 0);
 		// Remember client as "currently receiving file"
 		d->settings.users[client.usernum].ready = false;
 		SendPacket s;
@@ -2246,7 +2231,7 @@
 			send_system_message_code("SENDING_FILE_PART",
 			                         (boost::format("%i/%i") % part % (file_->parts.size() + 1)).str(),
 			                         file_->filename, d->settings.users.at(client.usernum).name);
-		send_file_part(client.sock, part);
+		send_file_part(client.sock_id, part);
 		break;
 	}
 
@@ -2255,7 +2240,7 @@
 	}
 }
 
-void NetHost::send_file_part(TCPsocket csock, uint32_t part) {
+void GameHost::send_file_part(NetHost::ConId csock_id, uint32_t part) {
 	assert(part < file_->parts.size());
 
 	uint32_t left = file_->bytes - NETFILEPARTSIZE * part;
@@ -2267,10 +2252,10 @@
 	s.unsigned_32(part);
 	s.unsigned_32(size);
 	s.data(file_->parts[part].part, size);
-	s.send(csock);
+	d->net->send(csock_id, s);
 }
 
-void NetHost::disconnect_player_controller(uint8_t const number, const std::string& name) {
+void GameHost::disconnect_player_controller(uint8_t const number, const std::string& name) {
 	log("[Host]: disconnect_player_controller(%u, %s)\n", number, name.c_str());
 
 	for (uint32_t i = 0; i < d->settings.users.size(); ++i) {
@@ -2294,7 +2279,7 @@
 		init_computer_player(number + 1);
 }
 
-void NetHost::disconnect_client(uint32_t const number,
+void GameHost::disconnect_client(uint32_t const number,
                                 const std::string& reason,
                                 bool const sendreason,
                                 const std::string& arg) {
@@ -2330,7 +2315,7 @@
 
 	log("[Host]: disconnect_client(%u, %s, %s)\n", number, reason.c_str(), arg.c_str());
 
-	if (client.sock) {
+	if (client.sock_id > 0) {
 		if (sendreason) {
 			SendPacket s;
 			s.unsigned_8(NETCMD_DISCONNECT);
@@ -2338,12 +2323,11 @@
 			s.string(reason);
 			if (!arg.empty())
 				s.string(arg);
-			s.send(client.sock);
+			d->net->send(client.sock_id, s);
 		}
 
-		SDLNet_TCP_DelSocket(d->sockset, client.sock);
-		SDLNet_TCP_Close(client.sock);
-		client.sock = nullptr;
+		d->net->close(client.sock_id);
+		client.sock_id = 0;
 	}
 
 	if (d->game) {
@@ -2358,16 +2342,16 @@
  * Calls this when you're certain that nobody is holding any client indices or
  * iterators, since this function will invalidate them.
  */
-void NetHost::reaper() {
+void GameHost::reaper() {
 	uint32_t index = 0;
 	while (index < d->clients.size())
-		if (d->clients.at(index).sock)
+		if (d->clients.at(index).sock_id > 0)
 			++index;
 		else
 			d->clients.erase(d->clients.begin() + index);
 }
 
-void NetHost::report_result(uint8_t p_nr,
+void GameHost::report_result(uint8_t p_nr,
                             Widelands::PlayerEndResult result,
                             const std::string& info) {
 	// Send to game
@@ -2393,6 +2377,6 @@
 		}
 	}
 
-	log("NetHost::report_result(%d, %u, %s)\n", player->player_number(),
+	log("GameHost::report_result(%d, %u, %s)\n", player->player_number(),
 	    static_cast<uint8_t>(result), info.c_str());
 }

=== renamed file 'src/network/nethost.h' => 'src/network/gamehost.h'
--- src/network/nethost.h	2017-02-10 14:12:36 +0000
+++ src/network/gamehost.h	2017-05-09 19:19:42 +0000
@@ -17,8 +17,8 @@
  *
  */
 
-#ifndef WL_NETWORK_NETHOST_H
-#define WL_NETWORK_NETHOST_H
+#ifndef WL_NETWORK_GAMEHOST_H
+#define WL_NETWORK_GAMEHOST_H
 
 #include "logic/game_controller.h"
 #include "logic/game_settings.h"
@@ -26,19 +26,19 @@
 #include "network/network.h"
 
 struct ChatMessage;
-struct NetHostImpl;
+struct GameHostImpl;
 struct Client;
 
 /**
- * NetHost manages the lifetime of a network game in which this computer
+ * GameHost manages the lifetime of a network game in which this computer
  * acts as the host.
  *
  * This includes running the game setup screen and the actual game after
  * launch, as well as dealing with the actual network protocol.
  */
-struct NetHost : public GameController, private SyncCallback {
-	NetHost(const std::string& playername, bool internet = false);
-	virtual ~NetHost();
+struct GameHost : public GameController, private SyncCallback {
+	GameHost(const std::string& playername, bool internet = false);
+	virtual ~GameHost();
 
 	void run();
 	const std::string& get_local_playername() const;
@@ -125,7 +125,7 @@
 
 	void handle_packet(uint32_t i, RecvPacket&);
 	void handle_network();
-	void send_file_part(TCPsocket, uint32_t);
+	void send_file_part(uint32_t, uint32_t);
 
 	void check_hung_clients();
 	void broadcast_real_speed(uint32_t speed);
@@ -153,9 +153,9 @@
 	void reaper();
 
 	NetTransferFile* file_;
-	NetHostImpl* d;
+	GameHostImpl* d;
 	bool internet_;
 	bool forced_pause_;
 };
 
-#endif  // end of include guard: WL_NETWORK_NETHOST_H
+#endif  // end of include guard: WL_NETWORK_GAMEHOST_H

=== modified file 'src/network/internet_gaming.cc'
--- src/network/internet_gaming.cc	2017-02-10 15:37:44 +0000
+++ src/network/internet_gaming.cc	2017-05-09 19:19:42 +0000
@@ -34,8 +34,7 @@
 /// will ensure
 /// that only one instance is running at time.
 InternetGaming::InternetGaming()
-   : sock_(nullptr),
-     sockset_(nullptr),
+   : net(),
      state_(OFFLINE),
      reg_(false),
      port_(INTERNET_GAMING_PORT),
@@ -58,8 +57,7 @@
 
 /// resets all stored variables without the chat messages for a clean new login (not relogin)
 void InternetGaming::reset() {
-	sock_ = nullptr;
-	sockset_ = nullptr;
+	net.reset();
 	state_ = OFFLINE;
 	pwd_ = "";
 	reg_ = false;
@@ -97,27 +95,13 @@
 void InternetGaming::initialize_connection() {
 	// First of all try to connect to the metaserver
 	log("InternetGaming: Connecting to the metaserver.\n");
-	IPaddress peer;
-	if (hostent* const he = gethostbyname(meta_.c_str())) {
-		peer.host = (reinterpret_cast<in_addr*>(he->h_addr_list[0]))->s_addr;
-		DIAG_OFF("-Wold-style-cast")
-		peer.port = htons(port_);
-		DIAG_ON("-Wold-style-cast")
-	} else
-		throw WLWarning(
-		   _("Connection problem"), "%s", _("Widelands could not connect to the metaserver."));
-
-	SDLNet_ResolveHost(&peer, meta_.c_str(), port_);
-	sock_ = SDLNet_TCP_Open(&peer);
-	if (sock_ == nullptr)
+	net = NetClient::connect(meta_, port_);
+	if (!net || !net->is_connected())
 		throw WLWarning(_("Could not establish connection to host"),
 		                _("Widelands could not establish a connection to the given address.\n"
 		                  "Either there was no metaserver running at the supposed port or\n"
 		                  "your network setup is broken."));
 
-	sockset_ = SDLNet_AllocSocketSet(1);
-	SDLNet_TCP_AddSocket(sockset_, sock_);
-
 	// Of course not 100% true, but we just care about an answer at all, so we reset this tracker
 	lastping_ = time(nullptr);
 }
@@ -148,7 +132,7 @@
 	s.string(bool2str(reg));
 	if (reg)
 		s.string(pwd);
-	s.send(sock_);
+	net->send(s);
 
 	// Now let's see, whether the metaserver is answering
 	uint32_t const secs = time(nullptr);
@@ -191,7 +175,7 @@
 	s.string(bool2str(reg_));
 	if (reg_)
 		s.string(pwd_);
-	s.send(sock_);
+	net->send(s);
 
 	// Now let's see, whether the metaserver is answering
 	uint32_t const secs = time(nullptr);
@@ -235,7 +219,7 @@
 	SendPacket s;
 	s.string(IGPCMD_DISCONNECT);
 	s.string(msgcode);
-	s.send(sock_);
+	net->send(s);
 
 	const std::string& msg = InternetGamingMessages::get_message(msgcode);
 	log("InternetGaming: logout(%s)\n", msg.c_str());
@@ -278,20 +262,16 @@
 	if (error())
 		return;
 	try {
-		while (sock_ != nullptr && SDLNet_CheckSockets(sockset_, 0) > 0) {
-			// Perform only one read operation, then process all packets
-			// from this read. This ensures that we process DISCONNECT
-			// packets that are followed immediately by connection close.
-			if (!deserializer_.read(sock_)) {
-				handle_failed_read();
-				return;
-			}
-
-			// Process all the packets from the last read
-			while (sock_ && deserializer_.avail()) {
-				RecvPacket packet(deserializer_);
-				handle_packet(packet);
-			}
+		assert(net != nullptr);
+		// Check if the connection is still open
+		if (!net->is_connected()) {
+			handle_failed_read();
+			return;
+		}
+		// Process all available packets
+		RecvPacket packet;
+		while (net->try_receive(packet)) {
+			handle_packet(packet);
 		}
 	} catch (const std::exception& e) {
 		logout((boost::format(_("Something went wrong: %s")) % e.what()).str());
@@ -303,7 +283,7 @@
 		if (clientupdateonmetaserver_) {
 			SendPacket s;
 			s.string(IGPCMD_CLIENTS);
-			s.send(sock_);
+			net->send(s);
 
 			clientupdateonmetaserver_ = false;
 		}
@@ -311,7 +291,7 @@
 		if (gameupdateonmetaserver_) {
 			SendPacket s;
 			s.string(IGPCMD_GAMES);
-			s.send(sock_);
+			net->send(s);
 
 			gameupdateonmetaserver_ = false;
 		}
@@ -434,7 +414,7 @@
 			// Client received a PING and should immediately PONG as requested
 			SendPacket s;
 			s.string(IGPCMD_PONG);
-			s.send(sock_);
+			net->send(s);
 
 			lastping_ = time(nullptr);
 		}
@@ -618,7 +598,7 @@
 	SendPacket s;
 	s.string(IGPCMD_GAME_CONNECT);
 	s.string(gamename);
-	s.send(sock_);
+	net->send(s);
 	gamename_ = gamename;
 	log("InternetGaming: Client tries to join a game with the name %s\n", gamename_.c_str());
 	state_ = IN_GAME;
@@ -637,7 +617,7 @@
 	s.string(IGPCMD_GAME_OPEN);
 	s.string(gamename_);
 	s.string("1024");  // Used to be maxclients, no longer used.
-	s.send(sock_);
+	net->send(s);
 	log("InternetGaming: Client opened a game with the name %s.\n", gamename_.c_str());
 	state_ = IN_GAME;
 
@@ -653,7 +633,7 @@
 
 	SendPacket s;
 	s.string(IGPCMD_GAME_START);
-	s.send(sock_);
+	net->send(s);
 	log("InternetGaming: Client announced the start of the game %s.\n", gamename_.c_str());
 
 	// From now on we wait for a reply from the metaserver
@@ -670,7 +650,7 @@
 
 	SendPacket s;
 	s.string(IGPCMD_GAME_DISCONNECT);
-	s.send(sock_);
+	net->send(s);
 
 	gameip_ = "";
 	state_ = LOBBY;
@@ -767,14 +747,14 @@
 			}
 			// send the request to change the motd
 			m.string(arg);
-			m.send(sock_);
+			net->send(s);
 			return;
 		} else if (cmd == "announcement") {
 			// send the request to change the motd
 			SendPacket m;
 			m.string(IGPCMD_ANNOUNCEMENT);
 			m.string(arg);
-			m.send(sock_);
+			net->send(s);
 			return;
 		} else
 			// let everything else pass
@@ -786,7 +766,7 @@
 		s.string("");
 	}
 
-	s.send(sock_);
+	net->send(s);
 }
 
 /**
@@ -827,7 +807,7 @@
 
 	receive(c);
 	if (system && (state_ == IN_GAME)) {
-		// Save system chat messages seperately as well, so the nethost can import and show them in
+		// Save system chat messages separately as well, so the nethost can import and show them in
 		// game;
 		c.msg = "METASERVER: " + msg;
 		ingame_system_chat_.push_back(c);

=== modified file 'src/network/internet_gaming.h'
--- src/network/internet_gaming.h	2017-01-25 18:55:59 +0000
+++ src/network/internet_gaming.h	2017-05-09 19:19:42 +0000
@@ -20,6 +20,7 @@
 #ifndef WL_NETWORK_INTERNET_GAMING_H
 #define WL_NETWORK_INTERNET_GAMING_H
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -31,6 +32,7 @@
 #include "build_info.h"
 #include "chat/chat.h"
 #include "network/internet_gaming_protocol.h"
+#include "network/netclient.h"
 #include "network/network.h"
 #include "network/network_lan_promotion.h"
 
@@ -158,14 +160,8 @@
 
 	void format_and_add_chat(std::string from, std::string to, bool system, std::string msg);
 
-	/// The socket that connects us to the host
-	TCPsocket sock_;
-
-	/// Socket set used for selection
-	SDLNet_SocketSet sockset_;
-
-	/// Deserializer acts as a buffer for packets (reassembly/splitting up)
-	Deserializer deserializer_;
+	/// The connection to the metaserver
+	std::unique_ptr<NetClient> net;
 
 	/// Current state of this class
 	enum { OFFLINE, CONNECTING, LOBBY, IN_GAME, COMMUNICATION_ERROR } state_;

=== added file 'src/network/netclient.cc'
--- src/network/netclient.cc	1970-01-01 00:00:00 +0000
+++ src/network/netclient.cc	2017-05-09 19:19:42 +0000
@@ -0,0 +1,102 @@
+#include "network/netclient.h"
+
+#include <memory>
+
+#include <SDL_net.h>
+
+#include "base/log.h"
+
+class NetClientImpl {
+	public:
+		NetClientImpl()
+			: sock(nullptr), sockset(nullptr), deserializer() {
+		}
+
+		/// The socket that connects us to the host
+		TCPsocket sock;
+
+		/// Socket set used for selection
+		SDLNet_SocketSet sockset;
+
+		/// Deserializer acts as a buffer for packets (reassembly/splitting up)
+		Deserializer deserializer;
+};
+
+std::unique_ptr<NetClient> NetClient::connect(const std::string& ip_address, const uint16_t port) {
+	std::unique_ptr<NetClient> ptr(new NetClient(ip_address, port));
+	if (ptr->is_connected()) {
+		return ptr;
+	} else {
+		ptr.reset();
+		return ptr;
+	}
+}
+
+NetClient::~NetClient() {
+	if (is_connected())
+		close();
+	if (d->sockset != nullptr)
+		SDLNet_FreeSocketSet(d->sockset);
+	delete d;
+}
+
+bool NetClient::is_connected() const {
+	return d->sock != nullptr;
+}
+
+void NetClient::close() {
+	if (!is_connected())
+		return;
+	SDLNet_TCP_DelSocket(d->sockset, d->sock);
+	SDLNet_TCP_Close(d->sock);
+	d->sock = nullptr;
+}
+
+bool NetClient::try_receive(RecvPacket& packet) {
+	if (!is_connected())
+		return false;
+
+	uint8_t buffer[512];
+	while (SDLNet_CheckSockets(d->sockset, 0) > 0) {
+
+		const int32_t bytes = SDLNet_TCP_Recv(d->sock, buffer, sizeof(buffer));
+		if (bytes <= 0) {
+			// Error while receiving
+			close();
+			return false;
+		}
+
+		d->deserializer.read_data(buffer, bytes);
+	}
+	// Get one packet from the deserializer
+	if (d->deserializer.write_packet(packet)) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+void NetClient::send(const SendPacket& packet) {
+	if (is_connected()) {
+		SDLNet_TCP_Send(d->sock, packet.get_data(), packet.size());
+	}
+}
+
+NetClient::NetClient(const std::string& ip_address, const uint16_t port)
+	: d(new NetClientImpl) {
+
+	IPaddress addr;
+	if (SDLNet_ResolveHost(&addr, ip_address.c_str(), port) != 0) {
+		log("[Client]: Failed to resolve host address %s:%u.\n", ip_address.c_str(), port);
+		return;
+	}
+	log("[Client]: Trying to connect to %s:%u ... ", ip_address.c_str(), port);
+	d->sock = SDLNet_TCP_Open(&addr);
+	if (is_connected()) {
+		log("success\n");
+		d->sockset = SDLNet_AllocSocketSet(1);
+		SDLNet_TCP_AddSocket(d->sockset, d->sock);
+	} else {
+		log("failed\n");
+	}
+}

=== added file 'src/network/netclient.h'
--- src/network/netclient.h	1970-01-01 00:00:00 +0000
+++ src/network/netclient.h	2017-05-09 19:19:42 +0000
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008-2017 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef WL_NETWORK_NETCLIENT_H
+#define WL_NETWORK_NETCLIENT_H
+
+#include <memory>
+
+#include "network/network.h"
+
+class NetClientImpl;
+
+/**
+ * GameClient manages the lifetime of a network game in which this computer
+ * participates as a client.
+ *
+ * This includes running the game setup screen and the actual game after
+ * launch, as well as dealing with the actual network protocol.
+ */
+class NetClient {
+	public:
+		/**
+		 * Tries to establish a connection to the given host.
+		 * @param ip_address A hostname or an IPv4 address as string.
+		 * @param port The port to connect to.
+		 * @return A pointer to a connected \c NetClient object or an invalid pointer if the connection failed.
+		 */
+		static std::unique_ptr<NetClient> connect(const std::string& ip_address,
+												const uint16_t port);
+
+		/**
+		 * Closes the connection.
+		 * If you want to send a goodbye-message to the host, do so before freeing the object.
+		 */
+		~NetClient();
+
+		/**
+		 * Returns whether the client is connected.
+		 * @return \c true if the connection is open, \c false otherwise.
+		 */
+		bool is_connected() const;
+
+		/**
+		 * Closes the connection.
+		 * If you want to send a goodbye-message to the host, do so before calling this.
+		 */
+		void close();
+
+		/**
+		 * Tries to receive a packet.
+		 * @param packet A packet that should be overwritten with the received data.
+		 * @return \c true if a packet is available, \c false otherwise.
+		 *   The given packet is only modified when \c true is returned.
+		 *   Calling this on a closed connection will return false.
+		 */
+		bool try_receive(RecvPacket& packet);
+
+		/**
+		 * Sends a packet.
+		 * Calling this on a closed connection will silently fail.
+		 * @param packet The packet to send.
+		 */
+		 void send(const SendPacket& packet);
+
+	private:
+		NetClient(const std::string& ip_address, const uint16_t port);
+
+		NetClientImpl *d;
+};
+
+#endif  // end of include guard: WL_NETWORK_NETCLIENT_H

=== added file 'src/network/nethost.cc'
--- src/network/nethost.cc	1970-01-01 00:00:00 +0000
+++ src/network/nethost.cc	2017-05-09 19:19:42 +0000
@@ -0,0 +1,143 @@
+#include "network/nethost.h"
+
+#include <map>
+#include <memory>
+
+#include <SDL_net.h>
+
+#include "base/log.h"
+
+class NetHostImpl {
+	public:
+
+		class Client {
+			public:
+				Client(TCPsocket sock)
+					: socket(sock), deserializer() {
+				}
+				TCPsocket socket;
+				Deserializer deserializer;
+		};
+
+		NetHostImpl()
+			: svsock(nullptr), sockset(nullptr), next_id(1) {
+		}
+
+		TCPsocket svsock;
+		SDLNet_SocketSet sockset;
+		std::map<NetHost::ConId, NetHostImpl::Client> clients;
+		NetHost::ConId next_id;
+};
+
+std::unique_ptr<NetHost> NetHost::listen(const uint16_t port) {
+	std::unique_ptr<NetHost> ptr(new NetHost(port));
+	if (ptr->is_listening()) {
+		return ptr;
+	} else {
+		ptr.reset();
+		return ptr;
+	}
+}
+
+NetHost::~NetHost() {
+	stop_listening();
+	while (!d->clients.empty()) {
+		close(d->clients.begin()->first);
+	}
+	SDLNet_FreeSocketSet(d->sockset);
+	delete d;
+}
+
+
+bool NetHost::is_listening() const {
+	return d->svsock != nullptr;
+}
+
+bool NetHost::is_connected(const ConId id) const {
+	return d->clients.count(id) > 0;
+}
+
+void NetHost::stop_listening() {
+	if (!is_listening())
+		return;
+	SDLNet_TCP_DelSocket(d->sockset, d->svsock);
+	SDLNet_TCP_Close(d->svsock);
+	d->svsock = nullptr;
+}
+
+void NetHost::close(const ConId id) {
+	auto iter_client = d->clients.find(id);
+	if (iter_client == d->clients.end()) {
+		// Not connected anyway
+		return;
+	}
+	SDLNet_TCP_DelSocket(d->sockset, iter_client->second.socket);
+	SDLNet_TCP_Close(iter_client->second.socket);
+	d->clients.erase(iter_client);
+}
+
+bool NetHost::try_accept(ConId& new_id) {
+	if (!is_listening())
+		return false;
+
+	TCPsocket sock = SDLNet_TCP_Accept(d->svsock);
+	// No client wants to connect
+	if (sock == nullptr)
+		return false;
+	SDLNet_TCP_AddSocket(d->sockset, sock);
+	ConId id = d->next_id++;
+	assert(id > 0);
+	assert(d->clients.count(id) == 0);
+	d->clients.insert(std::make_pair(id, NetHostImpl::Client(sock)));
+	assert(d->clients.count(id) == 1);
+	new_id = id;
+	return true;
+}
+
+bool NetHost::try_receive(const ConId id, RecvPacket& packet) {
+
+	// Always read all available data into buffers
+	uint8_t buffer[512];
+	while (SDLNet_CheckSockets(d->sockset, 0) > 0) {
+		for (auto& e : d->clients) {
+			if (SDLNet_SocketReady(e.second.socket)) {
+				const int32_t bytes = SDLNet_TCP_Recv(e.second.socket, buffer, sizeof(buffer));
+				if (bytes <= 0) {
+					// Error while receiving
+					close(e.first);
+					// We have to run the for-loop again since we modified the map
+					break;
+				}
+
+				e.second.deserializer.read_data(buffer, bytes);
+			}
+		}
+	}
+
+	// Now check whether there is data for the requested client
+	if (!is_connected(id))
+		return false;
+
+	// Get one packet from the deserializer
+	if (d->clients.at(id).deserializer.write_packet(packet)) {
+		return true;
+	} else {
+		return false;
+	}
+}
+
+void NetHost::send(const ConId id, const SendPacket& packet) {
+	if (is_connected(id)) {
+		SDLNet_TCP_Send(d->clients.at(id).socket, packet.get_data(), packet.size());
+	}
+}
+
+NetHost::NetHost(const uint16_t port)
+	: d(new NetHostImpl) {
+
+	IPaddress myaddr;
+	SDLNet_ResolveHost(&myaddr, nullptr, port);
+	d->svsock = SDLNet_TCP_Open(&myaddr);
+	// Maximal 16 sockets! This mean we can have at most 15 clients in our game (+ metaserver)
+	d->sockset = SDLNet_AllocSocketSet(16);
+}

=== added file 'src/network/nethost.h'
--- src/network/nethost.h	1970-01-01 00:00:00 +0000
+++ src/network/nethost.h	2017-05-09 19:19:42 +0000
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008-2017 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef WL_NETWORK_NETHOST_H
+#define WL_NETWORK_NETHOST_H
+
+#include <memory>
+
+#include "network/network.h"
+
+class NetHostImpl;
+
+/**
+ * GameClient manages the lifetime of a network game in which this computer
+ * participates as a client.
+ *
+ * This includes running the game setup screen and the actual game after
+ * launch, as well as dealing with the actual network protocol.
+ */
+class NetHost {
+	public:
+
+		using ConId = uint32_t;
+
+		/**
+		 * Tries to listen on the given port.
+		 * @param port The port to listen on.
+		 * @return A pointer to a listening \c NetHost object or an invalid pointer if the connection failed.
+		 */
+		static std::unique_ptr<NetHost> listen(const uint16_t port);
+
+		/**
+		 * Closes the server.
+		 */
+		~NetHost();
+
+		/**
+		 * Returns whether the server is started and is listening.
+		 * @return \c true if the server is listening, \c false otherwise.
+		 */
+		bool is_listening() const;
+
+		/**
+		 * Returns whether the given client is connected.
+		 * @param The id of the client to check.
+		 * @return \c true if the connection is open, \c false otherwise.
+		 */
+		bool is_connected(ConId id) const;
+
+		/**
+		 * Stops listening for connections.
+		 */
+		void stop_listening();
+
+		/**
+		 * Closes the connection to the given client.
+		 * @param id The id of the client to close the connection to.
+		 */
+		void close(ConId id);
+
+		/**
+		 * Tries to accept a new client.
+		 * @param new_id The connection id of the new client will be stored here.
+		 * @return \c true if a client has connected, \c false otherwise.
+		 *   The given id is only modified when \c true is returned.
+		 *   Calling this on a closed server will return false.
+		 *   The returned id is always greater 0.
+		 */
+		bool try_accept(ConId& new_id);
+
+		/**
+		 * Tries to receive a packet.
+		 * @param id The connection id of the client that should be received.
+		 * @param packet A packet that should be overwritten with the received data.
+		 * @return \c true if a packet is available, \c false otherwise.
+		 *   The given packet is only modified when \c true is returned.
+		 *   Calling this on a closed connection will return false.
+		 */
+		bool try_receive(ConId id, RecvPacket& packet);
+
+		/**
+		 * Sends a packet.
+		 * Calling this on a closed connection will silently fail.
+		 * @param id The connection id of the client that should be send to.
+		 * @param packet The packet to send.
+		 */
+		 void send(ConId id, const SendPacket& packet);
+
+	private:
+		NetHost(const uint16_t port);
+
+		NetHostImpl *d;
+};
+
+#endif  // end of include guard: WL_NETWORK_NETHOST_H

=== modified file 'src/network/network.cc'
--- src/network/network.cc	2017-01-25 18:55:59 +0000
+++ src/network/network.cc	2017-05-09 19:19:42 +0000
@@ -117,11 +117,21 @@
 		buffer.push_back(0);  //  this will finally be the length of the packet
 		buffer.push_back(0);
 	}
+
 	for (size_t idx = 0; idx < size; ++idx)
 		buffer.push_back(static_cast<const uint8_t*>(packet_data)[idx]);
 }
 
-void SendPacket::send(TCPsocket sock) {
+void SendPacket::reset() {
+	buffer.clear();
+}
+
+size_t SendPacket::size() const {
+	return buffer.size();
+}
+
+uint8_t* SendPacket::get_data() const {
+
 	uint32_t const length = buffer.size();
 
 	assert(length < 0x10000);
@@ -130,29 +140,11 @@
 	buffer[0] = length >> 8;
 	buffer[1] = length & 0xFF;
 
-	if (sock)
-		SDLNet_TCP_Send(sock, &(buffer[0]), buffer.size());
-}
-
-void SendPacket::reset() {
-	buffer.clear();
+	return &(buffer[0]);
 }
 
 /*** class RecvPacket ***/
 
-RecvPacket::RecvPacket(Deserializer& des) {
-	uint16_t const size = des.queue_[0] << 8 | des.queue_[1];
-
-	// The following should be caught by Deserializer::read and ::avail
-	assert(des.queue_.size() >= static_cast<size_t>(size));
-	assert(size >= 2);
-
-	buffer.insert(buffer.end(), des.queue_.begin() + 2, des.queue_.begin() + size);
-	index_ = 0;
-
-	des.queue_.erase(des.queue_.begin(), des.queue_.begin() + size);
-}
-
 size_t RecvPacket::data(void* const packet_data, size_t const bufsize) {
 	if (index_ + bufsize > buffer.size())
 		throw wexception("Packet too short");
@@ -167,29 +159,29 @@
 	return index_ < buffer.size();
 }
 
-bool Deserializer::read(TCPsocket sock) {
-	uint8_t buffer[512];
-	const int32_t bytes = SDLNet_TCP_Recv(sock, buffer, sizeof(buffer));
-	if (bytes <= 0)
-		return false;
-
-	queue_.insert(queue_.end(), &buffer[0], &buffer[bytes]);
-
-	return queue_.size() < 2 || 2 <= (queue_[0] << 8 | queue_[1]);
+void Deserializer::read_data(const uint8_t *data, const int32_t len) {
+
+	queue_.insert(queue_.end(), &data[0], &data[len]);
 }
 
-/**
- * Returns true if an entire packet is available
- */
-bool Deserializer::avail() const {
+bool Deserializer::write_packet(RecvPacket& packet) {
+	// No data at all
 	if (queue_.size() < 2)
 		return false;
 
-	const uint16_t size = queue_[0] << 8 | queue_[1];
-	if (size < 2)
+	uint16_t const size = queue_[0] << 8 | queue_[1];
+	assert(size >= 2);
+
+	// Not enough data for a complete packet
+	if (queue_.size() < static_cast<size_t>(size))
 		return false;
 
-	return queue_.size() >= static_cast<size_t>(size);
+	packet.buffer.clear();
+	packet.buffer.insert(packet.buffer.end(), queue_.begin() + 2, queue_.begin() + size);
+	packet.index_ = 0;
+
+	queue_.erase(queue_.begin(), queue_.begin() + size);
+	return true;
 }
 
 DisconnectException::DisconnectException(const char* fmt, ...) {

=== modified file 'src/network/network.h'
--- src/network/network.h	2017-01-25 18:55:59 +0000
+++ src/network/network.h	2017-05-09 19:19:42 +0000
@@ -43,7 +43,7 @@
 };
 
 /**
- * This non-gamelogic command is used by \ref NetHost and \ref NetClient
+ * This non-gamelogic command is used by \ref GameHost and \ref GameClient
  * to schedule taking a synchronization hash.
  */
 struct CmdNetCheckSync : public Widelands::Command {
@@ -92,18 +92,22 @@
 
 /**
  * Buffered StreamWrite object for assembling a packet that will be
- * sent via the \ref send() function.
+ * sent over the network.
  */
 struct SendPacket : public StreamWrite {
 	SendPacket();
 
-	void send(TCPsocket);
 	void reset();
 
 	void data(void const* data, size_t size) override;
 
+	size_t size() const;
+
+	uint8_t* get_data() const;
+
 private:
-	std::vector<uint8_t> buffer;
+	// First two bytes are overwritten on call to get_data()
+	mutable std::vector<uint8_t> buffer;
 };
 
 /**
@@ -111,12 +115,11 @@
  */
 struct RecvPacket : public StreamRead {
 public:
-	RecvPacket(Deserializer&);
-
 	size_t data(void* data, size_t bufsize) override;
 	bool end_of_file() const override;
 
 private:
+	friend struct Deserializer;
 	std::vector<uint8_t> buffer;
 	size_t index_;
 };
@@ -135,21 +138,17 @@
 class Deserializer {
 public:
 	/**
-	 * Read data from the given socket.
-	 * \return \c false if the socket was disconnected or another error
-	 * occurred.
-	 * \c true if some data could be read (this does not imply that \ref avail
-	 * will return \c true !)
+	 * Adds the given data to the internal buffer.
 	 */
-	bool read(TCPsocket sock);
+	void read_data(const uint8_t *data, const int32_t len);
 
 	/**
-	 * \return \c true if an entire packet has been received.
+	 * \param packet The packet to fill with the received data.
+	 * \return \c true if an entire packet has been received and written to the given packet.
 	 */
-	bool avail() const;
+	bool write_packet(RecvPacket& packet);
 
 private:
-	friend struct RecvPacket;
 	std::vector<uint8_t> queue_;
 };
 

=== modified file 'src/network/network_lan_promotion.h'
--- src/network/network_lan_promotion.h	2017-01-25 18:55:59 +0000
+++ src/network/network_lan_promotion.h	2017-05-09 19:19:42 +0000
@@ -34,6 +34,8 @@
 #define LAN_GAME_CLOSED 0
 #define LAN_GAME_OPEN 1
 
+// TODO(Notabilis): Update file for IPv6
+
 struct NetGameInfo {
 	char magic[6];
 	uint8_t version;

=== modified file 'src/network/network_protocol.h'
--- src/network/network_protocol.h	2017-01-25 18:55:59 +0000
+++ src/network/network_protocol.h	2017-05-09 19:19:42 +0000
@@ -165,7 +165,7 @@
     * During game setup, this command is sent by the host to inform clients
     * about the names of the tribes they may choose.
     *
-    * \see NetClient::handle_network
+    * \see GameClient::handle_network
     */
 	NETCMD_SETTING_TRIBES = 6,
 
@@ -173,7 +173,7 @@
     * During game setup, this command contains complete information about all
     * player slots (independent of their state).
     *
-    * \see NetClient::handle_network
+    * \see GameClient::handle_network
     */
 	NETCMD_SETTING_ALLPLAYERS = 7,
 
@@ -184,7 +184,7 @@
     * Payload in that case is:
     * \li unsigned_8: number of the player
     *
-    * \see NetClient::handle_network
+    * \see GameClient::handle_network
     */
 	NETCMD_SETTING_PLAYER = 8,
 
@@ -192,7 +192,7 @@
     * During game setup, this command contains complete information about all
     * users.
     *
-    * \see NetClient::handle_network
+    * \see GameClient::handle_network
     */
 	NETCMD_SETTING_ALLUSERS = 9,
 
@@ -200,7 +200,7 @@
     * During game setup, this command updates the information associated to
     * one user.
     *
-    * \see NetClient::handle_network
+    * \see GameClient::handle_network
     */
 	NETCMD_SETTING_USER = 10,
 

=== modified file 'src/ui_fsmenu/internet_lobby.cc'
--- src/ui_fsmenu/internet_lobby.cc	2017-02-27 13:45:46 +0000
+++ src/ui_fsmenu/internet_lobby.cc	2017-05-09 19:19:42 +0000
@@ -28,9 +28,9 @@
 #include "graphic/graphic.h"
 #include "graphic/text_constants.h"
 #include "network/constants.h"
+#include "network/gameclient.h"
+#include "network/gamehost.h"
 #include "network/internet_gaming.h"
-#include "network/netclient.h"
-#include "network/nethost.h"
 #include "profile/profile.h"
 #include "ui_basic/messagebox.h"
 
@@ -375,6 +375,7 @@
 		}
 		std::string ip = InternetGaming::ref().ip();
 
+		// TODO(Notabilis): Change this for IPv6
 		//  convert IPv6 addresses returned by the metaserver to IPv4 addresses.
 		//  At the moment SDL_net does not support IPv6 anyways.
 		if (!ip.compare(0, 7, "::ffff:")) {
@@ -382,25 +383,7 @@
 			log("InternetGaming: cut IPv6 address: %s\n", ip.c_str());
 		}
 
-		IPaddress peer;
-		if (hostent* const he = gethostbyname(ip.c_str())) {
-			peer.host = (reinterpret_cast<in_addr*>(he->h_addr_list[0]))->s_addr;
-			DIAG_OFF("-Wold-style-cast")
-			peer.port = htons(WIDELANDS_PORT);
-			DIAG_ON("-Wold-style-cast")
-		} else {
-			// Actually the game is not done, but that way we are again listed as in the lobby
-			InternetGaming::ref().set_game_done();
-			// Show a popup warning message
-			std::string warningheader(_("Connection problem"));
-			std::string warning(_("Widelands was unable to connect to the host."));
-			UI::WLMessageBox mmb(
-			   this, warningheader, warning, UI::WLMessageBox::MBoxType::kOk, UI::Align::kLeft);
-			mmb.run<UI::Panel::Returncodes>();
-		}
-		SDLNet_ResolveHost(&peer, ip.c_str(), WIDELANDS_PORT);
-
-		NetClient netgame(&peer, InternetGaming::ref().get_local_clientname(), true);
+		GameClient netgame(ip, WIDELANDS_PORT, InternetGaming::ref().get_local_clientname(), true);
 		netgame.run();
 	} else
 		throw wexception("No server selected! That should not happen!");
@@ -423,6 +406,6 @@
 	InternetGaming::ref().set_local_servername(servername_ui);
 
 	// Start the game
-	NetHost netgame(InternetGaming::ref().get_local_clientname(), true);
+	GameHost netgame(InternetGaming::ref().get_local_clientname(), true);
 	netgame.run();
 }

=== modified file 'src/ui_fsmenu/netsetup_lan.h'
--- src/ui_fsmenu/netsetup_lan.h	2017-01-25 18:55:59 +0000
+++ src/ui_fsmenu/netsetup_lan.h	2017-05-09 19:19:42 +0000
@@ -34,6 +34,8 @@
 struct NetOpenGame;
 struct NetGameInfo;
 
+// TODO(Notabilis): Update for IPv6
+
 class FullscreenMenuNetSetupLAN : public FullscreenMenuBase {
 public:
 	FullscreenMenuNetSetupLAN();

=== modified file 'src/wlapplication.cc'
--- src/wlapplication.cc	2017-04-20 10:54:30 +0000
+++ src/wlapplication.cc	2017-05-09 19:19:42 +0000
@@ -69,9 +69,9 @@
 #include "logic/single_player_game_controller.h"
 #include "logic/single_player_game_settings_provider.h"
 #include "map_io/map_loader.h"
+#include "network/gameclient.h"
+#include "network/gamehost.h"
 #include "network/internet_gaming.h"
-#include "network/netclient.h"
-#include "network/nethost.h"
 #include "profile/profile.h"
 #include "sound/sound_handler.h"
 #include "ui_basic/messagebox.h"
@@ -1164,27 +1164,29 @@
 			FullscreenMenuNetSetupLAN ns;
 			menu_result = ns.run<FullscreenMenuBase::MenuTarget>();
 			std::string playername = ns.get_playername();
+			// TODO(Notabilis): This has to be updated for IPv6
 			uint32_t addr;
 			uint16_t port;
 			bool const host_address = ns.get_host_address(addr, port);
 
 			switch (menu_result) {
 			case FullscreenMenuBase::MenuTarget::kHostgame: {
-				NetHost netgame(playername);
+				GameHost netgame(playername);
 				netgame.run();
 				break;
 			}
 			case FullscreenMenuBase::MenuTarget::kJoingame: {
-				IPaddress peer;
-
 				if (!host_address)
 					throw WLWarning(
 					   "Invalid Address", "%s", "The address of the game server is invalid");
 
-				peer.host = addr;
-				peer.port = port;
-
-				NetClient netgame(&peer, playername);
+				// TODO(Notabilis): Make this prettier. I am aware that this is quite ugly but it should work
+				// for now and will be removed shortly when we switch to boost.asio
+				char ip_str[] = {"255.255.255.255"};
+				sprintf(ip_str, "%d.%d.%d.%d", (addr & 0x000000ff), (addr & 0x0000ff00) >> 8,
+												(addr & 0x00ff0000) >> 16, (addr & 0xff000000) >> 24);
+				port = (port >> 8) | ((port & 0xFF) << 8);
+				GameClient netgame(ip_str, port, playername);
 				netgame.run();
 				break;
 			}


Follow ups