various changes, better network error handling

Wed, 27 Jan 2021 19:27:23 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 27 Jan 2021 19:27:23 +0200
changeset 189
248d0b85cbda
parent 188
5fc32e4b2a8c
child 190
90bf9049e5eb

various changes, better network error handling

sources/basics.h file | annotate | diff | comparison | revisions
sources/interface.h file | annotate | diff | comparison | revisions
sources/main.cpp file | annotate | diff | comparison | revisions
sources/mystring.h file | annotate | diff | comparison | revisions
sources/network/ipaddress.h file | annotate | diff | comparison | revisions
sources/network/rconsession.cpp file | annotate | diff | comparison | revisions
sources/network/rconsession.h file | annotate | diff | comparison | revisions
sources/network/udpsocket.cpp file | annotate | diff | comparison | revisions
sources/network/udpsocket.h file | annotate | diff | comparison | revisions
--- a/sources/basics.h	Wed Jan 27 19:01:37 2021 +0200
+++ b/sources/basics.h	Wed Jan 27 19:27:23 2021 +0200
@@ -125,4 +125,10 @@
 
 struct Exitception {};
 
+#ifdef __GNUC__
+# define GNUATTRIBUTE(X) __attribute__(X)
+#else
+# define GNUATTRIBUTE(X)
+#endif
+
 END_ZFC_NAMESPACE
--- a/sources/interface.h	Wed Jan 27 19:01:37 2021 +0200
+++ b/sources/interface.h	Wed Jan 27 19:27:23 2021 +0200
@@ -55,10 +55,10 @@
 	void handleCommand(const std::string& input);
 	void handleInput();
 	void needRefresh();
-	void __cdecl print(const char* fmtstr, ...);
-	void __cdecl printWarning(const char* fmtstr, ...);
-	void __cdecl printError(const char* fmtstr, ...);
-	void __cdecl printText(const char* fmtstr, ...);
+	void __cdecl print(const char* fmtstr, ...) GNUATTRIBUTE((format(printf, 2, 3)));
+	void __cdecl printWarning(const char* fmtstr, ...) GNUATTRIBUTE((format(printf, 2, 3)));
+	void __cdecl printError(const char* fmtstr, ...) GNUATTRIBUTE((format(printf, 2, 3)));
+	void __cdecl printText(const char* fmtstr, ...) GNUATTRIBUTE((format(printf, 2, 3)));
 	void render();
 	void renderFull();
 	void setPlayerNames(const std::vector<std::string>& names);
--- a/sources/main.cpp	Wed Jan 27 19:01:37 2021 +0200
+++ b/sources/main.cpp	Wed Jan 27 19:27:23 2021 +0200
@@ -84,7 +84,7 @@
 			FD_ZERO (&fdset);
 			FD_SET (0, &fdset);
 
-			int fd = iface.getSession()->getSocket()->file_descriptor();
+			int fd = iface.getSession()->getSocket()->file_descriptor;
 			highest = zfc::max (highest, fd);
 			FD_SET (fd, &fdset);
 
--- a/sources/mystring.h	Wed Jan 27 19:01:37 2021 +0200
+++ b/sources/mystring.h	Wed Jan 27 19:27:23 2021 +0200
@@ -46,7 +46,7 @@
 std::string mid(const std::string& str, int rangeBegin, int rangeEnd);
 std::string right(const std::string& str, int length);
 std::string vsprintf(const char* formatString, va_list args);
-std::string __cdecl sprintf(const char* formatString, ...);
+std::string __cdecl sprintf(const char* formatString, ...) GNUATTRIBUTE((format(printf, 1, 2)));
 std::string remove_range(const std::string& string, int start, int end);
 void replace_all(std::string& str, const char* text, const char* replacement);
 bool starts_with(const std::string& str, const std::string& other);
