private void ProcessReceive(SocketAsyncEventArgs args) { _idleTimeoutTimer?.Stop(); _readTimeoutTimer?.Stop(); if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success) { ReceiveToken token = (ReceiveToken)args.UserToken; token.BufferLength += args.BytesTransferred; if (Socket.Available == 0) { byte[] receiveBuffer = new byte[token.BufferLength]; Array.Copy(args.Buffer, receiveBuffer, token.BufferLength); token.BufferLength = 0; token.ReceiveHandler(receiveBuffer); StartReceive(args); } else if (Socket.ReceiveAsync(args) == false) { // Read the next block of data sent by client. ProcessReceive(args); } } else { if (Socket.Connected) { Close(); } } }
private static readonly byte[] HEADER_DELIM = { 13, 10, 13, 10 }; // UTF-8 for \r\n\r\n i.e. CRLF CRLF, which appears after the last HTTP header field protected byte[] HandleHttpHeader(SocketAsyncEventArgs e, ref int bytesAlreadyProcessed) { if (bytesAlreadyProcessed < 0) { throw new ArgumentException("bytesAlreadyProcessed must be non-negative."); } else if (bytesAlreadyProcessed >= e.BytesTransferred) { throw new ArgumentException("bytesAlreadyProcessed must be less than e.BytesTransferred."); } ReceiveToken token = (ReceiveToken)e.UserToken; int totalBytes = token.bytesReceived + e.BytesTransferred - bytesAlreadyProcessed; if (totalBytes >= 4) { int searchSpace = totalBytes - 3; // totalBytes - HEADER_DELIM.Length + 1 byte[] data = token.internalBuffer.Array; for (int i = 0; i < searchSpace; i++) { if (HEADER_DELIM[0] == data[token.internalBuffer.Offset + i]) { if (HEADER_DELIM[1] == data[token.internalBuffer.Offset + i + 1] && HEADER_DELIM[2] == data[token.internalBuffer.Offset + i + 2] && HEADER_DELIM[3] == data[token.internalBuffer.Offset + i + 3]) { byte[] header = new byte[i + 4]; Buffer.BlockCopy(data, token.internalBuffer.Offset, header, 0, header.Length); if (token.bytesReceived > 0) { bytesAlreadyProcessed += header.Length - token.bytesReceived; token.bytesReceived = 0; e.SetBuffer(token.internalBuffer.Offset, token.internalBuffer.Count); } else { bytesAlreadyProcessed += header.Length; } if (header.Length < totalBytes) { Buffer.BlockCopy(data, token.internalBuffer.Offset + header.Length, data, token.internalBuffer.Offset, totalBytes - header.Length); } e.UserToken = token; return(header); } } } if (totalBytes == token.internalBuffer.Count) { throw new Exception(String.Format("Could not find end of header within {0} bytes.", token.internalBuffer.Count)); } } token.bytesReceived = totalBytes; e.SetBuffer(token.internalBuffer.Offset + totalBytes, token.internalBuffer.Count - totalBytes); bytesAlreadyProcessed = e.BytesTransferred; e.UserToken = token; return(null); }
private void ReturnBuffer(SocketAsyncEventArgs e) { if (e.UserToken != null) { ReceiveToken token = (ReceiveToken)e.UserToken; bufferManager.ReturnBuffer(token.internalBuffer); token.internalBuffer = default(ArraySegment <byte>); e.SetBuffer(new byte[0], 0, 0); } }
public void Receive(Action <byte[]> buffer) { ReceiveToken token = new ReceiveToken { ReceiveHandler = buffer }; ReceiveSocketAsyncEventArgs.UserToken = token; StartReceive(ReceiveSocketAsyncEventArgs); }
// Should validate the handshake response from the server private void ReceiveAsync_Completed(object sender, SocketAsyncEventArgs e) { if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) { int bytesAlreadyProcessed = 0; // Count of the total freshly transferred bytes processed so far ReceiveToken token = (ReceiveToken)e.UserToken; if (!headerExchanged) { byte[] header = HandleHttpHeader(e, ref bytesAlreadyProcessed); token = (ReceiveToken)e.UserToken; if (header == null) { DoRead(e); return; } else if (Websockets.ValidateResponseHeader(headerHash, header)) { headerExchanged = true; token.maxAllowedBytes = int.MaxValue; e.UserToken = token; // Ping the server to finalize the player's connection Send(Text.CreateFromString(Time.Timestep, InstanceGuid.ToString(), true, Receivers.Server, MessageGroupIds.NETWORK_ID_REQUEST, true)); } else { // Improper header, so a disconnect is required Disconnect(true); return; } } while (bytesAlreadyProcessed < e.BytesTransferred) { byte[] data = HandleData(e, true, ref bytesAlreadyProcessed); if (data == null) { break; } FrameStream frame = Factory.DecodeMessage(data, false, MessageGroupIds.TCP_FIND_GROUP_ID, Server); FireRead(frame, Server); } DoRead(e); } else { Disconnect(true); } }
protected virtual void Initialize(string host, ushort port, bool pendCreates = true) { // By default pending creates should be true and flushed when ready if (pendCreates) { PendCreates = true; } // Get a random hash key that needs to be used for validating that the server was connected to headerHash = Websockets.HeaderHashKey(); // This is a typical Websockets accept header to be validated byte[] connectionHeader = Websockets.ConnectionHeader(headerHash, port); // Register the server as a NetworkingPlayer server = new NetworkingPlayer(0, host, true, client, this); // Send the upgrade request to the server RawWrite(connectionHeader); //Let myself know I connected successfully OnPlayerConnected(server); // Set myself as a connected client server.Connected = true; ReceiveToken token = new ReceiveToken { internalBuffer = new ArraySegment <byte>(buffer, 0, buffer.Length), player = server, bytesReceived = 0, dataHolder = null, maxAllowedBytes = 8192 }; // Read from the server async SocketAsyncEventArgs e = new SocketAsyncEventArgs(); e.Completed += new EventHandler <SocketAsyncEventArgs>(ReceiveAsync_Completed); e.UserToken = token; e.SetBuffer(token.internalBuffer.Array, token.internalBuffer.Offset, token.internalBuffer.Count); if (!client.Client.ReceiveAsync(e)) { Task.Queue(() => ReceiveAsync_Completed(this, e)); } }
private void DoRead(SocketAsyncEventArgs e) { if (!IsBound) { ReturnBuffer(e); return; } ReceiveToken token = (ReceiveToken)e.UserToken; Socket playerSocket = null; try { // Try to get the client stream if it is still available playerSocket = token.player.TcpClientHandle.Client; } catch { // Failed to get the stream for the client so forcefully disconnect it //Console.WriteLine("Exception: Failed to get stream for client (Forcefully disconnecting)"); Disconnect(token.player, true); ReturnBuffer(e); return; } // If the player is no longer connected, then make sure to disconnect it properly if (!token.player.TcpClientHandle.Connected) { Disconnect(token.player, false); ReturnBuffer(e); return; } // False means operation was synchronous (usually error) if (!playerSocket.ReceiveAsync(e)) { ReceiveAsync_Completed(this, e); } }
protected byte[] HandleData(SocketAsyncEventArgs e, bool isStream, ref int bytesAlreadyProcessed) { if (bytesAlreadyProcessed < 0) { throw new ArgumentException("bytesAlreadyProcessed must be non-negative."); } else if (bytesAlreadyProcessed >= e.BytesTransferred) { throw new ArgumentException("bytesAlreadyProcessed must be less than e.BytesTransferred."); } ReceiveToken token = (ReceiveToken)e.UserToken; int socketOffset = token.internalBuffer.Offset; byte[] bytes = token.internalBuffer.Array; int totalBytes; #region ParseFrameHeader if (token.dataHolder == null) // Null dataHolder means header not parsed yet { totalBytes = token.bytesReceived + e.BytesTransferred - bytesAlreadyProcessed; if (totalBytes < 2) // Not enough bytes to determine length, so move offset { token.bytesReceived = totalBytes; e.SetBuffer(socketOffset + totalBytes, token.internalBuffer.Count - totalBytes); bytesAlreadyProcessed = e.BytesTransferred; e.UserToken = token; return(null); } int dataLength = bytes[socketOffset + 1] & 127; bool usingMask = bytes[socketOffset + 1] > 127; // same as bytes[socketOffset + 1] & 128 != 0 int payloadOffset; if (dataLength == 126) // 126 means 125 < length < 65536 { payloadOffset = 4; } else if (dataLength == 127) // 127 means length >= 65536 { payloadOffset = 10; } else { payloadOffset = 2; } int length; if (payloadOffset != 2) { if (totalBytes < payloadOffset) // Not enough bytes to determine length, so move offset { token.bytesReceived = totalBytes; e.SetBuffer(socketOffset + totalBytes, token.internalBuffer.Count - totalBytes); bytesAlreadyProcessed = e.BytesTransferred; e.UserToken = token; return(null); } // Need to worry about endian order since length is in big endian if (payloadOffset == 4) { if (BitConverter.IsLittleEndian) { length = BitConverter.ToUInt16(new byte[] { bytes[socketOffset + 3], bytes[socketOffset + 2] }, 0); } else { length = BitConverter.ToUInt16(bytes, socketOffset + 2); } } else { // First 4 bytes will be 0 for sizes less than max int32 if (BitConverter.IsLittleEndian) { length = (int)BitConverter.ToUInt32(new byte[] { bytes[socketOffset + 9], bytes[socketOffset + 8], bytes[socketOffset + 7], bytes[socketOffset + 6] }, 0); } else { length = (int)BitConverter.ToUInt32(bytes, socketOffset + 6); } } // Group id, receivers, time step, unique id, and router id lengths will be present in the payload length //if (isStream) // length += 21; // Group id (4), receivers (1), time step (8), unique id (8) //if ((bytes[socketOffset + 0] & 0xF) == (Binary.CONTROL_BYTE & 0xF)) // length += 1; // routerId (1) if this is a binary frame } else { length = dataLength; } length += usingMask ? 4 + payloadOffset : payloadOffset; // Add full length of header to length if (length < 0 || length > token.maxAllowedBytes) { throw new UnauthorizedAccessException(String.Format("Tried to receive a frame larger than expected. Got {0}, and expected less than {1}", length, token.maxAllowedBytes)); } e.SetBuffer(token.internalBuffer.Offset, token.internalBuffer.Count); token.bytesReceived = 0; token.dataHolder = new byte[length]; } #endregion totalBytes = token.bytesReceived + e.BytesTransferred - bytesAlreadyProcessed; if (totalBytes < token.dataHolder.Length) // Full frame not yet received { Buffer.BlockCopy(bytes, socketOffset, token.dataHolder, token.bytesReceived, e.BytesTransferred - bytesAlreadyProcessed); token.bytesReceived += e.BytesTransferred - bytesAlreadyProcessed; bytesAlreadyProcessed = e.BytesTransferred; e.UserToken = token; return(null); } else { byte[] data = token.dataHolder; int dataProcessed = (data.Length - token.bytesReceived); Buffer.BlockCopy(bytes, socketOffset, data, token.bytesReceived, dataProcessed); token.bytesReceived = 0; token.dataHolder = null; bytesAlreadyProcessed += dataProcessed; if (bytesAlreadyProcessed < e.BytesTransferred) // More frames left to handle, so shuffle the remaining data to beginning of buffer. { Buffer.BlockCopy(bytes, socketOffset + dataProcessed, bytes, socketOffset, e.BytesTransferred - bytesAlreadyProcessed); } e.UserToken = token; return(data); } }
public TokenInput(ReceiveToken callback) { this.callback = callback; }
private void ReceiveAsync_Completed(object sender, SocketAsyncEventArgs e) { if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) { int bytesAlreadyProcessed = 0; ReceiveToken token = (ReceiveToken)e.UserToken; if (!token.player.Accepted && !token.player.Connected) { byte[] header = HandleHttpHeader(e, ref bytesAlreadyProcessed); if (header == null) { DoRead(e); return; } byte[] response = Websockets.ValidateConnectionHeader(header); // The response will be null if the header sent is invalid, if so then disconnect client as they are sending invalid headers if (response == null) { OnPlayerRejected(token.player); Disconnect(token.player, true); ReturnBuffer(e); return; } // If all is in order then send the validated response to the client token.player.TcpClientHandle.GetStream().Write(response, 0, response.Length); // The player has successfully connected token.player.Connected = true; } while (bytesAlreadyProcessed < e.BytesTransferred) { byte[] data = HandleData(e, true, ref bytesAlreadyProcessed); if (data == null) { break; } FrameStream frame = Factory.DecodeMessage(data, true, MessageGroupIds.TCP_FIND_GROUP_ID, token.player); if (!token.player.Accepted) { if (frame.GroupId == MessageGroupIds.NETWORK_ID_REQUEST) { token.player.InstanceGuid = ((Text)frame).ToString(); // If the player was rejected during the handling of the playerGuidAssigned event, don't accept them. if (!TryPlayerGuidAssignment(token.player)) { break; } token.maxAllowedBytes = int.MaxValue; if (authenticator != null) { authenticator.IssueChallenge(this, token.player, IssueChallenge, AuthUser); } else { AuthUser(token.player); } } else if (frame.GroupId == MessageGroupIds.AUTHENTICATION_RESPONSE) { // Authenticate user response if (authenticator == null) { return; } authenticator.VerifyResponse(this, token.player, frame.StreamData, AuthUser, RejectUser); } else { Disconnect(token.player, true); ReturnBuffer(e); } } else { token.player.Ping(); FireRead(frame, token.player); } } DoRead(e); } else { Disconnect(((ReceiveToken)e.UserToken).player, true); ReturnBuffer(e); } }
/// <summary> /// Infinite loop listening for client connections on a separate thread. /// This loop breaks if there is an exception thrown on the blocking accept call /// </summary> private void ListenForConnections(IAsyncResult obj) { TcpListener asyncListener = (TcpListener)obj.AsyncState; TcpClient client = null; try { client = asyncListener.EndAcceptTcpClient(obj); } catch { return; } asyncListener.BeginAcceptTcpClient(ListenForConnections, asyncListener); if (rawClients.Count == MaxConnections) { // Tell the client why they are being disconnected Send(client, Error.CreateErrorMessage(Time.Timestep, "Max Players Reached On Server", false, MessageGroupIds.MAX_CONNECTIONS, true)); // Send the close connection frame to the client Send(client, new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, true)); // Do disconnect logic for client ClientRejected(client, false); return; } else if (!AcceptingConnections) { // Tell the client why they are being disconnected Send(client, Error.CreateErrorMessage(Time.Timestep, "The server is busy and not accepting connections", false, MessageGroupIds.MAX_CONNECTIONS, true)); // Send the close connection frame to the client Send(client, new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, true)); // Do disconnect logic for client ClientRejected(client, false); return; } ArraySegment <byte> segment; if (!bufferManager.TryTakeBuffer(out segment)) { // Tell the client why they are being disconnected Send(client, Error.CreateErrorMessage(Time.Timestep, "The server is busy and not accepting connections", false, MessageGroupIds.MAX_CONNECTIONS, true)); // Send the close connection frame to the client Send(client, new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, true)); // Do disconnect logic for client ClientRejected(client, false); throw new OutOfMemoryException("Buffer manager has run out of allocated memory (possible memory leak)."); } // Clients will be looped through on other threads so be sure to lock it before adding ReceiveToken token; lock (Players) { rawClients.Add(client); // Create the identity wrapper for this player NetworkingPlayer player = new NetworkingPlayer(ServerPlayerCounter++, ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString(), false, client, this); // Generically add the player and fire off the events attached to player joining OnPlayerConnected(player); token = new ReceiveToken { internalBuffer = segment, player = player, bytesReceived = 0, dataHolder = null, maxAllowedBytes = 8192 }; } // Let all of the event listeners know that the client has successfully connected if (rawClientConnected != null) { rawClientConnected(client); } SocketAsyncEventArgs e = new SocketAsyncEventArgs(); e.Completed += new EventHandler <SocketAsyncEventArgs>(ReceiveAsync_Completed); e.UserToken = token; e.SetBuffer(token.internalBuffer.Array, token.internalBuffer.Offset, token.internalBuffer.Count); if (!client.Client.ReceiveAsync(e)) { Task.Queue(() => ReceiveAsync_Completed(this, e)); } }