From 8d2a2cd5de40e2b94ef5007c32832ed9a063dc40 Mon Sep 17 00:00:00 2001
From: chai <215380520@qq.com>
Date: Thu, 12 Oct 2023 22:09:49 +0800
Subject: +hazel-networking
---
Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs | 259 ++++++++++++++++++++++
1 file changed, 259 insertions(+)
create mode 100644 Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs
(limited to 'Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs')
diff --git a/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs b/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs
new file mode 100644
index 0000000..e64576a
--- /dev/null
+++ b/Tools/Hazel-Networking/Hazel/Udp/UdpConnection.cs
@@ -0,0 +1,259 @@
+using System;
+using System.Net.Sockets;
+
+namespace Hazel.Udp
+{
+ ///
+ /// Represents a connection that uses the UDP protocol.
+ ///
+ ///
+ public abstract partial class UdpConnection : NetworkConnection
+ {
+ public static readonly byte[] EmptyDisconnectBytes = new byte[] { (byte)UdpSendOption.Disconnect };
+
+ public override float AveragePingMs => this._pingMs;
+ protected readonly ILogger logger;
+
+
+ public UdpConnection(ILogger logger) : base()
+ {
+ this.logger = logger;
+ this.PacketPool = new ObjectPool(() => new Packet(this));
+ }
+
+ internal static Socket CreateSocket(IPMode ipMode)
+ {
+ Socket socket;
+ if (ipMode == IPMode.IPv4)
+ {
+ socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ }
+ else
+ {
+ if (!Socket.OSSupportsIPv6)
+ throw new InvalidOperationException("IPV6 not supported!");
+
+ socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
+ socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
+ }
+
+ try
+ {
+ socket.DontFragment = false;
+ }
+ catch { }
+
+ try
+ {
+ const int SIO_UDP_CONNRESET = -1744830452;
+ socket.IOControl(SIO_UDP_CONNRESET, new byte[1], null);
+ }
+ catch { } // Only necessary on Windows
+
+ return socket;
+ }
+
+ ///
+ /// Writes the given bytes to the connection.
+ ///
+ /// The bytes to write.
+ protected abstract void WriteBytesToConnection(byte[] bytes, int length);
+
+ ///
+ public override SendErrors Send(MessageWriter msg)
+ {
+ if (this._state != ConnectionState.Connected)
+ {
+ return SendErrors.Disconnected;
+ }
+
+ try
+ {
+ byte[] buffer = new byte[msg.Length];
+ Buffer.BlockCopy(msg.Buffer, 0, buffer, 0, msg.Length);
+
+ switch (msg.SendOption)
+ {
+ case SendOption.Reliable:
+ ResetKeepAliveTimer();
+
+ AttachReliableID(buffer, 1);
+ WriteBytesToConnection(buffer, buffer.Length);
+ Statistics.LogReliableSend(buffer.Length - 3);
+ break;
+
+ default:
+ WriteBytesToConnection(buffer, buffer.Length);
+ Statistics.LogUnreliableSend(buffer.Length - 1);
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ this.logger?.WriteError("Unknown exception while sending: " + e);
+ return SendErrors.Unknown;
+ }
+
+ return SendErrors.None;
+ }
+
+ ///
+ /// Handles the reliable/fragmented sending from this connection.
+ ///
+ /// The data being sent.
+ /// The specified as its byte value.
+ /// The callback to invoke when this packet is acknowledged.
+ /// The bytes that should actually be sent.
+ protected virtual void HandleSend(byte[] data, byte sendOption, Action ackCallback = null)
+ {
+ switch (sendOption)
+ {
+ case (byte)UdpSendOption.Ping:
+ case (byte)SendOption.Reliable:
+ case (byte)UdpSendOption.Hello:
+ ReliableSend(sendOption, data, ackCallback);
+ break;
+
+ //Treat all else as unreliable
+ default:
+ UnreliableSend(sendOption, data);
+ break;
+ }
+ }
+
+ ///
+ /// Handles the receiving of data.
+ ///
+ /// The buffer containing the bytes received.
+ protected internal virtual void HandleReceive(MessageReader message, int bytesReceived)
+ {
+ ushort id;
+ switch (message.Buffer[0])
+ {
+ //Handle reliable receives
+ case (byte)SendOption.Reliable:
+ ReliableMessageReceive(message, bytesReceived);
+ break;
+
+ //Handle acknowledgments
+ case (byte)UdpSendOption.Acknowledgement:
+ AcknowledgementMessageReceive(message.Buffer, bytesReceived);
+ message.Recycle();
+ break;
+
+ //We need to acknowledge hello and ping messages but dont want to invoke any events!
+ case (byte)UdpSendOption.Ping:
+ ProcessReliableReceive(message.Buffer, 1, out id);
+ Statistics.LogHelloReceive(bytesReceived);
+ message.Recycle();
+ break;
+ case (byte)UdpSendOption.Hello:
+ ProcessReliableReceive(message.Buffer, 1, out id);
+ Statistics.LogHelloReceive(bytesReceived);
+ message.Recycle();
+ break;
+
+ case (byte)UdpSendOption.Disconnect:
+ message.Offset = 1;
+ message.Position = 0;
+ DisconnectRemote("The remote sent a disconnect request", message);
+ message.Recycle();
+ break;
+
+ case (byte)SendOption.None:
+ InvokeDataReceived(SendOption.None, message, 1, bytesReceived);
+ Statistics.LogUnreliableReceive(bytesReceived - 1, bytesReceived);
+ break;
+
+ // Treat everything else as garbage
+ default:
+ message.Recycle();
+
+ // TODO: A new stat for unused data
+ Statistics.LogUnreliableReceive(bytesReceived - 1, bytesReceived);
+ break;
+ }
+ }
+
+ ///
+ /// Sends bytes using the unreliable UDP protocol.
+ ///
+ /// The SendOption to attach.
+ /// The data.
+ void UnreliableSend(byte sendOption, byte[] data)
+ {
+ this.UnreliableSend(sendOption, data, 0, data.Length);
+ }
+
+ ///
+ /// Sends bytes using the unreliable UDP protocol.
+ ///
+ /// The data.
+ /// The SendOption to attach.
+ ///
+ ///
+ void UnreliableSend(byte sendOption, byte[] data, int offset, int length)
+ {
+ byte[] bytes = new byte[length + 1];
+
+ //Add message type
+ bytes[0] = sendOption;
+
+ //Copy data into new array
+ Buffer.BlockCopy(data, offset, bytes, bytes.Length - length, length);
+
+ //Write to connection
+ WriteBytesToConnection(bytes, bytes.Length);
+
+ Statistics.LogUnreliableSend(length);
+ }
+
+ ///
+ /// Helper method to invoke the data received event.
+ ///
+ /// The send option the message was received with.
+ /// The buffer received.
+ /// The offset of data in the buffer.
+ void InvokeDataReceived(SendOption sendOption, MessageReader buffer, int dataOffset, int bytesReceived)
+ {
+ buffer.Offset = dataOffset;
+ buffer.Length = bytesReceived - dataOffset;
+ buffer.Position = 0;
+
+ InvokeDataReceived(buffer, sendOption);
+ }
+
+ ///
+ /// Sends a hello packet to the remote endpoint.
+ ///
+ /// The callback to invoke when the hello packet is acknowledged.
+ protected void SendHello(byte[] bytes, Action acknowledgeCallback)
+ {
+ //First byte of handshake is version indicator so add data after
+ byte[] actualBytes;
+ if (bytes == null)
+ {
+ actualBytes = new byte[1];
+ }
+ else
+ {
+ actualBytes = new byte[bytes.Length + 1];
+ Buffer.BlockCopy(bytes, 0, actualBytes, 1, bytes.Length);
+ }
+
+ HandleSend(actualBytes, (byte)UdpSendOption.Hello, acknowledgeCallback);
+ }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ DisposeKeepAliveTimer();
+ DisposeReliablePackets();
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
--
cgit v1.1-26-g67d0