Skip to content

Commit a89e7e5

Browse files
add SocketError, better error handling
1 parent 556af04 commit a89e7e5

File tree

4 files changed

+125
-91
lines changed

4 files changed

+125
-91
lines changed

game/src/engine.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,11 @@ void Engine::push_error(const std::string &msg) {
415415
popups_async.emplace(msg, ui::PopupType::error);
416416
}
417417

418+
void Engine::push_error(const char *func, SocketError &err) {
419+
fprintf(stderr, "%s: %s\n", func, err.what());
420+
push_error(err.user);
421+
}
422+
418423
void Engine::start_client_now(const char *host, uint16_t port, UI_TaskInfo &info) {
419424
ZoneScoped;
420425

@@ -437,6 +442,8 @@ void Engine::start_client(const char *host, uint16_t port) {
437442

438443
start_client_now(host, port, info);
439444
trigger_client_connected();
445+
} catch (SocketError &e) {
446+
push_error(func, e);
440447
} catch (std::exception &e) {
441448
push_error(func, std::string("cannot connect to server: ") + e.what());
442449
}

game/src/engine.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ class Engine final {
239239
public:
240240
void push_error(const char *func, const std::string &msg);
241241
void push_error(const std::string &msg);
242+
void push_error(const char *func, SocketError &err);
242243

243244
// API for asynchronous tasks
244245
UI_TaskInfo ui_async(const std::string &title, const std::string &desc, unsigned steps, TaskFlags flags=TaskFlags::all);

game/src/net/net.cpp

Lines changed: 109 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -32,52 +32,64 @@ static int close(SOCKET sock)
3232
namespace aoe {
3333

3434
#if _WIN32
35-
// process and throw error message. always throws
36-
static void wsa_generic_error(const char *prefix, int code) noexcept(false)
35+
static std::string wsa_parse_error(const char *prefix="", int code=WSAGetLastError())
3736
{
3837
std::string msg(prefix);
39-
msg += ": ";
4038

4139
switch (code) {
42-
case WSANOTINITIALISED:
43-
msg += "winsock not ready";
44-
break;
45-
case WSAENETDOWN:
46-
msg += "network subsystem error";
47-
break;
48-
case WSAENOBUFS:
49-
msg += "out of memory";
50-
break;
51-
case WSAENOTSOCK:
52-
msg += "invalid socket";
53-
break;
54-
case WSAEOPNOTSUPP:
55-
msg += "operation not supported";
56-
break;
57-
default:
58-
msg += "code " + std::to_string(code);
59-
break;
40+
case WSANOTINITIALISED:
41+
msg += "winsock not ready";
42+
break;
43+
case WSAENETDOWN:
44+
msg += "network subsystem error";
45+
break;
46+
case WSAEINPROGRESS:
47+
msg += "waiting for network to become ready";
48+
break;
49+
case WSAENOBUFS:
50+
msg += "out of memory";
51+
break;
52+
case WSAENOTSOCK:
53+
msg += "invalid socket";
54+
break;
55+
case WSAEOPNOTSUPP:
56+
msg += "operation not supported";
57+
break;
58+
case WSAEFAULT:
59+
msg += "argument address fault";
60+
break;
61+
case WSAEACCES:
62+
msg += "access denied";
63+
break;
64+
case WSAEADDRINUSE:
65+
msg += "socket address still in use";
66+
break;
67+
case WSAEADDRNOTAVAIL:
68+
msg += "invalid address";
69+
break;
70+
case WSAEINVAL:
71+
msg += "invalid state or invalid value specified";
72+
break;
73+
default:
74+
msg += "unknown error code " + std::to_string(code);
75+
break;
6076
}
6177

62-
throw std::runtime_error(msg);
78+
return msg;
79+
}
80+
81+
// process and throw error message. always throws
82+
static void wsa_generic_error(const char *prefix, int code) noexcept(false)
83+
{
84+
throw std::runtime_error(wsa_parse_error(prefix, code));
6385
}
6486

6587
std::atomic<unsigned> initnet(0);
6688

6789
void set_nonblocking(SOCKET s, bool nonbl) {
6890
u_long arg = !!nonbl;
69-
if (!ioctlsocket(s, FIONBIO, &arg))
70-
return;
71-
72-
int r;
73-
74-
switch (r = WSAGetLastError()) {
75-
case WSAEFAULT:
76-
throw std::runtime_error("wsa: argument address fault");
77-
default:
78-
wsa_generic_error("wsa: cannot change non-blocking mode", r);
79-
break;
80-
}
91+
if (ioctlsocket(s, FIONBIO, &arg))
92+
throw std::runtime_error(wsa_parse_error());
8193
}
8294

8395
Net::Net() {
@@ -95,6 +107,8 @@ Net::Net() {
95107
throw std::runtime_error("wsa: winsock blocked");
96108
case WSAEPROCLIM:
97109
throw std::runtime_error("wsa: winsock process limit reached");
110+
case WSAEFAULT:
111+
throw std::runtime_error("wsa: wsadata bogus pointer");
98112
default:
99113
if (r)
100114
throw std::runtime_error(std::string("wsa: winsock error code ") + std::to_string(r));
@@ -108,27 +122,16 @@ Net::~Net() {
108122
// https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsacleanup
109123
int r = WSACleanup();
110124

125+
if (r == 0) {
126+
--initnet;
127+
return;
128+
}
129+
111130
if (r == SOCKET_ERROR)
112131
// https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsacleanup
113-
r = WSAGetLastError();
114-
115-
switch (r) {
116-
case WSANOTINITIALISED:
117-
fprintf(stderr, "%s: winsock not initialised\n", __func__);
118-
break;
119-
case WSAENETDOWN:
120-
fprintf(stderr, "%s: winsock failed\n", __func__);
121-
break;
122-
case WSAEINPROGRESS:
123-
fprintf(stderr, "%s: winsock is blocked\n", __func__);
124-
break;
125-
default:
126-
if (r)
127-
fprintf(stderr, "%s: winsock error %d\n", __func__, r);
128-
else
129-
--initnet;
130-
break;
131-
}
132+
fprintf(stderr, "%s: %s\n", __func__, wsa_parse_error("").c_str());
133+
else
134+
fprintf(stderr, "%s: unexpected error code %d\n", __func__, r);
132135
}
133136

134137
TcpSocket::TcpSocket() : s((int)INVALID_SOCKET) {
@@ -137,7 +140,7 @@ TcpSocket::TcpSocket() : s((int)INVALID_SOCKET) {
137140
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
138141
throw std::runtime_error("socket failed");
139142

140-
s.store((int)sock);
143+
s.store(sock);
141144
}
142145

143146
TcpSocket::~TcpSocket() {
@@ -152,7 +155,7 @@ void TcpSocket::open() {
152155
throw std::runtime_error("socket failed");
153156

154157
closesocket(s);
155-
s.store((int)sock);
158+
s.store(sock);
156159
}
157160

158161
void TcpSocket::close() {
@@ -172,48 +175,40 @@ void TcpSocket::bind(const char *address, uint16_t port) {
172175
const auto sock = s.load(std::memory_order_relaxed);
173176
sockaddr_in dst{ 0 };
174177

175-
dst.sin_family = AF_INET;
176-
dst.sin_addr.s_addr = inet_addr(address);
178+
int af = AF_INET;
179+
dst.sin_family = af;
177180
dst.sin_port = htons(port);
178181

179-
// https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind
180-
int r = ::bind(sock, (const sockaddr *)&dst, sizeof dst);
181-
182-
if (r == 0)
183-
return;
184-
185-
if (r != SOCKET_ERROR)
186-
throw std::runtime_error(std::string("wsa: bind failed: unknown return code ") + std::to_string(r));
187-
188-
int err = WSAGetLastError();
189-
190-
switch (err) {
191-
case WSAEACCES:
192-
throw std::runtime_error("wsa: bind failed: access denied");
193-
case WSAEADDRINUSE:
194-
throw std::runtime_error("wsa: bind failed: socket address still in use");
195-
case WSAEADDRNOTAVAIL:
196-
throw std::runtime_error("wsa: bind failed: invalid address");
197-
case WSAEFAULT:
198-
throw std::runtime_error("wsa: bind failed: bad argument");
199-
case WSAEINPROGRESS:
200-
throw std::runtime_error("wsa: bind failed: in progress");
201-
case WSAEINVAL:
202-
throw std::runtime_error("wsa: bind failed: already bound");
203-
default:
204-
wsa_generic_error("wsa: bind failed", err);
205-
break;
182+
int r = InetPton(af, address, &dst.sin_addr);
183+
if (r != 1) {
184+
if (r == 0)
185+
throw SocketError(
186+
std::string("wsa: InetPton: invalid address \"") + address + "\"",
187+
std::string("Invalid address \"") + address + "\""
188+
);
189+
190+
if (r != -1)
191+
throw SocketError(
192+
std::string("wsa: InetPton: unexpected error code ") + std::to_string(r),
193+
std::string("Unexpected error for address \"") + address + "\""
194+
);
195+
196+
throw std::runtime_error(wsa_parse_error("", r));
206197
}
198+
199+
// https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind
200+
r = ::bind(sock, (const sockaddr*)&dst, sizeof dst);
201+
if (r != 0)
202+
throw std::runtime_error(wsa_parse_error("wsa: bind failed: "));
207203
}
208204

209205
void TcpSocket::listen(int backlog) {
210206
if (backlog < 1)
211207
throw std::runtime_error("listen failed: backlog must be positive");
212208

213-
const auto sock = s.load(std::memory_order_relaxed);
209+
SOCKET sock = s.load(std::memory_order_relaxed);
214210
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen
215211
int r = ::listen(sock, backlog);
216-
217212
if (r == 0)
218213
return;
219214

@@ -243,15 +238,39 @@ void TcpSocket::listen(int backlog) {
243238
}
244239

245240
void TcpSocket::connect(const char *address, uint16_t port) {
246-
const auto sock = s.load(std::memory_order_relaxed);
241+
SOCKET sock = s.load(std::memory_order_relaxed);
247242
sockaddr_in dst{ 0 };
248243

249-
dst.sin_family = AF_INET;
250-
dst.sin_addr.s_addr = inet_addr(address);
244+
int af = AF_INET;
245+
dst.sin_family = af;
251246
dst.sin_port = htons(port);
252247

248+
int r = InetPton(af, address, &dst.sin_addr);
249+
if (r != 1) {
250+
if (r == 0)
251+
throw SocketError(
252+
std::string("wsa: connect failed: invalid address \"") + address + "\"",
253+
std::string("Invalid IPv4 host address \"" ) + address + "\"");
254+
255+
if (r != -1)
256+
throw SocketError(
257+
std::string("wsa: connect failed: unknown error code: ") + std::to_string(r),
258+
std::string("Unknown error ") + std::to_string(r) + " has occurred");
259+
260+
r = WSAGetLastError();
261+
262+
switch (r) {
263+
case WSAEFAULT: // should we just abort?
264+
throw std::string("wsa: connect failed: invalid address: The system detected an invalid pointer address in attempting to use a pointer argument in a call");
265+
case WSAEAFNOSUPPORT: // should we just abort?
266+
throw std::string("wsa: connect failed: invalid address: An address incompatible with the requested protocol was used");
267+
default:
268+
throw std::runtime_error(std::string("wsa: connect failed: invalid address: unknown error code: ") + std::to_string(r));
269+
}
270+
}
271+
253272
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect
254-
int r = ::connect(sock, (const sockaddr *)&dst, sizeof dst);
273+
r = ::connect(sock, (sockaddr*)&dst, sizeof dst);
255274

256275
if (r == 0)
257276
return;

game/src/net/net.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,19 @@ class SocketClosedError final : public std::runtime_error {
6464
explicit SocketClosedError(const char *msg) : std::runtime_error(msg) {}
6565
};
6666

67+
class SocketError final : public std::runtime_error {
68+
public:
69+
std::string user; // user friendly message
70+
71+
explicit SocketError(const std::string &what, const std::string &user) : std::runtime_error(what), user(user) {}
72+
};
73+
6774
class ServerSocket;
6875

6976
void set_nonblocking(SOCKET s, bool nonbl=true);
7077

7178
class TcpSocket final {
72-
std::atomic<int> s;
79+
std::atomic<SOCKET> s;
7380
friend ServerSocket;
7481
public:
7582
TcpSocket();

0 commit comments

Comments
 (0)