C#: an unsendable goodbye names the failure by a fixed code
EndUnsendable put the SocketException's message on the wire, the platform's wording. SendErrorCode names a permanent failure EMSGSIZE, EINVAL or EAFNOSUPPORT, anything else UNKNOWN, so every runtime's unsendable goodbye reads "packet could not be sent: CODE" with the same codes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d541e9eb35
commit
e1bdf92321
2 changed files with 29 additions and 2 deletions
|
|
@ -1490,15 +1490,30 @@ namespace GameCult.Networking
|
|||
|
||||
internal const string UnsendablePacketReason = "packet could not be sent";
|
||||
|
||||
/// <summary>
|
||||
/// The fixed name of a failed send's cause: EMSGSIZE, EINVAL or EAFNOSUPPORT for a failure
|
||||
/// that can never pass (<see cref="IsPermanentSendError"/>), UNKNOWN for anything else, as
|
||||
/// every runtime's goodbye names it. Never the error's own message, which is the platform's
|
||||
/// wording.
|
||||
/// </summary>
|
||||
internal static string SendErrorCode(SocketException error) => error.SocketErrorCode switch
|
||||
{
|
||||
SocketError.MessageSize => "EMSGSIZE",
|
||||
SocketError.InvalidArgument => "EINVAL",
|
||||
SocketError.AddressFamilyNotSupported => "EAFNOSUPPORT",
|
||||
_ => "UNKNOWN",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Ends a session that owes its peer a packet that can never be sent as built. Resending it
|
||||
/// would fail forever, and dropping it would leave a reliable sequence the peer waits on for
|
||||
/// good, so the session cannot be kept. The goodbye's reason names the error.
|
||||
/// good, so the session cannot be kept. The goodbye's reason names the failure by its fixed
|
||||
/// name (<see cref="SendErrorCode"/>).
|
||||
/// </summary>
|
||||
internal CultNetRudpPacket EndUnsendable(SocketException error)
|
||||
{
|
||||
ResetPeerState();
|
||||
return CreateDisconnect(Encoding.UTF8.GetBytes(UnsendablePacketReason + ": " + error.Message));
|
||||
return CreateDisconnect(Encoding.UTF8.GetBytes(UnsendablePacketReason + ": " + SendErrorCode(error)));
|
||||
}
|
||||
|
||||
internal uint NextSequence => _nextSequence;
|
||||
|
|
|
|||
|
|
@ -137,6 +137,18 @@ namespace GameCult.Networking.Tests
|
|||
// caller-directed send throws it and queues nothing; inside a poll it ends that peer's
|
||||
// session and never the poll.
|
||||
|
||||
[Test]
|
||||
public void An_unsendable_goodbye_names_the_failure_by_a_fixed_code_never_its_message()
|
||||
{
|
||||
var session = new CultNetRudpSession(new CultNetRudpSessionOptions { ConnectionId = 1 });
|
||||
Assert.That(
|
||||
Encoding.UTF8.GetString(session.EndUnsendable(new SocketException((int)SocketError.MessageSize)).Payload!),
|
||||
Is.EqualTo("packet could not be sent: EMSGSIZE"));
|
||||
Assert.That(CultNetRudpSession.SendErrorCode(new SocketException((int)SocketError.InvalidArgument)), Is.EqualTo("EINVAL"));
|
||||
Assert.That(CultNetRudpSession.SendErrorCode(new SocketException((int)SocketError.AddressFamilyNotSupported)), Is.EqualTo("EAFNOSUPPORT"));
|
||||
Assert.That(CultNetRudpSession.SendErrorCode(new SocketException((int)SocketError.ConnectionRefused)), Is.EqualTo("UNKNOWN"));
|
||||
}
|
||||
|
||||
private static bool IsUnsendableReason(byte[]? reason)
|
||||
{
|
||||
var text = Encoding.UTF8.GetString(reason ?? Array.Empty<byte>());
|
||||
|
|
|
|||
Loading…
Reference in a new issue