--- a/sources/network/ipaddress.h	Wed Jan 27 19:01:37 2021 +0200
+++ b/sources/network/ipaddress.h	Wed Jan 27 19:27:23 2021 +0200
@@ -40,10 +40,12 @@
 namespace net
 {
 	using octet_t = std::uint8_t;
+	using port_t = std::uint16_t;
+	using host_t = std::uint32_t;
 	struct ip_address
 	{
-		std::uint32_t host;
-		std::uint16_t port;
+		net::host_t host = 0;
+		net::port_t port = 0;
 		bool operator<(const ip_address& other) const;
 		bool operator==(const ip_address& other) const;
 	};
--- a/sources/network/rconsession.cpp	Wed Jan 27 19:01:37 2021 +0200
+++ b/sources/network/rconsession.cpp	Wed Jan 27 19:27:23 2021 +0200
@@ -42,10 +42,10 @@
 	m_adminCount(0),
 	m_interface(nullptr)
 {
-	if (not m_socket.set_blocking(false))
+	std::stringstream errors;
+	if (not m_socket.set_blocking(false, errors))
 	{
-		fprintf(stderr, "unable to set socket as non-blocking: %s\n",
-			m_socket.error_string().data());
+		fprintf(stderr, "unable to set socket as non-blocking: %s\n", errors.str().data());
 		exit(EXIT_FAILURE);
 	}
 }
@@ -80,9 +80,15 @@
 
 // -------------------------------------------------------------------------------------------------
 //
-void RCONSession::send(const ByteArray& packet)
+bool RCONSession::send(const ByteArray& packet)
 {
-	m_socket.send(m_address, packet);
+	std::stringstream errors;
+	const bool result = m_socket.send(m_address, packet, errors);
+	if (not result)
+	{
+		this->m_interface->printError("Network error: %s\n", errors.str().data());
+	}
+	return result;
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -112,8 +118,14 @@
 		}
 	}
 
-	for (Datagram datagram; m_socket.read(datagram);)
+	std::stringstream errors;
+	for (net::Datagram datagram; m_socket.read(datagram, errors);)
 	{
+		if (errors.tellp() > 0)
+		{
+			m_interface->printError("Network error: %s\n", errors.str().data());
+			errors = {};
+		}
 		// Only process packets that originate from the game server.
 		if (datagram.address == m_address)
 			handlePacket(datagram.message);
@@ -268,7 +280,7 @@
 
 // -------------------------------------------------------------------------------------------------
 //
-UDPSocket* RCONSession::getSocket()
+net::UDPSocket* RCONSession::getSocket()
 {
 	return &m_socket;
 }
@@ -379,7 +391,7 @@
 	}
 	else
 	{
-		m_interface->print("This server does not support tab-completion\n", m_serverProtocol);
+		m_interface->print("This server does not support tab-completion\n");
 	}
 }
 
--- a/sources/network/rconsession.h	Wed Jan 27 19:01:37 2021 +0200
+++ b/sources/network/rconsession.h	Wed Jan 27 19:27:23 2021 +0200
@@ -101,13 +101,13 @@
 	void                        disconnect();
 	int                         getAdminCount() const;
 	const std::string&               getLevel() const;
-	UDPSocket*                  getSocket();
+	net::UDPSocket*                  getSocket();
 	RCONSessionState            getState() const;
 	void                        handlePacket(ByteArray& message);
 	bool                        isActive() const;
 	void                        processServerUpdates(Bytestream& packet);
 	void                        requestTabCompletion(const std::string& part);
-	void                        send(const ByteArray& packet);
+	bool send(const ByteArray& packet);
 	bool                        sendCommand(const std::string& commandString);
 	void                        sendHello();
 	void                        sendPassword();
@@ -118,7 +118,7 @@
 private:
 	RCONSessionState m_state;
 	net::ip_address m_address;
-	UDPSocket m_socket;
+	net::UDPSocket m_socket;
 	time_t m_lastPing;
 	std::string m_password;
 	std::string m_salt;
--- a/sources/network/udpsocket.cpp	Wed Jan 27 19:01:37 2021 +0200
+++ b/sources/network/udpsocket.cpp	Wed Jan 27 19:27:23 2021 +0200
@@ -47,115 +47,119 @@
 
 BEGIN_ZFC_NAMESPACE
 
-char UDPSocket::HuffmanBuffer[131072];
+static char HuffmanBuffer[131072];
 
