/// <summary> /// Releases the SocketAsyncEventArgs back to the pool, /// and free's up another slot for a new client to connect /// </summary> /// <param name="Stream"></param> protected void Release(SocketAsyncEventArgs e) { // Get our ReadWrite AsyncEvent object back SocketReadWritePool.Push(e); // Now that we have another set of AsyncEventArgs, we can // release this users Semephore lock, allowing another connection MaxConnectionsEnforcer.Release(); }
/// <summary> /// Releases the Stream's SocketAsyncEventArgs back to the pool, /// and free's up another slot for a new client to connect /// </summary> /// <param name="Stream">The GamespyTcpStream object that is being released.</param> public void Release(GamespyTcpStream Stream) { // If the stream has been released, then we stop here if (!IsListening || Stream.Released) { return; } // Make sure the connection is closed properly if (!Stream.SocketClosed) { Stream.Close(); return; } // To prevent cross instance releasing //if (!Object.ReferenceEquals(this, Stream.SocketManager)) //throw new ArgumentException("Cannot pass a GamespyTcpStream belonging to a different TcpSocket than this one."); // If we are still registered for this event, then the EventArgs should // NEVER be disposed here, or we have an error to fix if (Stream.DisposedEventArgs) { // Dispose old buffer tokens BufferManager.ReleaseBuffer(Stream.ReadEventArgs); BufferManager.ReleaseBuffer(Stream.WriteEventArgs); // Create new Read Event Args SocketAsyncEventArgs SockArgR = new SocketAsyncEventArgs(); BufferManager.AssignBuffer(SockArgR); SocketReadWritePool.Push(SockArgR); // Create new Write Event Args SocketAsyncEventArgs SockArgW = new SocketAsyncEventArgs(); BufferManager.AssignBuffer(SockArgW); SocketReadWritePool.Push(SockArgW); } else { // Set null's Stream.ReadEventArgs.AcceptSocket = null; Stream.WriteEventArgs.AcceptSocket = null; // Get our ReadWrite AsyncEvent object back SocketReadWritePool.Push(Stream.ReadEventArgs); SocketReadWritePool.Push(Stream.WriteEventArgs); } // Now that we have another set of AsyncEventArgs, we can // release this users Semephore lock, allowing another connection MaxConnectionsEnforcer.Release(); }
public GamespyTcpSocket(IPEndPoint bindTo, int MaxConnections) { // Create our Socket Listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // Set Socket options Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true); Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, false); Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); // Bind to our port Listener.Bind(bindTo); Listener.Listen(25); // Set the rest of our internals MaxNumConnections = MaxConnections; MaxConnectionsEnforcer = new SemaphoreSlim(MaxNumConnections, MaxNumConnections); SocketAcceptPool = new SocketAsyncEventArgsPool(ConcurrentAcceptPoolSize); SocketReadWritePool = new SocketAsyncEventArgsPool(MaxNumConnections * 2); // Create our Buffer Manager for IO operations. // Always allocate double space, one for recieving, and another for sending BufferManager = new BufferManager(MaxNumConnections * 2, BufferSizePerOperation); // Assign our Connection Accept SocketAsyncEventArgs object instances for (int i = 0; i < ConcurrentAcceptPoolSize; i++) { SocketAsyncEventArgs SockArg = new SocketAsyncEventArgs(); SockArg.Completed += (s, e) => PrepareAccept(e); // Do NOT assign buffer space for Accept operations! SocketAcceptPool.Push(SockArg); } // Assign our Connection IO SocketAsyncEventArgs object instances for (int i = 0; i < MaxNumConnections * 2; i++) { SocketAsyncEventArgs SockArg = new SocketAsyncEventArgs(); BufferManager.AssignBuffer(SockArg); SocketReadWritePool.Push(SockArg); } // set public internals IsListening = true; }
public GamespyUdpSocket(IPEndPoint bindTo, int MaxConnections) { // Create our Socket this.Port = Port; Listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp) { SendTimeout = 5000, // We have a limited pool, so we dont want to be locked often SendBufferSize = BufferSizePerEvent, ReceiveBufferSize = BufferSizePerEvent }; // Bind to our port Listener.Bind(bindTo); // Set the rest of our internals MaxNumConnections = MaxConnections; MaxConnectionsEnforcer = new SemaphoreSlim(MaxNumConnections, MaxNumConnections); SocketReadWritePool = new SocketAsyncEventArgsPool(MaxNumConnections); // Create our Buffer Manager for IO operations. BufferManager = new BufferManager( MaxNumConnections, BufferSizePerEvent ); // Assign our Connection IO SocketAsyncEventArgs object instances for (int i = 0; i < MaxNumConnections; i++) { SocketAsyncEventArgs SockArg = new SocketAsyncEventArgs(); SockArg.Completed += IOComplete; BufferManager.AssignBuffer(SockArg); SocketReadWritePool.Push(SockArg); } // set public internals IsRunning = true; IsDisposed = false; }
/// <summary> /// Once a connection has been received, its handed off here to convert it into /// our client object, and prepared to be handed off to the parent for processing /// </summary> /// <param name="AcceptEventArg"></param> protected async void PrepareAccept(SocketAsyncEventArgs AcceptEventArg) { // If we do not get a success code here, we have a bad socket if (IgnoreNewConnections || AcceptEventArg.SocketError != SocketError.Success) { // This method closes the socket and releases all resources, both // managed and unmanaged. It internally calls Dispose. AcceptEventArg.AcceptSocket.Close(); // Put the SAEA back in the pool. SocketAcceptPool.Push(AcceptEventArg); StartAcceptAsync(); return; } // If the server is full, send an error message to the player if (ConnectionEnforceMode == EnforceMode.DuringPrepare) { bool Success = await MaxConnectionsEnforcer.WaitAsync(WaitTimeout); if (!Success) { // If we arent even listening... if (!IsListening) { return; } // Alert the client that we are full if (!String.IsNullOrEmpty(FullErrorMessage)) { byte[] buffer = Encoding.UTF8.GetBytes( String.Format(@"\error\\err\0\fatal\\errmsg\{0}\id\1\final\", FullErrorMessage) ); AcceptEventArg.AcceptSocket.Send(buffer); } // Log so we can track this! //Program.ErrorLog.Write("NOTICE: [GamespyTcpSocket.PrepareAccept] The Server is currently full! Rejecting connecting client."); // Put the SAEA back in the pool. AcceptEventArg.AcceptSocket.Close(); SocketAcceptPool.Push(AcceptEventArg); StartAcceptAsync(); return; } } // Begin accepting a new connection StartAcceptAsync(); // Grab a send/recieve object SocketAsyncEventArgs ReadArgs = SocketReadWritePool.Pop(); SocketAsyncEventArgs WriteArgs = SocketReadWritePool.Pop(); // Pass over the reference to the new socket that is handling // this specific stream, and dereference it so we can hand the // acception event back over ReadArgs.AcceptSocket = WriteArgs.AcceptSocket = AcceptEventArg.AcceptSocket; AcceptEventArg.AcceptSocket = null; // Hand back the AcceptEventArg so another connection can be accepted SocketAcceptPool.Push(AcceptEventArg); // Hand off processing to the parent GamespyTcpStream Stream = null; try { Stream = new GamespyTcpStream(this, ReadArgs, WriteArgs); ProcessAccept(Stream); } catch (Exception e) { // Report Error OnException(e); // Make sure the connection is closed properly if (Stream != null) { Release(Stream); } } }