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; } } }