/// <summary> /// Constructor /// </summary> public GpspClient(GamespyTcpStream client) { // Set disposed to false! this.Disposed = false; // Generate a unique name for this connection this.ConnectionId = Interlocked.Increment(ref SessionsCreated); // Init a new client stream class Stream = client; Stream.OnDisconnect += () => Dispose(); Stream.DataReceived += (message) => { // Read client message, and parse it into key value pairs string[] recieved = message.TrimStart('\\').Split('\\'); switch (recieved[0]) { case "nicks": SendNicks(ConvertToKeyValue(recieved)); break; case "check": SendCheck(ConvertToKeyValue(recieved)); break; } }; }
/// <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) { // Log this error Program.ErrorLog.Write("WARNING: [GamespyTcpSocket.Release] Event Args were disposed imporperly!"); // 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(); }
/// <summary> /// When a new connection is established, we the parent class are responsible /// for handling the processing /// </summary> /// <param name="Stream">A GamespyTcpStream object that wraps the I/O AsyncEventArgs and socket</param> protected override void ProcessAccept(GamespyTcpStream Stream) { try { // Convert the TcpClient to a MasterClient GpspClient client = new GpspClient(Stream); Clients.TryAdd(client.ConnectionId, client); // Begin accepting data now that we are fully connected Stream.BeginReceive(); } catch (Exception e) { Program.ErrorLog.Write("WARNING: An Error occured at [Gpsp.ProcessAccept] : Generating Exception Log"); ExceptionHandler.GenerateExceptionLog(e); base.Release(Stream); } }
/// <summary> /// Accepts a TcpClient, and begin the serverlist fetching process for the client. /// This method is executed when the user updates his server browser ingame /// </summary> protected override void ProcessAccept(GamespyTcpStream Stream) { // End the operation and display the received data on // the console. try { // Convert the TcpClient to a MasterClient MasterClient client = new MasterClient(Stream); Clients.TryAdd(client.ConnectionId, client); // Begin accepting data now that we are fully connected Stream.BeginReceive(); } catch (Exception e) { Program.ErrorLog.Write("WARNING: An Error occured at [MstrServer.AcceptClient] : Generating Exception Log"); ExceptionHandler.GenerateExceptionLog(e); base.Release(Stream); } }
/// <summary> /// Constructor /// </summary> /// <param name="client"></param> public MasterClient(GamespyTcpStream client) { // Internal Variables this.Disposed = false; // Generate a unique name for this connection this.ConnectionId = Interlocked.Increment(ref SessionsCreated); // Init a new client stream class Stream = client; Stream.OnDisconnect += () => Dispose(); Stream.DataReceived += (receivedData) => { // lets split up the message based on the delimiter string[] messages = receivedData.Split(new string[] { "\x00\x00\x00\x00" }, StringSplitOptions.RemoveEmptyEntries); foreach (string message in messages) { // Ignore Non-BF2 related queries if (message.StartsWith("battlefield2")) ParseRequest(message); } }; }
/// <summary> /// When a new connection is established, the parent class is responsible for /// processing the connected client /// </summary> /// <param name="Stream">A GamespyTcpStream object that wraps the I/O AsyncEventArgs and socket</param> protected abstract void ProcessAccept(GamespyTcpStream Stream);
/// <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 = AcceptEventArg.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 Program.ErrorLog.Write("ERROR: An Error occured at [GamespyTcpSocket.PrepareAccept] : Generating Exception Log"); ExceptionHandler.GenerateExceptionLog(e); // Make sure the connection is closed properly if (Stream != null) { Release(Stream); } } }
/// <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 = AcceptEventArg.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 Program.ErrorLog.Write("ERROR: An Error occured at [GamespyTcpSocket.PrepareAccept] : Generating Exception Log"); ExceptionHandler.GenerateExceptionLog(e); // Make sure the connection is closed properly if (Stream != null) Release(Stream); } }
/// <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) { // Log this error Program.ErrorLog.Write("WARNING: [GamespyTcpSocket.Release] Event Args were disposed imporperly!"); // 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(); }
/// <summary> /// Constructor /// </summary> public GpcmClient(GamespyTcpStream ConnectionStream, int ConnectionId) { // Set default variable values PlayerNick = "Connecting..."; PlayerId = 0; RemoteEndPoint = (IPEndPoint)ConnectionStream.RemoteEndPoint; Disposed = false; Status = LoginStatus.None; // Set the connection ID this.ConnectionId = ConnectionId; // Create our Client Stream Stream = ConnectionStream; Stream.OnDisconnect += Stream_OnDisconnect; Stream.DataReceived += Stream_DataReceived; Stream.BeginReceive(); }
/// <summary> /// When a new connection is established, we the parent class are responsible /// for handling the processing /// </summary> /// <param name="Stream">A GamespyTcpStream object that wraps the I/O AsyncEventArgs and socket</param> protected override void ProcessAccept(GamespyTcpStream Stream) { // Get our connection id int ConID = Interlocked.Increment(ref ConnectionCounter); GpcmClient client; try { // Create a new GpcmClient, passing the IO object for the TcpClientStream client = new GpcmClient(Stream, ConID); Processing.TryAdd(ConID, client); // Begin the asynchronous login process client.SendServerChallenge(); } catch (Exception e) { // Log the error Program.ErrorLog.Write("WARNING: An Error occured at [GpcmServer.ProcessAccept] : Generating Exception Log"); ExceptionHandler.GenerateExceptionLog(e); // Remove pending connection Processing.TryRemove(ConID, out client); // Release this stream so it can be used again base.Release(Stream); } }