-// -----------------------------------------------------------------------------
-//
-UDPSocket::UDPSocket() :
-	m_socket (socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) {}
+net::UDPSocket::UDPSocket() :
+	file_descriptor{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)} {}
 
-// -----------------------------------------------------------------------------
-//
-UDPSocket::~UDPSocket()
+net::UDPSocket::~UDPSocket()
 {
 #ifdef _WIN32
-	closesocket (m_socket);
+	::closesocket(m_socket);
 #else
-	close (m_socket);
+	::close(this->file_descriptor);
 #endif
 }
 
-// -------------------------------------------------------------------------------------------------
-//
-bool UDPSocket::set_blocking (bool a)
+bool net::UDPSocket::set_blocking(bool a, std::ostream& errors)
 {
 #ifndef _WIN32
-	int flags = fcntl (m_socket, F_GETFL, 0);
-	int newflags = a ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
-
-	if (flags < 0 || fcntl (m_socket, F_SETFL, newflags) != 0)
+	int flags = ::fcntl(this->file_descriptor, F_GETFL, 0);
+	int newflags = a ?(flags & ~O_NONBLOCK) :(flags | O_NONBLOCK);
+	if (flags < 0 || ::fcntl(this->file_descriptor, F_SETFL, newflags) != 0)
 	{
-		m_error = "Unable to set socket as non-blocking";
+		errors << "Unable to set the UDP socket as non-blocking";
 		return false;
 	}
-
-	return true;
+	else
+	{
+		return true;
+	}
 #else
 	unsigned long mode = a ? 0 : 1;
 
-	if (ioctlsocket (m_socket, FIONBIO, &mode) != 0)
+	if (::ioctlsocket(m_socket, FIONBIO, &mode) != 0)
 	{
-		m_error = strerror (errno);
+		errors << strerror(errno);
 		return false;
 	}
-
-	return true;
+	else
+	{
+		return true;
+	}
 #endif
 }
 
-// -------------------------------------------------------------------------------------------------
-//
-bool UDPSocket::bind (unsigned short port)
+bool net::UDPSocket::bind(const net::port_t port, std::ostream& errors)
 {
 	sockaddr_in svaddr;
-	memset (&svaddr, 0, sizeof svaddr);
+	std::memset(&svaddr, 0, sizeof svaddr);
 	svaddr.sin_family = AF_INET;
-	svaddr.sin_port = htons (port);
-	svaddr.sin_addr.s_addr = htonl (INADDR_ANY);
-
-	if (::bind (m_socket, reinterpret_cast<sockaddr*> (&svaddr), sizeof svaddr) == -1)
+	svaddr.sin_port = htons(port);
+	svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+	if (::bind(this->file_descriptor, reinterpret_cast<sockaddr*>(&svaddr), sizeof svaddr) == -1)
 	{
-		m_error = "Couldn't bind to port "s + std::to_string(port);
+		errors << "Couldn't bind to port "s + std::to_string(port);
 		return false;
 	}
-
-	return true;
+	else
+	{
+		return true;
+	}
 }
 
