/// </summary>
/// <remarks>
/// <para>
- /// Connection is the base class for all connections that Hazel can make. It provides common functionality and a
+ /// Connection is the base class for all connections that Hazel can make. It provides common functionality and a
/// standard interface to allow connections to be swapped easily.
/// </para>
/// <para>
/// </list>
/// </para>
/// </remarks>
- /// <threadsafety static="true" instance="true" />
+ /// <threadsafety static="true" instance="true"/>
public abstract class Connection : IDisposable
{
private static readonly ILogger Logger = Log.ForContext<Connection>();
/// </summary>
/// <remarks>
/// <para>
- /// DataReceived is invoked everytime a message is received from the end point of this connection, the message
- /// that was received can be found in the <see cref="DataReceivedEventArgs" /> alongside other information from the
+ /// DataReceived is invoked everytime a message is received from the end point of this connection, the message
+ /// that was received can be found in the <see cref="DataReceivedEventArgs"/> alongside other information from the
/// event.
/// </para>
/// <include file="DocInclude/common.xml" path="docs/item[@name='Event_Thread_Safety_Warning']/*" />
/// </remarks>
/// <example>
- /// <code language="C#" source="DocInclude/TcpClientExample.cs" />
+ /// <code language="C#" source="DocInclude/TcpClientExample.cs"/>
/// </example>
public Func<DataReceivedEventArgs, ValueTask> DataReceived;
public int TestLagMs = -1;
public int TestDropRate = 0;
protected int testDropCount = 0;
-
+
/// <summary>
/// Called when the end point disconnects or an error occurs.
/// </summary>
/// <remarks>
/// <para>
- /// Disconnected is invoked when the connection is closed due to an exception occuring or because the remote
- /// end point disconnected. If it was invoked due to an exception occuring then the exception is available
- /// in the <see cref="DisconnectedEventArgs" /> passed with the event.
+ /// Disconnected is invoked when the connection is closed due to an exception occuring or because the remote
+ /// end point disconnected. If it was invoked due to an exception occuring then the exception is available
+ /// in the <see cref="DisconnectedEventArgs"/> passed with the event.
/// </para>
/// <include file="DocInclude/common.xml" path="docs/item[@name='Event_Thread_Safety_Warning']/*" />
/// </remarks>
/// <example>
- /// <code language="C#" source="DocInclude/TcpClientExample.cs" />
+ /// <code language="C#" source="DocInclude/TcpClientExample.cs"/>
/// </example>
public Func<DisconnectedEventArgs, ValueTask> Disconnected;
/// The remote end point of this Connection.
/// </summary>
/// <remarks>
- /// This is the end point that this connection is connected to (i.e. the other device). This returns an abstract
- /// <see cref="ConnectionEndPoint" /> which can then be cast to an appropriate end point depending on the
+ /// This is the end point that this connection is connected to (i.e. the other device). This returns an abstract
+ /// <see cref="ConnectionEndPoint"/> which can then be cast to an appropriate end point depending on the
/// connection type.
/// </remarks>
public IPEndPoint EndPoint { get; protected set; }
/// </summary>
/// <remarks>
/// All implementers should be aware that when this is set to ConnectionState.Connected it will
- /// release all threads that are blocked on <see cref="WaitOnConnect" />.
+ /// release all threads that are blocked on <see cref="WaitOnConnect"/>.
/// </remarks>
public ConnectionState State
{
{
return this._state;
}
-
+
protected set
{
this._state = value;
protected ConnectionState _state;
protected virtual void SetState(ConnectionState state) { }
-
+
/// <summary>
/// Constructor that initializes the ConnecitonStatistics object.
/// </summary>
/// <remarks>
- /// This constructor initialises <see cref="Statistics" /> with empty statistics and sets <see cref="State" /> to
- /// <see cref="ConnectionState.NotConnected" />.
+ /// This constructor initialises <see cref="Statistics"/> with empty statistics and sets <see cref="State"/> to
+ /// <see cref="ConnectionState.NotConnected"/>.
/// </remarks>
protected Connection()
{
}
/// <summary>
- /// Sends a number of bytes to the end point of the connection using the specified <see cref="MessageType" />.
+ /// Sends a number of bytes to the end point of the connection using the specified <see cref="MessageType"/>.
/// </summary>
/// <param name="msg">The message to send.</param>
/// <remarks>
/// <include file="DocInclude/common.xml" path="docs/item[@name='Connection_SendBytes_General']/*" />
/// <para>
/// The messageType parameter is only a request to use those options and the actual method used to send the
- /// data is up to the implementation. There are circumstances where this parameter may be ignored but in
+ /// data is up to the implementation. There are circumstances where this parameter may be ignored but in
/// general any implementer should aim to always follow the user's request.
/// </para>
/// </remarks>
public abstract ValueTask SendAsync(IMessageWriter msg);
/// <summary>
- /// Sends a number of bytes to the end point of the connection using the specified <see cref="MessageType" />.
+ /// Sends a number of bytes to the end point of the connection using the specified <see cref="MessageType"/>.
/// </summary>
/// <param name="bytes">The bytes of the message to send.</param>
/// <param name="messageType">The option specifying how the message should be sent.</param>
/// <include file="DocInclude/common.xml" path="docs/item[@name='Connection_SendBytes_General']/*" />
/// <para>
/// The messageType parameter is only a request to use those options and the actual method used to send the
- /// data is up to the implementation. There are circumstances where this parameter may be ignored but in
+ /// data is up to the implementation. There are circumstances where this parameter may be ignored but in
/// general any implementer should aim to always follow the user's request.
/// </para>
/// </remarks>
/// Invokes the DataReceived event.
/// </summary>
/// <param name="msg">The bytes received.</param>
- /// <param name="messageType">The <see cref="MessageType" /> the message was received with.</param>
+ /// <param name="messageType">The <see cref="MessageType"/> the message was received with.</param>
/// <remarks>
- /// Invokes the <see cref="DataReceived" /> event on this connection to alert subscribers a new message has been
+ /// Invokes the <see cref="DataReceived"/> event on this connection to alert subscribers a new message has been
/// received. The bytes and the send option that the message was sent with should be passed in to give to the
/// subscribers.
/// </remarks>
/// <param name="e">The exception, if any, that occurred to cause this.</param>
/// <param name="reader">Extra disconnect data</param>
/// <remarks>
- /// Invokes the <see cref="Disconnected" /> event to alert subscribres this connection has been disconnected either
- /// by the end point or because an error occurred. If an error occurred the error should be passed in in order to
+ /// Invokes the <see cref="Disconnected"/> event to alert subscribres this connection has been disconnected either
+ /// by the end point or because an error occurred. If an error occurred the error should be passed in in order to
/// pass to the subscribers, otherwise null can be passed in.
/// </remarks>
protected async ValueTask InvokeDisconnected(string e, IMessageReader reader)
}
/// <summary>
- /// For times when you want to force the disconnect handler to fire as well as close it.
- /// If you only want to close it, just use Dispose.
+ /// For times when you want to force the disconnect handler to fire as well as close it.
+ /// If you only want to close it, just use Dispose.
/// </summary>
public abstract ValueTask Disconnect(string reason, MessageWriter writer = null);
-
+
/// <summary>
/// Disposes of this NetworkConnection.
/// </summary>
/// </summary>
/// <remarks>
/// <para>
- /// ConnectionListeners are server side objects that listen for clients and create matching server side connections
+ /// ConnectionListeners are server side objects that listen for clients and create matching server side connections
/// for each client in a similar way to TCP does. These connections should be ready for communication immediately.
/// </para>
/// <para>
- /// Each time a client connects the <see cref="NewConnection" /> event will be invoked to alert all subscribers to
- /// the new connection. A disconnected event is then present on the <see cref="Connection" /> that is passed to the
+ /// Each time a client connects the <see cref="NewConnection"/> event will be invoked to alert all subscribers to
+ /// the new connection. A disconnected event is then present on the <see cref="Connection"/> that is passed to the
/// subscribers.
/// </para>
/// </remarks>
- /// <threadsafety static="true" instance="true" />
+ /// <threadsafety static="true" instance="true"/>
public abstract class ConnectionListener : IAsyncDisposable
{
private static readonly ILogger Logger = Log.ForContext<ConnectionListener>();
/// </summary>
/// <remarks>
/// <para>
- /// NewConnection is invoked each time a client connects to the listener. The
- /// <see cref="NewConnectionEventArgs" /> contains the new <see cref="Connection" /> for communication with this
+ /// NewConnection is invoked each time a client connects to the listener. The
+ /// <see cref="NewConnectionEventArgs"/> contains the new <see cref="Connection"/> for communication with this
/// client.
/// </para>
/// <para>
- /// Hazel may or may not store connections so it is your responsibility to keep track and properly Dispose of
- /// connections to your server.
+ /// Hazel may or may not store connections so it is your responsibility to keep track and properly Dispose of
+ /// connections to your server.
/// </para>
/// <include file="DocInclude/common.xml" path="docs/item[@name='Event_Thread_Safety_Warning']/*" />
/// </remarks>
/// <example>
- /// <code language="C#" source="DocInclude/TcpListenerExample.cs" />
+ /// <code language="C#" source="DocInclude/TcpListenerExample.cs"/>
/// </example>
public Func<NewConnectionEventArgs, ValueTask> NewConnection;
/// </summary>
/// <remarks>
/// <para>
- /// This instructs the listener to begin listening for new clients connecting to the server. When a new client
- /// connects the <see cref="NewConnection" /> event will be invoked containing the connection to the new client.
+ /// This instructs the listener to begin listening for new clients connecting to the server. When a new client
+ /// connects the <see cref="NewConnection"/> event will be invoked containing the connection to the new client.
/// </para>
/// <para>
- /// To stop listening you should call <see cref="DisposeAsync()" />.
+ /// To stop listening you should call <see cref="DisposeAsync()"/>.
/// </para>
/// </remarks>
/// <example>
- /// <code language="C#" source="DocInclude/TcpListenerExample.cs" />
+ /// <code language="C#" source="DocInclude/TcpListenerExample.cs"/>
/// </example>
public abstract Task StartAsync();
/// <param name="msg">The user sent bytes that were received as part of the handshake.</param>
/// <param name="connection">The connection to pass in the arguments.</param>
/// <remarks>
- /// Implementers should call this to invoke the <see cref="NewConnection" /> event before data is received so that
+ /// Implementers should call this to invoke the <see cref="NewConnection"/> event before data is received so that
/// subscribers do not miss any data that may have been sent immediately after connecting.
/// </remarks>
internal async Task InvokeNewConnection(IMessageReader msg, Connection connection)
namespace Impostor.Hazel
{
/// <summary>
- /// Represents the state a <see cref="Connection" /> is currently in.
+ /// Represents the state a <see cref="Connection"/> is currently in.
/// </summary>
public enum ConnectionState
{
/// The Connection has either not been established yet or has been disconnected.
/// </summary>
NotConnected,
-
+
/// <summary>
/// The Connection is currently connecting to an endpoint.
/// </summary>
using System.Threading;
[assembly: InternalsVisibleTo("Hazel.Tests")]
-
namespace Impostor.Hazel
{
/// <summary>
- /// Holds statistics about the traffic through a <see cref="Connection" />.
+ /// Holds statistics about the traffic through a <see cref="Connection"/>.
/// </summary>
- /// <threadsafety static="true" instance="true" />
+ /// <threadsafety static="true" instance="true"/>
public class ConnectionStatistics
{
private const int ExpectedMTU = 1200;
/// The number of messages sent larger than 576 bytes. This is smaller than most default MTUs.
/// </summary>
/// <remarks>
- /// This is the number of unreliable messages that were sent from the <see cref="Connection" />, incremented
- /// each time that LogUnreliableSend is called by the Connection. Messages that caused an error are not
+ /// This is the number of unreliable messages that were sent from the <see cref="Connection"/>, incremented
+ /// each time that LogUnreliableSend is called by the Connection. Messages that caused an error are not
/// counted and messages are only counted once all other operations in the send are complete.
/// </remarks>
public int FragmentableMessagesSent
/// The number of unreliable messages sent.
/// </summary>
/// <remarks>
- /// This is the number of unreliable messages that were sent from the <see cref="Connection" />, incremented
- /// each time that LogUnreliableSend is called by the Connection. Messages that caused an error are not
+ /// This is the number of unreliable messages that were sent from the <see cref="Connection"/>, incremented
+ /// each time that LogUnreliableSend is called by the Connection. Messages that caused an error are not
/// counted and messages are only counted once all other operations in the send are complete.
/// </remarks>
public int UnreliableMessagesSent
/// The number of reliable messages sent.
/// </summary>
/// <remarks>
- /// This is the number of reliable messages that were sent from the <see cref="Connection" />, incremented
- /// each time that LogReliableSend is called by the Connection. Messages that caused an error are not
+ /// This is the number of reliable messages that were sent from the <see cref="Connection"/>, incremented
+ /// each time that LogReliableSend is called by the Connection. Messages that caused an error are not
/// counted and messages are only counted once all other operations in the send are complete.
/// </remarks>
public int ReliableMessagesSent
/// The number of fragmented messages sent.
/// </summary>
/// <remarks>
- /// This is the number of fragmented messages that were sent from the <see cref="Connection" />, incremented
- /// each time that LogFragmentedSend is called by the Connection. Messages that caused an error are not
+ /// This is the number of fragmented messages that were sent from the <see cref="Connection"/>, incremented
+ /// each time that LogFragmentedSend is called by the Connection. Messages that caused an error are not
/// counted and messages are only counted once all other operations in the send are complete.
/// </remarks>
public int FragmentedMessagesSent
/// The number of acknowledgement messages sent.
/// </summary>
/// <remarks>
- /// This is the number of acknowledgements that were sent from the <see cref="Connection" />, incremented
- /// each time that LogAcknowledgementSend is called by the Connection. Messages that caused an error are not
+ /// This is the number of acknowledgements that were sent from the <see cref="Connection"/>, incremented
+ /// each time that LogAcknowledgementSend is called by the Connection. Messages that caused an error are not
/// counted and messages are only counted once all other operations in the send are complete.
/// </remarks>
public int AcknowledgementMessagesSent
/// The number of hello messages sent.
/// </summary>
/// <remarks>
- /// This is the number of hello messages that were sent from the <see cref="Connection" />, incremented
- /// each time that LogHelloSend is called by the Connection. Messages that caused an error are not
+ /// This is the number of hello messages that were sent from the <see cref="Connection"/>, incremented
+ /// each time that LogHelloSend is called by the Connection. Messages that caused an error are not
/// counted and messages are only counted once all other operations in the send are complete.
/// </remarks>
public int HelloMessagesSent
/// </summary>
/// <remarks>
/// <para>
- /// This is the number of bytes of data (i.e. user bytes) that were sent from the <see cref="Connection" />,
- /// accumulated each time that LogSend is called by the Connection. Messages that caused an error are not
+ /// This is the number of bytes of data (i.e. user bytes) that were sent from the <see cref="Connection"/>,
+ /// accumulated each time that LogSend is called by the Connection. Messages that caused an error are not
/// counted and messages are only counted once all other operations in the send are complete.
/// </para>
/// <para>
- /// For the number of bytes including protocol bytes see <see cref="TotalBytesSent" />.
+ /// For the number of bytes including protocol bytes see <see cref="TotalBytesSent"/>.
/// </para>
/// </remarks>
public long DataBytesSent
/// </summary>
/// <remarks>
/// <para>
- /// This is the total number of bytes (the data bytes plus protocol bytes) that were sent from the
- /// <see cref="Connection" />, accumulated each time that LogSend is called by the Connection. Messages that
- /// caused an error are not counted and messages are only counted once all other operations in the send are
+ /// This is the total number of bytes (the data bytes plus protocol bytes) that were sent from the
+ /// <see cref="Connection"/>, accumulated each time that LogSend is called by the Connection. Messages that
+ /// caused an error are not counted and messages are only counted once all other operations in the send are
/// complete.
/// </para>
/// <para>
- /// For the number of data bytes excluding protocol bytes see <see cref="DataBytesSent" />.
+ /// For the number of data bytes excluding protocol bytes see <see cref="DataBytesSent"/>.
/// </para>
/// </remarks>
public long TotalBytesSent
return UnreliableMessagesReceived + ReliableMessagesReceived + FragmentedMessagesReceived + AcknowledgementMessagesReceived + helloMessagesReceived;
}
}
-
+
/// <summary>
/// The number of unreliable messages received.
/// </summary>
/// <remarks>
- /// This is the number of unreliable messages that were received by the <see cref="Connection" />, incremented
+ /// This is the number of unreliable messages that were received by the <see cref="Connection"/>, incremented
/// each time that LogUnreliableReceive is called by the Connection. Messages are counted before the receive event is invoked.
/// </remarks>
public int UnreliableMessagesReceived
/// The number of reliable messages received.
/// </summary>
/// <remarks>
- /// This is the number of reliable messages that were received by the <see cref="Connection" />, incremented
+ /// This is the number of reliable messages that were received by the <see cref="Connection"/>, incremented
/// each time that LogReliableReceive is called by the Connection. Messages are counted before the receive event is invoked.
/// </remarks>
public int ReliableMessagesReceived
/// The number of fragmented messages received.
/// </summary>
/// <remarks>
- /// This is the number of fragmented messages that were received by the <see cref="Connection" />, incremented
+ /// This is the number of fragmented messages that were received by the <see cref="Connection"/>, incremented
/// each time that LogFragmentedReceive is called by the Connection. Messages are counted before the receive event is invoked.
/// </remarks>
public int FragmentedMessagesReceived
/// The number of acknowledgement messages received.
/// </summary>
/// <remarks>
- /// This is the number of acknowledgement messages that were received by the <see cref="Connection" />, incremented
+ /// This is the number of acknowledgement messages that were received by the <see cref="Connection"/>, incremented
/// each time that LogAcknowledgemntReceive is called by the Connection. Messages are counted before the receive event is invoked.
/// </remarks>
public int AcknowledgementMessagesReceived
/// The number of ping messages received.
/// </summary>
/// <remarks>
- /// This is the number of hello messages that were received by the <see cref="Connection" />, incremented
+ /// This is the number of hello messages that were received by the <see cref="Connection"/>, incremented
/// each time that LogHelloReceive is called by the Connection. Messages are counted before the receive event is invoked.
/// </remarks>
public int PingMessagesReceived
/// The number of hello messages received.
/// </summary>
/// <remarks>
- /// This is the number of hello messages that were received by the <see cref="Connection" />, incremented
+ /// This is the number of hello messages that were received by the <see cref="Connection"/>, incremented
/// each time that LogHelloReceive is called by the Connection. Messages are counted before the receive event is invoked.
/// </remarks>
public int HelloMessagesReceived
/// </summary>
/// <remarks>
/// <para>
- /// This is the number of bytes of data (i.e. user bytes) that were received by the <see cref="Connection" />,
+ /// This is the number of bytes of data (i.e. user bytes) that were received by the <see cref="Connection"/>,
/// accumulated each time that LogReceive is called by the Connection. Messages are counted before the receive
/// event is invoked.
/// </para>
/// <para>
- /// For the number of bytes including protocol bytes see <see cref="TotalBytesReceived" />.
+ /// For the number of bytes including protocol bytes see <see cref="TotalBytesReceived"/>.
/// </para>
/// </remarks>
public long DataBytesReceived
/// </summary>
/// <remarks>
/// <para>
- /// This is the total number of bytes (the data bytes plus protocol bytes) that were received by the
- /// <see cref="Connection" />, accumulated each time that LogReceive is called by the Connection. Messages are
+ /// This is the total number of bytes (the data bytes plus protocol bytes) that were received by the
+ /// <see cref="Connection"/>, accumulated each time that LogReceive is called by the Connection. Messages are
/// counted before the receive event is invoked.
/// </para>
/// <para>
- /// For the number of data bytes excluding protocol bytes see <see cref="DataBytesReceived" />.
+ /// For the number of data bytes excluding protocol bytes see <see cref="DataBytesReceived"/>.
/// </para>
/// </remarks>
public long TotalBytesReceived
public readonly IMessageReader Message;
/// <summary>
- /// The <see cref="Type" /> the data was sent with.
+ /// The <see cref="Type"/> the data was sent with.
/// </summary>
public readonly MessageType Type;
-
+
public DataReceivedEventArgs(Connection sender, IMessageReader msg, MessageType type)
{
this.Sender = sender;
public class DisconnectedEventArgs : EventArgs
{
/// <summary>
- /// Optional disconnect reason. May be null.
+ /// Optional disconnect reason. May be null.
/// </summary>
public readonly string Reason;
/// <summary>
- /// Optional data sent with a disconnect message. May be null.
- /// You must not recycle this. If you need the message outside of a callback, you should copy it.
+ /// Optional data sent with a disconnect message. May be null.
+ /// You must not recycle this. If you need the message outside of a callback, you should copy it.
/// </summary>
public readonly IMessageReader Message;
[Serializable]
public class HazelException : Exception
{
- internal HazelException(string msg) : base(msg)
+ internal HazelException(string msg) : base (msg)
{
+
}
- internal HazelException(string msg, Exception e) : base(msg, e)
+ internal HazelException(string msg, Exception e) : base (msg, e)
{
+
}
}
}
/// Represents the IP version that a connection or listener will use.
/// </summary>
/// <remarks>
- /// If you wand a client to connect or be able to connect using IPv6 then you should use <see cref="IPv4AndIPv6" />,
- /// this sets the underlying sockets to use IPv6 but still allow IPv4 sockets to connect for backwards compatability
+ /// If you wand a client to connect or be able to connect using IPv6 then you should use <see cref="IPv4AndIPv6"/>,
+ /// this sets the underlying sockets to use IPv6 but still allow IPv4 sockets to connect for backwards compatability
/// and hence it is the default IPMode in most cases.
/// </remarks>
public enum IPMode
IPv4,
/// <summary>
- /// Instruction to use IPv6 only, IPv4 connections will not be able to connect. IPv4 addresses can be connected
+ /// Instruction to use IPv6 only, IPv4 connections will not be able to connect. IPv4 addresses can be connected
/// by converting to IPv6 addresses.
/// </summary>
- IPv6,
+ IPv6
}
}
/// <summary>
/// Interface for all items that can be returned to an object pool.
/// </summary>
- /// <threadsafety static="true" instance="true" />
+ /// <threadsafety static="true" instance="true"/>
public interface IRecyclable
{
/// <summary>
<Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <TargetFramework>net5.0</TargetFramework>
- <DefineConstants>HAZEL_BAG</DefineConstants>
- </PropertyGroup>
+ <PropertyGroup>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <TargetFramework>net5.0</TargetFramework>
+ <DefineConstants>HAZEL_BAG</DefineConstants>
+ </PropertyGroup>
- <ItemGroup>
- <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="5.0.4" />
- <PackageReference Include="Serilog" Version="2.10.0" />
- </ItemGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="5.0.0" />
+ <PackageReference Include="Serilog" Version="2.10.0" />
+ </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\Impostor.Api\Impostor.Api.csproj" />
- </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Impostor.Api\Impostor.Api.csproj" />
+ </ItemGroup>
</Project>
public bool ReadBoolean()
{
- var val = FastByte();
+ byte val = FastByte();
return val != 0;
}
public uint ReadPackedUInt32()
{
- var readMore = true;
- var shift = 0;
+ bool readMore = true;
+ int shift = 0;
uint output = 0;
while (readMore)
{
- var b = FastByte();
+ byte b = FastByte();
if (b >= 0x80)
{
readMore = true;
public void CopyTo(IMessageWriter writer)
{
- writer.Write((ushort)Length);
- writer.Write((byte)Tag);
+ writer.Write((ushort) Length);
+ writer.Write((byte) Tag);
writer.Write(Buffer.AsMemory(Offset, Length));
}
System.Buffer.BlockCopy(Buffer, offsetEnd, Buffer, offsetStart, lengthToCopy);
- ((MessageReader)message).Parent.AdjustLength(message.Offset, message.Length + 3);
+ ((MessageReader) message).Parent.AdjustLength(message.Offset, message.Length + 3);
}
private void AdjustLength(int offset, int amount)
public Vector2 ReadVector2()
{
const float range = 50f;
-
- var x = ReadUInt16() / (float)ushort.MaxValue;
- var y = ReadUInt16() / (float)ushort.MaxValue;
+
+ var x = ReadUInt16() / (float) ushort.MaxValue;
+ var y = ReadUInt16() / (float) ushort.MaxValue;
return new Vector2(Mathf.Lerp(-range, range, x), Mathf.Lerp(-range, range, y));
}
-using System;
+using Impostor.Api.Games;
+using Impostor.Api.Net.Messages;
+
+using System;
using System.Collections.Generic;
using System.Net;
using System.Numerics;
using System.Text;
-using Impostor.Api.Games;
using Impostor.Api.Net.Inner;
-using Impostor.Api.Net.Messages;
using Impostor.Api.Unity;
namespace Impostor.Hazel
{
if (includeHeader)
{
- var output = new byte[this.Length];
+ byte[] output = new byte[this.Length];
System.Buffer.BlockCopy(this.Buffer, 0, output, 0, this.Length);
return output;
}
switch (this.SendOption)
{
case MessageType.Reliable:
- {
- var output = new byte[this.Length - 3];
- System.Buffer.BlockCopy(this.Buffer, 3, output, 0, this.Length - 3);
- return output;
- }
+ {
+ byte[] output = new byte[this.Length - 3];
+ System.Buffer.BlockCopy(this.Buffer, 3, output, 0, this.Length - 3);
+ return output;
+ }
case MessageType.Unreliable:
- {
- var output = new byte[this.Length - 1];
- System.Buffer.BlockCopy(this.Buffer, 1, output, 0, this.Length - 1);
- return output;
- }
+ {
+ byte[] output = new byte[this.Length - 1];
+ System.Buffer.BlockCopy(this.Buffer, 1, output, 0, this.Length - 1);
+ return output;
+ }
default:
throw new ArgumentOutOfRangeException();
}
throw new NotImplementedException();
}
+ ///
/// <param name="sendOption">The option specifying how the message should be sent.</param>
public static MessageWriter Get(MessageType sendOption = MessageType.Unreliable)
{
public void Write(Vector2 vector)
{
- Write((ushort)(Mathf.ReverseLerp(vector.X) * (double)ushort.MaxValue));
- Write((ushort)(Mathf.ReverseLerp(vector.Y) * (double)ushort.MaxValue));
+ Write((ushort)(Mathf.ReverseLerp(vector.X) * (double) ushort.MaxValue));
+ Write((ushort)(Mathf.ReverseLerp(vector.Y) * (double) ushort.MaxValue));
}
///
public void EndMessage()
{
var lastMessageStart = messageStarts.Pop();
- var length = (ushort)(this.Position - lastMessageStart - 3); // Minus length and type byte
+ ushort length = (ushort)(this.Position - lastMessageStart - 3); // Minus length and type byte
this.Buffer[lastMessageStart] = (byte)length;
this.Buffer[lastMessageStart + 1] = (byte)(length >> 8);
}
{
fixed (byte* ptr = &this.Buffer[this.Position])
{
- var valuePtr = (byte*)&value;
+ byte* valuePtr = (byte*)&value;
*ptr = *valuePtr;
*(ptr + 1) = *(valuePtr + 1);
{
do
{
- var b = (byte)(value & 0xFF);
+ byte b = (byte)(value & 0xFF);
if (value >= 0x80)
{
b |= 0x80;
public void Write(MessageWriter msg, bool includeHeader)
{
- var offset = 0;
+ int offset = 0;
if (!includeHeader)
{
switch (msg.SendOption)
byte b;
unsafe
{
- var i = 1;
- var bp = (byte*)&i;
+ int i = 1;
+ byte* bp = (byte*)&i;
b = *bp;
}
ReceivedZeroBytes,
PingsWithoutResponse,
ReliablePacketWithoutResponse,
- ConnectionDisconnected,
+ ConnectionDisconnected
}
/// <summary>
- /// Abstract base class for a <see cref="Connection" /> to a remote end point via a network protocol like TCP or UDP.
+ /// Abstract base class for a <see cref="Connection"/> to a remote end point via a network protocol like TCP or UDP.
/// </summary>
- /// <threadsafety static="true" instance="true" />
+ /// <threadsafety static="true" instance="true"/>
public abstract class NetworkConnection : Connection
{
/// <summary>
- /// An event that gives us a chance to send well-formed disconnect messages to clients when an internal disconnect happens.
+ /// An event that gives us a chance to send well-formed disconnect messages to clients when an internal disconnect happens.
/// </summary>
public Func<HazelInternalErrors, MessageWriter> OnInternalDisconnect;
/// The remote end point of this connection.
/// </summary>
/// <remarks>
- /// This is the end point of the other device given as an <see cref="System.Net.EndPoint" /> rather than a generic
- /// <see cref="ConnectionEndPoint" /> as the base <see cref="Connection" /> does.
+ /// This is the end point of the other device given as an <see cref="System.Net.EndPoint"/> rather than a generic
+ /// <see cref="ConnectionEndPoint"/> as the base <see cref="Connection"/> does.
/// </remarks>
public IPEndPoint RemoteEndPoint { get; protected set; }
}
/// <summary>
- /// Called when socket is disconnected internally
+ /// Called when socket is disconnected internally
/// </summary>
internal async ValueTask DisconnectInternal(HazelInternalErrors error, string reason)
{
var handler = this.OnInternalDisconnect;
if (handler != null)
{
- var messageToRemote = handler(error);
+ MessageWriter messageToRemote = handler(error);
if (messageToRemote != null)
{
try
namespace Impostor.Hazel
{
/// <summary>
- /// Abstract base class for a <see cref="ConnectionListener" /> for network based connections.
+ /// Abstract base class for a <see cref="ConnectionListener"/> for network based connections.
/// </summary>
- /// <threadsafety static="true" instance="true" />
+ /// <threadsafety static="true" instance="true"/>
public abstract class NetworkConnectionListener : ConnectionListener
{
/// <summary>
public struct NewConnectionEventArgs
{
/// <summary>
- /// The data received from the client in the handshake.
- /// This data is yours. Remember to recycle it.
+ /// The data received from the client in the handshake.
+ /// This data is yours. Remember to recycle it.
/// </summary>
public readonly IMessageReader HandshakeData;
/// <summary>
- /// The <see cref="Connection" /> to the new client.
+ /// The <see cref="Connection"/> to the new client.
/// </summary>
public readonly Connection Connection;
/// A fairly simple object pool for items that will be created a lot.
/// </summary>
/// <typeparam name="T">The type that is pooled.</typeparam>
- /// <threadsafety static="true" instance="true" />
+ /// <threadsafety static="true" instance="true"/>
public sealed class ObjectPoolCustom<T> where T : IRecyclable
{
private int numberCreated;
/// </summary>
/// <returns></returns>
private readonly Func<T> objectFactory;
-
+
/// <summary>
/// Internal constructor for our ObjectPool.
/// </summary>
internal T GetObject()
{
#if HAZEL_BAG
- if (!pool.TryTake(out var item))
+ if (!pool.TryTake(out T item))
{
Interlocked.Increment(ref numberCreated);
item = objectFactory.Invoke();
/// <param name="item">The item to return.</param>
internal void PutObject(T item)
{
- if (inuse.TryRemove(item, out var b))
+ if (inuse.TryRemove(item, out bool b))
{
#if HAZEL_BAG
pool.Add(item);
Hello = 8,
/// <summary>
- /// A single byte of continued existence
+ /// A single byte of continued existence
/// </summary>
Ping = 12,
return;
}
- if (numBytes < 3
+ if (numBytes < 3
|| buffer[0] != 4 || buffer[1] != 2)
{
this.StartListen();
return;
}
- var ipEnd = (IPEndPoint)endpt;
- var data = UTF8Encoding.UTF8.GetString(buffer, 2, numBytes - 2);
- var dataHash = data.GetHashCode();
+ IPEndPoint ipEnd = (IPEndPoint)endpt;
+ string data = UTF8Encoding.UTF8.GetString(buffer, 2, numBytes - 2);
+ int dataHash = data.GetHashCode();
lock (packets)
{
- var found = false;
- for (var i = 0; i < this.packets.Count; ++i)
+ bool found = false;
+ for (int i = 0; i < this.packets.Count; ++i)
{
var pkt = this.packets[i];
if (pkt == null || pkt.Data == null)
{
if (this.socket != null)
{
- try { this.socket.Shutdown(SocketShutdown.Both); }
- catch { }
-
- try { this.socket.Close(); }
- catch { }
-
- try { this.socket.Dispose(); }
- catch { }
-
+ try { this.socket.Shutdown(SocketShutdown.Both); } catch { }
+ try { this.socket.Close(); } catch { }
+ try { this.socket.Dispose(); } catch { }
this.socket = null;
}
}
}
-}
+}
\ No newline at end of file
///
public void SetData(string data)
{
- var len = UTF8Encoding.UTF8.GetByteCount(data);
+ int len = UTF8Encoding.UTF8.GetByteCount(data);
this.data = new byte[len + 2];
this.data[0] = 4;
this.data[1] = 2;
{
if (this.socket != null)
{
- try { this.socket.Shutdown(SocketShutdown.Both); }
- catch { }
-
- try { this.socket.Close(); }
- catch { }
-
- try { this.socket.Dispose(); }
- catch { }
-
+ try { this.socket.Shutdown(SocketShutdown.Both); } catch { }
+ try { this.socket.Close(); } catch { }
+ try { this.socket.Dispose(); } catch { }
this.socket = null;
}
}
}
-}
+}
\ No newline at end of file
using System;
+using System.Buffers;
using System.Net;
using System.Net.Sockets;
using System.Threading;
+using System.Threading.Channels;
using System.Threading.Tasks;
using Impostor.Api.Net.Messages;
using Microsoft.Extensions.ObjectPool;
/// <summary>
/// Represents a client's connection to a server that uses the UDP protocol.
/// </summary>
- /// <inheritdoc />
+ /// <inheritdoc/>
public sealed class UdpClientConnection : UdpConnection
{
private static readonly ILogger Logger = Log.ForContext<UdpClientConnection>();
/// <summary>
/// Creates a new UdpClientConnection.
/// </summary>
- /// <param name="remoteEndPoint">A <see cref="NetworkEndPoint" /> to connect to.</param>
+ /// <param name="remoteEndPoint">A <see cref="NetworkEndPoint"/> to connect to.</param>
public UdpClientConnection(IPEndPoint remoteEndPoint, ObjectPool<MessageReader> readerPool, IPMode ipMode = IPMode.IPv4) : base(null, readerPool)
{
EndPoint = remoteEndPoint;
_socket = new UdpClient
{
- DontFragment = false,
+ DontFragment = false
};
_reliablePacketTimer = new Timer(ManageReliablePacketsInternal, null, 100, Timeout.Infinite);
{
State = ConnectionState.NotConnected;
- try { _socket.Close(); }
- catch { }
-
- try { _socket.Dispose(); }
- catch { }
+ try { _socket.Close(); } catch { }
+ try { _socket.Dispose(); } catch { }
_reliablePacketTimer.Dispose();
_connectWaitLock.Dispose();
{
partial class UdpConnection
{
+
/// <summary>
/// Class to hold packet data
/// </summary>
ResetKeepAliveTimer();
}
}
-
private int keepAliveInterval = 1500;
public int MissingPingsUntilDisconnect { get; set; } = 6;
// pings should cause a disconnect.
private async ValueTask SendPing()
{
- var id = (ushort)Interlocked.Increment(ref lastIDAllocated);
+ ushort id = (ushort)Interlocked.Increment(ref lastIDAllocated);
- var bytes = new byte[3];
+ byte[] bytes = new byte[3];
bytes[0] = (byte)UdpSendOption.Ping;
bytes[1] = (byte)(id >> 8);
bytes[2] = (byte)id;
}
}
}
-}
+}
\ No newline at end of file
/// </summary>
/// <remarks>
/// <para>
- /// For reliable delivery data is resent at specified intervals unless an acknowledgement is received from the
+ /// For reliable delivery data is resent at specified intervals unless an acknowledgement is received from the
/// receiving device. The ResendTimeout specifies the interval between the packets being resent, each time a packet
- /// is resent the interval is increased for that packet until the duration exceeds the <see cref="DisconnectTimeout" /> value.
+ /// is resent the interval is increased for that packet until the duration exceeds the <see cref="DisconnectTimeout"/> value.
/// </para>
/// <para>
- /// Setting this to its default of 0 will mean the timeout is 2 times the value of the average ping, usually
+ /// Setting this to its default of 0 will mean the timeout is 2 times the value of the average ping, usually
/// resulting in a more dynamic resend that responds to endpoints on slower or faster connections.
/// </para>
/// </remarks>
public volatile int ResendTimeout = 0;
/// <summary>
- /// Max number of times to resend. 0 == no limit
+ /// Max number of times to resend. 0 == no limit
/// </summary>
public volatile int ResendLimit = 0;
/// <summary>
- /// A compounding multiplier to back off resend timeout.
- /// Applied to ping before first timeout when ResendTimeout == 0.
+ /// A compounding multiplier to back off resend timeout.
+ /// Applied to ping before first timeout when ResendTimeout == 0.
/// </summary>
public volatile float ResendPingMultiplier = 2;
internal ConcurrentDictionary<ushort, Packet> reliableDataPacketsSent = new ConcurrentDictionary<ushort, Packet>();
/// <summary>
- /// Packet ids that have not been received, but are expected.
+ /// Packet ids that have not been received, but are expected.
/// </summary>
private HashSet<ushort> reliableDataPacketsMissing = new HashSet<ushort>();
/// Returns the average ping to this endpoint.
/// </summary>
/// <remarks>
- /// This returns the average ping for a one-way trip as calculated from the reliable packets that have been sent
+ /// This returns the average ping for a one-way trip as calculated from the reliable packets that have been sent
/// and acknowledged by the endpoint.
/// </remarks>
public float AveragePingMs = 500;
/// The maximum times a message should be resent before marking the endpoint as disconnected.
/// </summary>
/// <remarks>
- /// Reliable packets will be resent at an interval defined in <see cref="ResendTimeout" /> for the number of times
+ /// Reliable packets will be resent at an interval defined in <see cref="ResendTimeout"/> for the number of times
/// specified here. Once a packet has been retransmitted this number of times and has not been acknowledged the
/// connection will be marked as disconnected and the <see cref="Connection.Disconnected">Disconnected</see> event
/// will be invoked.
var connection = this.Connection;
if (!this.Acknowledged && connection != null)
{
- var lifetime = this.Stopwatch.ElapsedMilliseconds;
+ long lifetime = this.Stopwatch.ElapsedMilliseconds;
if (lifetime >= connection.DisconnectTimeout)
{
- if (connection.reliableDataPacketsSent.TryRemove(this.Id, out var self))
+ if (connection.reliableDataPacketsSent.TryRemove(this.Id, out Packet self))
{
await connection.DisconnectInternal(HazelInternalErrors.ReliablePacketWithoutResponse, $"Reliable packet {self.Id} (size={this.Length}) was not ack'd after {lifetime}ms ({self.Retransmissions} resends)");
if (connection.ResendLimit != 0
&& this.Retransmissions > connection.ResendLimit)
{
- if (connection.reliableDataPacketsSent.TryRemove(this.Id, out var self))
+ if (connection.reliableDataPacketsSent.TryRemove(this.Id, out Packet self))
{
await connection.DisconnectInternal(HazelInternalErrors.ReliablePacketWithoutResponse, $"Reliable packet {self.Id} (size={this.Length}) was not ack'd after {self.Retransmissions} resends ({lifetime}ms)");
internal async ValueTask<int> ManageReliablePackets()
{
- var output = 0;
+ int output = 0;
if (this.reliableDataPacketsSent.Count > 0)
{
foreach (var kvp in this.reliableDataPacketsSent)
{
- var pkt = kvp.Value;
+ Packet pkt = kvp.Value;
try
{
/// <param name="ackCallback">The callback to make once the packet has been acknowledged.</param>
protected void AttachReliableID(byte[] buffer, int offset, int sendLength, Action ackCallback = null)
{
- var id = (ushort)Interlocked.Increment(ref lastIDAllocated);
+ ushort id = (ushort)Interlocked.Increment(ref lastIDAllocated);
buffer[offset] = (byte)(id >> 8);
buffer[offset + 1] = (byte)id;
- var packet = Packet.GetObject();
+ Packet packet = Packet.GetObject();
packet.Set(
id,
this,
//Inform keepalive not to send for a while
ResetKeepAliveTimer();
- var bytes = new byte[data.Length + 3];
+ byte[] bytes = new byte[data.Length + 3];
//Add message type
bytes[0] = sendOption;
*
* So...
*/
-
+
var result = true;
lock (reliableDataPacketsMissing)
{
//Calculate overwritePointer
- var overwritePointer = (ushort)(reliableReceiveLast - 32768);
+ ushort overwritePointer = (ushort)(reliableReceiveLast - 32768);
//Calculate if it is a new packet by examining if it is within the range
bool isNew;
if (overwritePointer < reliableReceiveLast)
- isNew = id > reliableReceiveLast || id <= overwritePointer; //Figure (2)
+ isNew = id > reliableReceiveLast || id <= overwritePointer; //Figure (2)
else
- isNew = id > reliableReceiveLast && id <= overwritePointer; //Figure (3)
-
+ isNew = id > reliableReceiveLast && id <= overwritePointer; //Figure (3)
+
//If it's new or we've not received anything yet
if (isNew)
{
// Mark items between the most recent receive and the id received as missing
if (id > reliableReceiveLast)
{
- for (var i = (ushort)(reliableReceiveLast + 1); i < id; i++)
+ for (ushort i = (ushort)(reliableReceiveLast + 1); i < id; i++)
{
reliableDataPacketsMissing.Add(i);
}
}
else
{
- var cnt = (ushort.MaxValue - reliableReceiveLast) + id;
+ int cnt = (ushort.MaxValue - reliableReceiveLast) + id;
for (ushort i = 1; i < cnt; ++i)
{
reliableDataPacketsMissing.Add((ushort)(i + reliableReceiveLast));
//Update the most recently received
reliableReceiveLast = id;
}
-
+
//Else it could be a missing packet
else
{
}
}
}
-
+
//Send an acknowledgement
await SendAck(id);
{
this.pingsSinceAck = 0;
- var id = (ushort)((bytes[1] << 8) + bytes[2]);
+ ushort id = (ushort)((bytes[1] << 8) + bytes[2]);
AcknowledgeMessageId(id);
if (bytes.Length == 4)
{
- var recentPackets = bytes[3];
- for (var i = 1; i <= 8; ++i)
+ byte recentPackets = bytes[3];
+ for (int i = 1; i <= 8; ++i)
{
if ((recentPackets & 1) != 0)
{
private void AcknowledgeMessageId(ushort id)
{
// Dispose of timer and remove from dictionary
- if (reliableDataPacketsSent.TryRemove(id, out var packet))
+ if (reliableDataPacketsSent.TryRemove(id, out Packet packet))
{
float rt = packet.Stopwatch.ElapsedMilliseconds;
this.AveragePingMs = Math.Max(50, this.AveragePingMs * .7f + rt * .3f);
}
}
- else if (this.activePingPackets.TryRemove(id, out var pingPkt))
+ else if (this.activePingPackets.TryRemove(id, out PingPacket pingPkt))
{
float rt = pingPkt.Stopwatch.ElapsedMilliseconds;
byte recentPackets = 0;
lock (this.reliableDataPacketsMissing)
{
- for (var i = 1; i <= 8; ++i)
+ for (int i = 1; i <= 8; ++i)
{
if (!this.reliableDataPacketsMissing.Contains((ushort)(id - i)))
{
}
}
- var bytes = new byte[]
+ byte[] bytes = new byte[]
{
(byte)UdpSendOption.Acknowledgement,
(byte)(id >> 8),
(byte)(id >> 0),
- recentPackets,
+ recentPackets
};
try
Pipeline = Channel.CreateUnbounded<byte[]>(new UnboundedChannelOptions
{
SingleReader = true,
- SingleWriter = true,
+ SingleWriter = true
});
}
/// <param name="length"></param>
protected abstract ValueTask WriteBytesToConnection(byte[] bytes, int length);
- /// <inheritdoc />
+ /// <inheritdoc/>
public override async ValueTask SendAsync(IMessageWriter msg)
{
if (this._state != ConnectionState.Connected)
throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?");
- var buffer = new byte[msg.Length];
+ byte[] buffer = new byte[msg.Length];
Buffer.BlockCopy(msg.Buffer, 0, buffer, 0, msg.Length);
switch (msg.SendOption)
}
}
- /// <inheritdoc />
+ /// <inheritdoc/>
/// <remarks>
/// <include file="DocInclude/common.xml" path="docs/item[@name='Connection_SendBytes_General']/*" />
/// <para>
- /// Udp connections can currently send messages using <see cref="SendOption.None" /> and
- /// <see cref="SendOption.Reliable" />. Fragmented messages are not currently supported and will default to
- /// <see cref="SendOption.None" /> until implemented.
+ /// Udp connections can currently send messages using <see cref="SendOption.None"/> and
+ /// <see cref="SendOption.Reliable"/>. Fragmented messages are not currently supported and will default to
+ /// <see cref="SendOption.None"/> until implemented.
/// </para>
/// </remarks>
public override async ValueTask SendBytes(byte[] bytes, MessageType sendOption = MessageType.Unreliable)
//Add header information and send
await HandleSend(bytes, (byte)sendOption);
}
-
+
/// <summary>
/// Handles the reliable/fragmented sending from this connection.
/// </summary>
/// <param name="data">The data being sent.</param>
- /// <param name="sendOption">The <see cref="SendOption" /> specified as its byte value.</param>
+ /// <param name="sendOption">The <see cref="SendOption"/> specified as its byte value.</param>
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
/// <returns>The bytes that should actually be sent.</returns>
protected async ValueTask HandleSend(byte[] data, byte sendOption, Action ackCallback = null)
case (byte)UdpSendOption.Hello:
await ReliableSend(sendOption, data, ackCallback);
break;
-
+
//Treat all else as unreliable
default:
await UnreliableSend(sendOption, data);
{
await DisconnectRemote("The remote sent a disconnect request", reader);
}
-
break;
-
+
//Treat everything else as unreliable
default:
using (var reader = message.Copy(1))
{
await InvokeDataReceived(reader, MessageType.Unreliable);
}
-
Statistics.LogUnreliableReceive(message.Length - 1, message.Length);
break;
}
/// <param name="length"></param>
async ValueTask UnreliableSend(byte sendOption, byte[] data, int offset, int length)
{
- var bytes = new byte[length + 1];
+ byte[] bytes = new byte[length + 1];
//Add message type
bytes[0] = sendOption;
return HandleSend(actualBytes, (byte)UdpSendOption.Hello, acknowledgeCallback);
}
-
- /// <inheritdoc />
+
+ /// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (disposing)
using System.Net;
using System.Net.Sockets;
using System.Threading;
+using System.Threading.Channels;
using System.Threading.Tasks;
using Microsoft.Extensions.ObjectPool;
using Serilog;
private static readonly ILogger Logger = Log.ForContext<UdpConnectionListener>();
/// <summary>
- /// A callback for early connection rejection.
- /// * Return false to reject connection.
- /// * A null response is ok, we just won't send anything.
+ /// A callback for early connection rejection.
+ /// * Return false to reject connection.
+ /// * A null response is ok, we just won't send anything.
/// </summary>
public AcceptConnectionCheck AcceptConnection;
-
public delegate bool AcceptConnectionCheck(IPEndPoint endPoint, byte[] input, out byte[] response);
private readonly UdpClient _socket;
private Task _executingTask;
/// <summary>
- /// Creates a new UdpConnectionListener for the given <see cref="IPAddress" />, port and <see cref="IPMode" />.
+ /// Creates a new UdpConnectionListener for the given <see cref="IPAddress"/>, port and <see cref="IPMode"/>.
/// </summary>
/// <param name="endPoint">The endpoint to listen on.</param>
/// <param name="ipMode"></param>
}
public int ConnectionCount => this._allConnections.Count;
-
+
private async void ManageReliablePackets(object state)
{
foreach (var kvp in _allConnections)
_timer.Dispose();
}
}
-}
+}
\ No newline at end of file
/// <summary>
/// Represents a servers's connection to a client that uses the UDP protocol.
/// </summary>
- /// <inheritdoc />
+ /// <inheritdoc/>
internal sealed class UdpServerConnection : UdpConnection
{
/// <summary>
/// The connection listener that we use the socket of.
/// </summary>
/// <remarks>
- /// Udp server connections utilize the same socket in the listener for sends/receives, this is the listener that
+ /// Udp server connections utilize the same socket in the listener for sends/receives, this is the listener that
/// created this connection and is hence the listener this conenction sends and receives via.
/// </remarks>
public UdpConnectionListener Listener { get; private set; }
if (this._state != ConnectionState.Connected) return false;
this._state = ConnectionState.NotConnected;
}
-
+
var bytes = EmptyDisconnectBytes;
if (data != null && data.Length > 0)
{