using System;
using System.Net;
namespace Hazel
{
///
/// Base class for all connection listeners.
///
///
///
/// 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.
///
///
/// Each time a client connects the event will be invoked to alert all subscribers to
/// the new connection. A disconnected event is then present on the that is passed to the
/// subscribers.
///
///
///
public abstract class ConnectionListener : IDisposable
{
///
/// The max size Hazel attempts to read from the network.
/// Defaults to 8096.
///
///
/// 8096 is 5 times the standard modern MTU of 1500, so it's already too large imo.
/// If Hazel ever implements fragmented packets, then we might consider a larger value since combining 5
/// packets into 1 reader would be realistic and would cause reallocations. That said, Hazel is not meant
/// for transferring large contiguous blocks of data, so... please don't?
///
public int ReceiveBufferSize = 8096;
public readonly ListenerStatistics Statistics = new ListenerStatistics();
public abstract double AveragePing { get; }
public abstract int ConnectionCount { get; }
public abstract int SendQueueLength { get; }
public abstract int ReceiveQueueLength { get; }
///
/// A callback for early connection rejection.
/// * Return false to reject connection.
/// * A null response is ok, we just won't send anything.
///
public AcceptConnectionCheck AcceptConnection;
public delegate bool AcceptConnectionCheck(IPEndPoint endPoint, byte[] input, out byte[] response);
///
/// Invoked when a new client connects.
///
///
///
/// NewConnection is invoked each time a client connects to the listener. The
/// contains the new for communication with this
/// client.
///
///
/// Hazel may or may not store connections so it is your responsibility to keep track and properly Dispose of
/// connections to your server.
///
///
///
///
///
///
public event Action NewConnection;
///
/// Invoked when an internal error causes the listener to be unable to continue handling messages.
///
///
/// Support for this is still pretty limited. At the time of writing, only iOS devices need this in one case:
/// When iOS suspends an app, it might also free our socket while not allowing Unity to run in the background.
/// When Unity resumes, it can't know that time passed or the socket is freed, so we used to continuously throw internal errors.
///
public event Action OnInternalError;
///
/// Makes this connection listener begin listening for connections.
///
///
///
/// This instructs the listener to begin listening for new clients connecting to the server. When a new client
/// connects the event will be invoked containing the connection to the new client.
///
///
/// To stop listening you should call .
///
///
///
///
///
public abstract void Start();
///
/// Invokes the NewConnection event with the supplied connection.
///
/// The user sent bytes that were received as part of the handshake.
/// The connection to pass in the arguments.
///
/// Implementers should call this to invoke the event before data is received so that
/// subscribers do not miss any data that may have been sent immediately after connecting.
///
protected void InvokeNewConnection(MessageReader msg, Connection connection)
{
// Make a copy to avoid race condition between null check and invocation
Action handler = NewConnection;
if (handler != null)
{
try
{
handler(new NewConnectionEventArgs(msg, connection));
}
catch (Exception e)
{
}
}
}
///
/// Invokes the InternalError event with the supplied reason.
///
protected void InvokeInternalError(HazelInternalErrors reason)
{
// Make a copy to avoid race condition between null check and invocation
Action handler = this.OnInternalError;
if (handler != null)
{
try
{
handler(reason);
}
catch
{
}
}
}
///
/// Call to dispose of the connection listener.
///
public void Dispose()
{
Dispose(true);
}
///
/// Called when the object is being disposed.
///
/// Are we disposing?
protected virtual void Dispose(bool disposing)
{
this.NewConnection = null;
this.OnInternalError = null;
}
}
}