-// -------------------------------------------------------------------------------------------------
-//
-bool UDPSocket::read (Datagram& datagram)
+bool net::UDPSocket::read(Datagram& datagram, std::ostream& errors)
 {
 	sockaddr_in claddr;
 	socklen_t socklen = sizeof claddr;
-	int length = ::recvfrom (m_socket, HuffmanBuffer, sizeof HuffmanBuffer, 0,
-		reinterpret_cast<sockaddr*> (&claddr), &socklen);
-
+	const int length = ::recvfrom(
+		this->file_descriptor,
+		zfc::HuffmanBuffer,
+		sizeof zfc::HuffmanBuffer,
+		0,
+		reinterpret_cast<sockaddr*>(&claddr),
+		&socklen
+	);
 	if (length == -1)
 	{
 		if (errno != EWOULDBLOCK)
-			m_error = std::string ("recvfrom error: ") + strerror (errno);
-
+		{
+			errors << std::string("recvfrom error: ") + std::strerror(errno);
+		}
 		return false;
 	}
-
 	unsigned char decodedPacket[MAX_DATAGRAM_LENGTH];
 	int decodedLength = sizeof decodedPacket;
-	HUFFMAN_Decode (reinterpret_cast<unsigned char*> (HuffmanBuffer),
+	::HUFFMAN_Decode(reinterpret_cast<unsigned char*>(HuffmanBuffer),
 		decodedPacket, length, &decodedLength);
-	datagram.address.host = ntohl (claddr.sin_addr.s_addr);
-	datagram.address.port = ntohs (claddr.sin_port);
+	datagram.address.host = ntohl(claddr.sin_addr.s_addr);
+	datagram.address.port = ntohs(claddr.sin_port);
 	datagram.message = ByteArray{&decodedPacket[0], &decodedPacket[decodedLength]};
 	return true;
 }
 
-// -------------------------------------------------------------------------------------------------
-//
-bool UDPSocket::send (const net::ip_address& address, const ByteArray& data)
+bool net::UDPSocket::send(const net::ip_address& address, const ByteArray& data, std::ostream& errors)
 {
 	int encodedlength = sizeof HuffmanBuffer;
-	HUFFMAN_Encode (data.data(), reinterpret_cast<unsigned char*> (HuffmanBuffer), data.size(), &encodedlength);
+	::HUFFMAN_Encode(data.data(), reinterpret_cast<unsigned char*>(HuffmanBuffer), data.size(), &encodedlength);
 	sockaddr_in claddr = net::ip_address_to_sockaddr_in(address);
-	int res = ::sendto (m_socket, HuffmanBuffer, encodedlength, 0,
-		reinterpret_cast<sockaddr*> (&claddr), sizeof claddr);
-
-	if (res == -1)
+	const int send_result = ::sendto(
+		this->file_descriptor,
+		HuffmanBuffer,
+		encodedlength,
+		0,
+		reinterpret_cast<sockaddr*>(&claddr),
+		sizeof claddr
+	);
+	if (send_result == -1)
 	{
-		m_error = std::string ("Unable to launch packet: ") + strerror (errno);
+		errors << "Unable to launch packet: "s + std::strerror(errno);
 		return false;
 	}
-
-	return true;
+	else
+	{
+		return true;
+	}
 }
 
 END_ZFC_NAMESPACE
\ No newline at end of file
--- a/sources/network/udpsocket.h	Wed Jan 27 19:01:37 2021 +0200
+++ b/sources/network/udpsocket.h	Wed Jan 27 19:27:23 2021 +0200
@@ -34,9 +34,14 @@
 #include "bytestream.h"
 BEGIN_ZFC_NAMESPACE
 
-enum { MAX_DATAGRAM_LENGTH = 5120 };
+namespace net
+{
+	constexpr int MAX_DATAGRAM_LENGTH = 5120;
+	struct Datagram;
+	class UDPSocket;
+}
 
-struct Datagram
+struct net::Datagram
 {
 	ByteArray message;
 	net::ip_address address;
@@ -44,24 +49,16 @@
 
 // -------------------------------------------------------------------------------------------------
 //
-class UDPSocket
+class net::UDPSocket
 {
 public:
 	UDPSocket();
 	virtual ~UDPSocket();
-
-	bool bind (unsigned short port);
-	bool read (Datagram& datagram);
-	bool send (const net::ip_address& address, const ByteArray& data);
-	bool set_blocking (bool a);
-	const std::string& error_string() const { return m_error; }
-	int file_descriptor() const { return m_socket; }
-
-private:
-	static char HuffmanBuffer[131072];
-
-	std::string m_error;
-	int m_socket;
+	[[nodiscard]] bool bind(port_t port, std::ostream &errors);
+	[[nodiscard]] bool read(Datagram& datagram, std::ostream& errors);
+	[[nodiscard]] bool send(const ip_address& address, const ByteArray& data, std::ostream &errors);
+	[[nodiscard]] bool set_blocking(bool a, std::ostream &errors);
+	const int file_descriptor;
 };
 
 END_ZFC_NAMESPACE
\ No newline at end of file

mercurial