feat(tcp): socket errors as std::system_error, with the phase that failed
SocketErrorException is now a std::system_error: code() carries the errno
under std::generic_category(), or getaddrinfo's result under the new
ResolverCategory(), whose message() is gai_strerror's text; `phase` says
which step failed (resolve, open, connect, send, receive). It is still a
std::runtime_error with the message ClientTCP always threw, the code's
text after it, so existing handlers keep working.
Before, the exception carried a `kind` and an int, and a caller that had
to print the cause its own way still had to match the message prefix to
learn where the failure happened. The standard type ends both: the
category knows how to print the number, and the phase is a field. A
caller writes "[Errno {}] {}" from code().value() and code().message(),
or compares code() with std::errc::connection_refused.
Breaking: SocketErrorException's `kind` and `code` members are gone;
read code().category() and code().value().
This commit is contained in:
parent
16da0a1979
commit
e86c3b70a0
9 changed files with 107 additions and 63 deletions
|
|
@ -53,13 +53,14 @@ client.Send("Hello World", 11);
|
|||
std::vector<char> data = client.RecieveSync();
|
||||
```
|
||||
|
||||
The host is resolved for IPv4 and IPv6 alike and every address it resolves to is tried in turn, as Python's `socket.create_connection` does, so `localhost` reaches a server that listens on `::1` only. The optional third argument is a connect timeout, applied to each address on its own. A failed resolve, connect, send or receive throws `SocketErrorException`: still a `std::runtime_error` with the same message, but carrying a `kind` (`SocketErrorKind::System` for an errno such as `ECONNREFUSED`, `SocketErrorKind::Resolver` for a getaddrinfo result such as `EAI_NONAME`) and the `code`, so a caller can report the cause without parsing the text.
|
||||
The host is resolved for IPv4 and IPv6 alike and every address it resolves to is tried in turn, as Python's `socket.create_connection` does, so `localhost` reaches a server that listens on `::1` only. The optional third argument is a connect timeout, applied to each address on its own. A failed resolve, connect, send or receive throws `SocketErrorException`: a `std::system_error`, so still a `std::runtime_error` with the same message, whose `code()` is the errno under `std::generic_category()` or the `getaddrinfo` result under `Crafter::ResolverCategory()`, and whose `phase` names the step (`SocketPhase::Resolve`, `Open`, `Connect`, `Send` or `Receive`), so a caller can report the cause without parsing the text.
|
||||
|
||||
```cpp
|
||||
try {
|
||||
Crafter::ClientTCP client("localhost", 5432, std::chrono::seconds(5));
|
||||
} catch (const Crafter::SocketErrorException& e) {
|
||||
std::println("[Errno {}] {}", e.code, std::strerror(e.code)); // [Errno 111] Connection refused
|
||||
if (e.code() == std::errc::connection_refused) std::println("nothing listens there");
|
||||
std::println("[Errno {}] {}", e.code().value(), e.code().message()); // [Errno 111] Connection refused
|
||||
} catch (const Crafter::SocketTimeoutException&) {
|
||||
std::println("timed out");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue