protected void InitializeMasterClient(string host, ushort port) { // Get a client stream for reading and writing. // Stream stream = client.GetStream(); //获取需要用于验证服务器连接的随机哈希键 // Get a random hash key that needs to be used for validating that the server was connected to headerHash = Websockets.HeaderHashKey(); //这是一个典型的Websockets接受头被验证 // This is a typical Websockets accept header to be validated byte[] connectHeader = Websockets.ConnectionHeader(headerHash, port); //将接受标头发送到服务器进行验证 // Send the accept headers to the server to validate RawWrite(connectHeader); //将服务器的身份设置为玩家 // Setup the identity of the server as a player server = new NetworkingPlayer(0, host, true, client, this); //让我知道我连接成功 //Let myself know I connected successfully OnPlayerConnected(server); //将自己设置为连接的客户端 // Set myself as a connected client server.Connected = true; }
/// <summary> /// Setup everything required for this client to be accepted by the server and /// construct / send the connection header /// </summary> /// <param name="port">The port that was connected to on the remote host</param> protected virtual void Initialize(string host, ushort port) { // By default pending creates should be true and flushed when ready PendCreates = true; // Get a client stream for reading and writing. // Stream stream = client.GetStream(); // 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[] connectHeader = Websockets.ConnectionHeader(headerHash, port); // Send the accept headers to the server to validate RawWrite(connectHeader); // Setup the identity of the server as a player server = new NetworkingPlayer(0, host, true, client, this); //Let myself know I connected successfully OnPlayerConnected(server); // Set myself as a connected client server.Connected = true; }
private void SetupClient(BMSByte packet, string incomingEndpoint, IPEndPoint groupEP) { // Check for a local listing request if (packet.Size.Between(2, 4) && packet[0] == BROADCAST_LISTING_REQUEST_1 && packet[1] == BROADCAST_LISTING_REQUEST_2 && packet[2] == BROADCAST_LISTING_REQUEST_3) { // Don't reply if the server is not currently accepting connections if (!AcceptingConnections) { return; } // This may be a local listing request so respond with the server flag byte Client.Send(new byte[] { SERVER_BROADCAST_CODE }, 1, groupEP); return; } if (Players.Count == MaxConnections) { // Tell the client why they are being disconnected Send(Error.CreateErrorMessage(Time.Timestep, "Max Players Reached On Server", false, MessageGroupIds.MAX_CONNECTIONS, true)); // Send the close connection frame to the client Send(new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, false)); return; } else if (!AcceptingConnections) { // Tell the client why they are being disconnected Send(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(new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, false)); return; } // Validate that the connection headers are properly formatted byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); // 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) { return; } UDPNetworkingPlayer player = new UDPNetworkingPlayer(ServerPlayerCounter++, incomingEndpoint, false, groupEP, this); // If all is in order then send the validated response to the client Client.Send(response, response.Length, groupEP); OnPlayerConnected(player); udpPlayers.Add(incomingEndpoint, player); // The player has successfully connected player.Connected = true; }
// 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); } }
private void HandleHeaderExchanging(BMSByte packet) { if (Websockets.ValidateResponseHeader(headerHash, packet.CompressBytes())) { CompleteHeaderExchange(); } else if (packet.Size >= MINIMUM_FRAME_SIZE) { HandleServerRejection(packet); CancelReadThread(); } else if (packet.Size != 1 || packet[0] != 0) { Disconnect(true); CancelReadThread(); } }
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 SetupClient(BMSByte packet, CSteamID steamId) { if (Players.Count == MaxConnections) { // Tell the client why they are being disconnected Send(Error.CreateErrorMessage(Time.Timestep, "Max Players Reached On Server", false, MessageGroupIds.MAX_CONNECTIONS, true)); // Send the close connection frame to the client Send(new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, false)); return; } else if (!AcceptingConnections) { // Tell the client why they are being disconnected Send(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(new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, false)); return; } // Validate that the connection headers are properly formatted byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); // 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) { return; } SteamNetworkingPlayer player = new SteamNetworkingPlayer(ServerPlayerCounter++, steamId, false, this); // If all is in order then send the validated response to the client Client.Send(response, response.Length, steamId); OnPlayerConnected(player); steamPlayers.Add(steamId, player); // The player has successfully connected player.Connected = true; }
private void AttemptServerConnection(object _) { int connectCounter = 0; // This is a typical Websockets accept header to be validated byte[] connectHeader = Websockets.ConnectionHeader(headerHash, Port); do { // Send the accept headers to the server to validate Client.Send(connectHeader, connectHeader.Length, ServerPlayer.IPEndPointHandle); Thread.Sleep(3000); } while (!initialConnectHeaderExchanged && IsBound && ++connectCounter < CONNECT_TRIES); if (connectCounter >= CONNECT_TRIES && connectAttemptFailed != null) { connectAttemptFailed(this); } }
private void SetNetworkBindings(ushort overrideBindingPort, ushort port, string natHost, string host, ushort natPort) { // Make sure not to listen on the same port as the server for local networks ushort clientPort = FindAvailablePort(overrideBindingPort, port); Client.EnableBroadcast = true; // If the server is behind a NAT, request for the port punch by the nat server if (!string.IsNullOrEmpty(natHost)) { nat.Connect(host, port, clientPort, natHost, natPort); } // Do any generic initialization in result of the successful bind OnBindSuccessful(); // Get a random hash key that needs to be used for validating that the server was connected to headerHash = Websockets.HeaderHashKey(); // Set the port SetPort(clientPort); }
private void HandleClientAcceptance(BMSByte packet, string incomingEndpoint, IPEndPoint groupEP) { // Validate that the connection headers are properly formatted byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); // 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) { return; } var player = new UDPNetworkingPlayer(ServerPlayerCounter++, incomingEndpoint, false, groupEP, this); // If all is in order then send the validated response to the client Client.Send(response, response.Length, groupEP); OnPlayerConnected(player); udpPlayers.Add(incomingEndpoint, player); // The player has successfully connected player.Connected = true; }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> private void ReadNetwork() { CSteamID messageFrom = default(CSteamID); try { BMSByte packet = null; // Intentional infinite loop while (IsBound) { // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } try { uint msgSize = 0; if (SteamNetworking.IsP2PPacketAvailable(out msgSize)) { packet = Client.Receive(msgSize, out messageFrom); } else { Thread.Sleep(1); continue; } // Read a packet from the network if (PacketLossSimulation > 0.0f && new Random().NextDouble() <= PacketLossSimulation) { // Skip this message continue; } BandwidthIn += (ulong)packet.Size; } catch (SocketException ex) { // This is a common exception when we exit the blocking call Logging.BMSLog.LogException(ex); Disconnect(true); } // Check to make sure a message was received if (packet == null || packet.Size <= 0) { continue; } // Check to see if the headers have been exchanged if (!headerExchanged) { if (Websockets.ValidateResponseHeader(headerHash, packet.CompressBytes())) { headerExchanged = true; // TODO: When getting the user id, it should also get the server time // by using the current time in the payload and getting it back along with server time // Ping the server to finalize the player's connection Send(Text.CreateFromString(Time.Timestep, InstanceGuid.ToString(), false, Receivers.Server, MessageGroupIds.NETWORK_ID_REQUEST, false), true); } else if (packet.Size != 1 || packet[0] != 0) { Logging.BMSLog.LogWarning("DISCONNECTED: RECEIVED UNKNOWN PACKET BEFORE HEADERS WERE EXCHANGED!"); Disconnect(true); break; } else { continue; } } else { if (packet.Size < 17) { continue; } // Format the byte data into a UDPPacket struct UDPPacket formattedPacket = TranscodePacket(Server, packet); // Check to see if this is a confirmation packet, which is just // a packet to say that the reliable packet has been read if (formattedPacket.isConfirmation) { if (formattedPacket.groupId == MessageGroupIds.DISCONNECT) { CloseConnection(); return; } OnMessageConfirmed(server, formattedPacket); continue; } // Add the packet to the manager so that it can be tracked and executed on complete packetManager.AddPacket(formattedPacket, PacketSequenceComplete, this); } } } catch (Exception ex) { Logging.BMSLog.LogException(ex); Disconnect(true); } }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> private void ReadClients() { IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 0); string incomingEndpoint = string.Empty; BMSByte packet = null; // Intentional infinite loop while (IsBound) { // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } try { // Read a packet from the network packet = Client.Receive(ref groupEP, ref incomingEndpoint); if (PacketLossSimulation > 0.0f && new Random().NextDouble() <= PacketLossSimulation) { // Skip this message continue; } BandwidthIn += (ulong)packet.Size; } catch { if (udpPlayers.ContainsKey(incomingEndpoint)) { Disconnect(udpPlayers[incomingEndpoint], true); CleanupDisconnections(); } continue; } // Check to make sure a message was received if (packet == null || packet.Size <= 0) { continue; } if (!udpPlayers.ContainsKey(incomingEndpoint)) { SetupClient(packet, incomingEndpoint, groupEP); continue; } else { currentReadingPlayer = udpPlayers[incomingEndpoint]; if (!currentReadingPlayer.Accepted && !currentReadingPlayer.PendingAccpeted) { // It is possible that the response validation was dropped so // check if the client is resending for a response byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); // The client has sent the connection request again if (response != null) { Client.Send(response, response.Length, groupEP); continue; } else { currentReadingPlayer.PendingAccpeted = true; ReadPacket(packet); } } else { ReadPacket(packet); } } } }
/// <summary> /// This will connect a UDP client to a given UDP server /// </summary> /// <param name="host">The server's host address on the network</param> /// <param name="port">The port that the server is hosting on</param> /// <param name="natHost">The NAT server host address, if blank NAT will be skipped</param> /// <param name="natPort">The port that the NAT server is hosting on</param> /// <param name="pendCreates">Immidiately set the NetWorker::PendCreates to true</param> public void Connect(CSteamID hostId, bool pendCreates = false) { if (Disposed) { throw new ObjectDisposedException("UDPClient", "This object has been disposed and can not be used to connect, please use a new UDPClient"); } if (hostId.IsLobby()) { //If this is a lobby we need to join it, make direct connection to the owner. m_JoinCall = SteamMatchmaking.JoinLobby(hostId); m_LobbyEntered = Callback <LobbyEnter_t> .Create((LobbyEnter_t data) => { LobbyID = (CSteamID)data.m_ulSteamIDLobby; //Get the owner and attempt a direct connection Connect(SteamMatchmaking.GetLobbyOwner(LobbyID), pendCreates); }); return; } // By default pending creates should be true and flushed when ready if (!pendCreates) { PendCreates = true; } try { ushort clientPort = DEFAULT_PORT; // Make sure not to listen on the same port as the server for local networks if (clientPort == DEFAULT_PORT) { clientPort++; } Client = new CachedSteamP2PClient(hostId); // Do any generic initialization in result of the successful bind OnBindSuccessful(); // 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[] connectHeader = Websockets.ConnectionHeader(headerHash, DEFAULT_PORT); // Setup the identity of the server as a player server = new NetworkingPlayer(0, hostId, true, this); // Create the thread that will be listening for new data from connected clients and start its execution Task.Queue(ReadNetwork); //Let myself know I connected successfully OnPlayerConnected(server); // Set myself as a connected client server.Connected = true; //Set the port SetPort(clientPort); int connectCounter = 0; Task.Queue(() => { do { // Send the accept headers to the server to validate Client.Send(connectHeader, connectHeader.Length, hostId, EP2PSend.k_EP2PSendReliable); Thread.Sleep(3000); } while (!headerExchanged && IsBound && ++connectCounter < CONNECT_TRIES); if (connectCounter >= CONNECT_TRIES) { if (connectAttemptFailed != null) { connectAttemptFailed(this); } } }); m_CallbackP2PSessionConnectFail = Callback <P2PSessionConnectFail_t> .Create((P2PSessionConnectFail_t data) => { if (data.m_eP2PSessionError > 0) { Disconnect(true); switch (data.m_eP2PSessionError) { case 1: Logging.BMSLog.LogException("The target user is not running the same game."); break; case 2: Logging.BMSLog.LogException("The local user doesn't own the app that is running."); break; case 3: Logging.BMSLog.LogException("Target user isn't connected to Steam."); break; case 4: Logging.BMSLog.LogException("The connection timed out because the target user didn't respond, perhaps they aren't calling AcceptP2PSessionWithUser"); break; } } // }); } catch (Exception e) { Logging.BMSLog.LogException(e); // Do any generic initialization in result of the binding failure OnBindFailure(); throw new FailedBindingException("Failed to bind to host/port, see inner exception", e); } }
/// <summary> /// This will connect a UDP client to a given UDP server /// </summary> /// <param name="host">The server's host address on the network</param> /// <param name="port">The port that the server is hosting on</param> /// <param name="natHost">The NAT server host address, if blank NAT will be skipped</param> /// <param name="natPort">The port that the NAT server is hosting on</param> /// <param name="pendCreates">Immidiately set the NetWorker::PendCreates to true</param> public void Connect(string host, ushort port = DEFAULT_PORT, string natHost = "", ushort natPort = NatHolePunch.DEFAULT_NAT_SERVER_PORT, bool pendCreates = false, ushort overrideBindingPort = DEFAULT_PORT + 1) { if (Disposed) { throw new ObjectDisposedException("UDPClient", "This object has been disposed and can not be used to connect, please use a new UDPClient"); } // By default pending creates should be true and flushed when ready if (!pendCreates) { PendCreates = true; } try { ushort clientPort = overrideBindingPort; // Make sure not to listen on the same port as the server for local networks if (clientPort == port) { clientPort++; } for (; ; clientPort++) { try { Client = new CachedUdpClient(clientPort); break; } catch { if (port == 0) { throw new BaseNetworkException("There were no ports available starting from port " + port); } } } Client.EnableBroadcast = true; // If the server is behind a NAT, request for the port punch by the nat server if (!string.IsNullOrEmpty(natHost)) { nat.Connect(host, port, clientPort, natHost, natPort); } // Do any generic initialization in result of the successful bind OnBindSuccessful(); // 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[] connectHeader = Websockets.ConnectionHeader(headerHash, port); try { // Setup the identity of the server as a player server = new NetworkingPlayer(0, host, true, ResolveHost(host, port), this); } catch (ArgumentException) { if (connectAttemptFailed != null) { connectAttemptFailed(this); } throw; } // Create the thread that will be listening for new data from connected clients and start its execution Task.Queue(ReadNetwork); //Let myself know I connected successfully OnPlayerConnected(server); // Set myself as a connected client server.Connected = true; //Set the port SetPort(clientPort); int connectCounter = 0; Task.Queue(() => { do { // Send the accept headers to the server to validate Client.Send(connectHeader, connectHeader.Length, Server.IPEndPointHandle); Thread.Sleep(3000); } while (!headerExchanged && IsBound && ++connectCounter < CONNECT_TRIES); if (connectCounter >= CONNECT_TRIES) { if (connectAttemptFailed != null) { connectAttemptFailed(this); } } }); } catch (Exception e) { Logging.BMSLog.LogException(e); // Do any generic initialization in result of the binding failure OnBindFailure(); throw new FailedBindingException("Failed to bind to host/port, see inner exception", e); } }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> private void ReadClients() { IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 0); string incomingEndpoint = string.Empty; BMSByte packet = null; // Intentional infinite loop while (IsBound) { // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } try { // Read a packet from the network packet = Client.Receive(ref groupEP, ref incomingEndpoint); if (PacketLossSimulation > 0.0f && new Random().NextDouble() <= PacketLossSimulation) { // Skip this message continue; } BandwidthIn += (ulong)packet.Size; } catch { UDPNetworkingPlayer player; if (udpPlayers.TryGetValue(incomingEndpoint, out player)) { FinalizeRemovePlayer(player, true); } continue; } // Check to make sure a message was received if (packet == null || packet.Size <= 0) { continue; } if (!udpPlayers.ContainsKey(incomingEndpoint)) { SetupClient(packet, incomingEndpoint, groupEP); continue; } else { currentReadingPlayer = udpPlayers[incomingEndpoint]; if (!currentReadingPlayer.Accepted && !currentReadingPlayer.PendingAccepted) { // It is possible that the response validation was dropped so // check if the client is resending for a response byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); // The client has sent the connection request again if (response != null) { Client.Send(response, response.Length, groupEP); continue; } else { currentReadingPlayer.PendingAccepted = true; ReadPacket(packet); } } else { // Due to the Forge Networking protocol, the only time that packet 1 // will be 71 and the second packet be 69 is a forced disconnect reconnect if (packet[0] == 71 && packet[1] == 69) { udpPlayers.Remove(currentReadingPlayer.Ip + "+" + currentReadingPlayer.Port); FinalizeRemovePlayer(currentReadingPlayer, true); continue; } currentReadingPlayer.Ping(); ReadPacket(packet); } } } }
/// <summary> /// Connect this FacepunchP2PClient directly to a steam user's FacepunchP2PServer with SteamId specified /// </summary> /// <param name="hostId">The host's <see cref="SteamId"/> SteamId object</param> /// <param name="pendCreates">Immediately set the NetWorker::PendCreates to true</param> public void Connect(SteamId hostId, bool pendCreates = false) { if (Disposed) { throw new ObjectDisposedException("FacepunchP2PClient", "This object has been disposed and can not be used to connect, please use a new FacepunchP2PClient"); } // By default pending creates should be true and flushed when ready if (!pendCreates) { PendCreates = true; } try { ushort clientPort = DEFAULT_PORT; // Make sure not to listen on the same port as the server for local networks if (clientPort == DEFAULT_PORT) { clientPort++; } Client = new CachedFacepunchP2PClient(hostId); // Do any generic initialization in result of the successful bind OnBindSuccessful(); // 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[] connectHeader = Websockets.ConnectionHeader(headerHash, DEFAULT_PORT); // Setup the identity of the server as a player server = new NetworkingPlayer(0, hostId, true, this); // Create the thread that will be listening for new data from connected clients and start its execution Task.Queue(ReadNetwork); //Let myself know I connected successfully OnPlayerConnected(server); // Set myself as a connected client server.Connected = true; //Set the port SetPort(clientPort); int connectCounter = 0; Task.Queue(() => { do { // Send the accept headers to the server to validate Client.Send(connectHeader, connectHeader.Length, hostId, P2PSend.Reliable); Thread.Sleep(3000); } while (!headerExchanged && IsBound && ++connectCounter < CONNECT_TRIES); if (connectCounter >= CONNECT_TRIES) { if (connectAttemptFailed != null) { connectAttemptFailed(this); } } }); } catch (Exception e) { Logging.BMSLog.LogException(e); // Do any generic initialization in result of the binding failure OnBindFailure(); throw new FailedBindingException("Failed to bind to server's SteamId, see inner exception", e); } }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> private void ReadNetwork() { IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 0); string incomingEndpoint = string.Empty; try { BMSByte packet = null; // Intentional infinite loop while (IsBound) { // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } try { // Read a packet from the network packet = Client.Receive(ref groupEP, ref incomingEndpoint); if (PacketLossSimulation > 0.0f && new Random().NextDouble() <= PacketLossSimulation) { // Skip this message continue; } BandwidthIn += (ulong)packet.Size; } catch (SocketException /*ex*/) { // This is a common exception when we exit the blocking call //Logging.BMSLog.LogException(ex); Disconnect(true); } // Check to make sure a message was received if (packet == null || packet.Size <= 0) { continue; } // This message was not from the server if (groupEP.Address != Server.IPEndPointHandle.Address && groupEP.Port != Server.IPEndPointHandle.Port) { if (packet.Size == 1 && (packet[0] == SERVER_BROADCAST_CODE || packet[1] == CLIENT_BROADCAST_CODE)) { } else if (packet.Size.Between(2, 4) && packet[0] == BROADCAST_LISTING_REQUEST_1 && packet[1] == BROADCAST_LISTING_REQUEST_2 && packet[2] == BROADCAST_LISTING_REQUEST_3) { // This may be a local listing request so respond with the client flag byte Client.Send(new byte[] { CLIENT_BROADCAST_CODE }, 1, groupEP); } continue; } // Check to see if the headers have been exchanged if (!headerExchanged) { if (Websockets.ValidateResponseHeader(headerHash, packet.CompressBytes())) { headerExchanged = true; // TODO: When getting the user id, it should also get the server time // by using the current time in the payload and getting it back along with server time // Ping the server to finalize the player's connection Send(Text.CreateFromString(Time.Timestep, InstanceGuid.ToString(), false, Receivers.Server, MessageGroupIds.NETWORK_ID_REQUEST, false), true); } else if (packet.Size >= MINIMUM_FRAME_SIZE) { // The server sent us a message before sending a responseheader to validate // This happens if the server is not accepting connections or the max connection count has been reached // We will get two messages. The first one is either a MAX_CONNECTIONS or NOT_ACCEPT_CONNECTIONS group message. // The second one will be the DISCONNECT message UDPPacket formattedPacket = TranscodePacket(Server, packet); if (formattedPacket.groupId == MessageGroupIds.MAX_CONNECTIONS) { Logging.BMSLog.LogWarning("Max Players Reached On Server"); // Wait for the second message (Disconnect) continue; } if (formattedPacket.groupId == MessageGroupIds.NOT_ACCEPT_CONNECTIONS) { Logging.BMSLog.LogWarning("The server is busy and not accepting connections"); // Wait for the second message (Disconnect) continue; } if (formattedPacket.groupId == MessageGroupIds.DISCONNECT) { CloseConnection(); return; } // Received something unexpected so do the same thing as the if below Disconnect(true); break; } else if (packet.Size != 1 || packet[0] != 0) { Disconnect(true); break; } else { continue; } } else { if (packet.Size < MINIMUM_FRAME_SIZE) { continue; } // Format the byte data into a UDPPacket struct UDPPacket formattedPacket = TranscodePacket(Server, packet); // Check to see if this is a confirmation packet, which is just // a packet to say that the reliable packet has been read if (formattedPacket.isConfirmation) { if (formattedPacket.groupId == MessageGroupIds.DISCONNECT) { CloseConnection(); return; } OnMessageConfirmed(server, formattedPacket); continue; } if (formattedPacket.groupId == MessageGroupIds.AUTHENTICATION_FAILURE) { Logging.BMSLog.LogWarning("The server rejected the authentication attempt"); // Wait for the second message (Disconnect) continue; } // Add the packet to the manager so that it can be tracked and executed on complete packetManager.AddPacket(formattedPacket, PacketSequenceComplete, this); } } } catch (Exception ex) { Logging.BMSLog.LogException(ex); Disconnect(true); } }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> private void ReadClients() { IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 0); string incomingEndpoint = string.Empty; BMSByte packet = null; // Intentional infinite loop while (IsBound) { // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } try { // Read a packet from the network packet = Client.Receive(ref groupEP, ref incomingEndpoint); if (PacketLossSimulation > 0.0f && new Random().NextDouble() <= PacketLossSimulation) { // Skip this message continue; } BandwidthIn += (ulong)packet.Size; } catch (Exception ex) when(!(ex is SocketException)) { Console.WriteLine($"Error in ReadClients() infinite loop, error was {ex.Message}"); UDPNetworkingPlayer player; if (udpPlayers.TryGetValue(incomingEndpoint, out player)) { Console.WriteLine($"Following the error, player '{player.NetworkId}' will be removed"); FinalizeRemovePlayer(player, true); } continue; } // Check to make sure a message was received if (packet == null || packet.Size <= 0) { continue; } if (!udpPlayers.ContainsKey(incomingEndpoint)) { SetupClient(packet, incomingEndpoint, groupEP); continue; } else { currentReadingPlayer = udpPlayers[incomingEndpoint]; if (!currentReadingPlayer.Accepted && !currentReadingPlayer.PendingAccepted) { // It is possible that the response validation was dropped so // check if the client is resending for a response byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); // The client has sent the connection request again if (response != null) { Client.Send(response, response.Length, groupEP); continue; } else { currentReadingPlayer.PendingAccepted = true; ReadPacket(packet); } } else { currentReadingPlayer.Ping(); ReadPacket(packet); } } } }
public void Connect(string host, ushort port = DEFAULT_PORT, string natHost = "", ushort natPort = NatHolePunch.DEFAULT_NAT_SERVER_PORT, bool isSpecial = false) { // By default pending creates should be true and flushed when ready if (!isSpecial) { PendCreates = true; } try { // TODO: Remove + 1, it is for linux tests ushort clientPort = port; //(ushort)(port + 1); for (; ; clientPort++) { try { Client = new CachedUdpClient(clientPort); break; } catch { if (port == 0) { throw new BaseNetworkException("There were no ports available starting from port " + port); } } } Client.EnableBroadcast = true; // If the server is behind a NAT, request for the port punch by the nat server if (!string.IsNullOrEmpty(natHost)) { nat.Connect(host, port, clientPort, natHost, natPort); } // Do any generic initialization in result of the successful bind OnBindSuccessful(); // 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[] connectHeader = Websockets.ConnectionHeader(headerHash, port); // Setup the identity of the server as a player server = new NetworkingPlayer(0, host, true, ResolveHost(host, port), this); // Create the thread that will be listening for new data from connected clients and start its execution Task.Queue(ReadNetwork); //Let myself know I connected successfully OnPlayerConnected(server); // Set myself as a connected client server.Connected = true; //Set the port SetPort(clientPort); Task.Queue(() => { do { // Send the accept headers to the server to validate Client.Send(connectHeader, connectHeader.Length, Server.IPEndPointHandle); Thread.Sleep(3000); } while (!headerExchanged && IsBound); }); } catch (Exception e) { Logging.BMSLog.LogException(e); // Do any generic initialization in result of the binding failure OnBindFailure(); throw new FailedBindingException("Failed to bind to host/port, see inner exception", e); } }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> /// <summary> ///无限循环在单独的线程上监听来自所有连接客户端的新数据。 ///当readThreadCancel设置为true时,此循环中断 /// </ summary> private void ReadNetwork() { IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 0); string incomingEndpoint = string.Empty; try { BMSByte packet = null; // Intentional infinite loop while (IsBound) { //如果读取已被标记为取消,则从此循环中断开 // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } try { // Read a packet from the network packet = Client.Receive(ref groupEP, ref incomingEndpoint); if (PacketLossSimulation > 0.0f && new Random().NextDouble() <= PacketLossSimulation) { // Skip this message continue; } BandwidthIn += (ulong)packet.Size; } catch (SocketException /*ex*/) { // This is a common exception when we exit the blocking call //Logging.BMSLog.LogException(ex); Disconnect(true); } // Check to make sure a message was received if (packet == null || packet.Size <= 0) { continue; } // This message was not from the server if (groupEP.Address != Server.IPEndPointHandle.Address && groupEP.Port != Server.IPEndPointHandle.Port) { if (packet.Size == 1 && (packet[0] == SERVER_BROADCAST_CODE || packet[1] == CLIENT_BROADCAST_CODE)) { } else if (packet.Size.Between(2, 4) && packet[0] == BROADCAST_LISTING_REQUEST_1 && packet[1] == BROADCAST_LISTING_REQUEST_2 && packet[2] == BROADCAST_LISTING_REQUEST_3) { //这可能是一个本地列表请求,所以用客户端标志字节进行响应 // This may be a local listing request so respond with the client flag byte Client.Send(new byte[] { CLIENT_BROADCAST_CODE }, 1, groupEP); } continue; } // Check to see if the headers have been exchanged if (!headerExchanged) { if (Websockets.ValidateResponseHeader(headerHash, packet.CompressBytes())) { headerExchanged = true; // TODO: When getting the user id, it should also get the server time // by using the current time in the payload and getting it back along with server time // Ping the server to finalize the player's connection Send(Text.CreateFromString(Time.Timestep, InstanceGuid.ToString(), false, Receivers.Server, MessageGroupIds.NETWORK_ID_REQUEST, false), true); } else if (packet.Size != 1 || packet[0] != 0) { Disconnect(true); break; } else { continue; } } else { if (packet.Size < 17) { continue; } // 格式的字节数据到一个udppacket结构 // Format the byte data into a UDPPacket struct UDPPacket formattedPacket = TranscodePacket(Server, packet); // Check to see if this is a confirmation packet, which is just // a packet to say that the reliable packet has been read if (formattedPacket.isConfirmation) { if (formattedPacket.groupId == MessageGroupIds.DISCONNECT) { CloseConnection(); return; } OnMessageConfirmed(server, formattedPacket); continue; } //将数据包添加到管理器,以便可以在完成时跟踪和执行 // Add the packet to the manager so that it can be tracked and executed on complete packetManager.AddPacket(formattedPacket, PacketSequenceComplete, this); } } } catch (Exception ex) { Logging.BMSLog.LogException(ex); Disconnect(true); } }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> private void ReadClients() { // Intentional infinite loop while (IsBound && !NetWorker.EndingSession) { try { // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } // This will loop through all of the players, so make sure to set the lock to // prevent any changes from other threads lock (Players) { for (int i = 0; i < Players.Count; i++) { // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } NetworkStream playerStream = null; if (Players[i].IsHost) { continue; } try { lock (Players[i].MutexLock) { // Try to get the client stream if it is still available playerStream = Players[i].TcpClientHandle.GetStream(); } } 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(Players[i], true); continue; } // If the player is no longer connected, then make sure to disconnect it properly if (!Players[i].TcpClientHandle.Connected) { Disconnect(Players[i], false); continue; } // Only continue to read for this client if there is any data available for it if (!playerStream.DataAvailable) { continue; } int available = Players[i].TcpClientHandle.Available; // Determine if the player is fully connected if (!Players[i].Accepted) { // Determine if the player has been accepted by the server if (!Players[i].Connected) { lock (Players[i].MutexLock) { // Read everything from the stream as the client hasn't been accepted yet byte[] bytes = new byte[available]; playerStream.Read(bytes, 0, bytes.Length); // Validate that the connection headers are properly formatted byte[] response = Websockets.ValidateConnectionHeader(bytes); // 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(Players[i]); Disconnect(Players[i], false); continue; } // If all is in order then send the validated response to the client playerStream.Write(response, 0, response.Length); // The player has successfully connected Players[i].Connected = true; } } else { lock (Players[i].MutexLock) { // Consume the message even though it is not being used so that it is removed from the buffer Text frame = (Text)Factory.DecodeMessage(GetNextBytes(playerStream, available, true), true, MessageGroupIds.TCP_FIND_GROUP_ID, Players[i]); Players[i].InstanceGuid = frame.ToString(); OnPlayerGuidAssigned(Players[i]); lock (writeBuffer) { writeBuffer.Clear(); writeBuffer.Append(BitConverter.GetBytes(Players[i].NetworkId)); Send(Players[i].TcpClientHandle, new Binary(Time.Timestep, false, writeBuffer, Receivers.Target, MessageGroupIds.NETWORK_ID_REQUEST, true)); SendBuffer(Players[i]); // All systems go, the player has been accepted OnPlayerAccepted(Players[i]); } } } } else { try { lock (Players[i].MutexLock) { Players[i].Ping(); // Get the frame that was sent by the client, the client // does send masked data //TODO: THIS IS CAUSING ISSUES!!! WHY!?!?!!? FrameStream frame = Factory.DecodeMessage(GetNextBytes(playerStream, available, true), true, MessageGroupIds.TCP_FIND_GROUP_ID, Players[i]); // The client has told the server that it is disconnecting if (frame is ConnectionClose) { // Confirm the connection close Send(Players[i].TcpClientHandle, new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, true)); Disconnect(Players[i], false); continue; } FireRead(frame, Players[i]); } } catch { // The player is sending invalid data so disconnect them Disconnect(Players[i], true); } } } } // Go through all of the pending disconnections and clean them // up and finalize the disconnection CleanupDisconnections(); // Sleep so that we free up the CPU a bit from this thread Thread.Sleep(10); } catch (Exception ex) { Logging.BMSLog.LogException(ex); } } }
/// <summary> /// 无限循环在单独的线程上监听来自所有连接客户端的新数据。 /// readThreadCancel设置为true时,此循环会中断 /// /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> protected ReadState Read() { if (disconnectedSelf) { return(ReadState.Disconnect); } NetworkStream stream = client.GetStream(); if (stream == null) //Some reason the stream is null! //某些原因流为空! { return(ReadState.Continue); } //如果流不再可读,则断开连接 // If the stream no longer can read then disconnect if (!stream.CanRead) { Disconnect(true); return(ReadState.Disconnect); } //如果没有可用的数据,则通过休眠线程释放CPU // If there isn't any data available, then free up the CPU by sleeping the thread if (!stream.DataAvailable) { return(ReadState.Continue); } int available = client.Available; if (available == 0) { return(ReadState.Continue); } //确定这个客户端是否已被服务器接受 // Determine if this client has been accepted by the server yet if (!headerExchanged) { //读取流中所有可用的字节,因为这个客户端还没有连接 // Read all available bytes in the stream as this client hasn't connected yet byte[] bytes = new byte[available]; stream.Read(bytes, 0, bytes.Length); //来自服务器的第一个数据包响应将是一个字符串 // The first packet response from the server is going to be a string if (Websockets.ValidateResponseHeader(headerHash, bytes)) { headerExchanged = true; //通过Ping服务器来确定玩家的连接 // 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(ReadState.Disconnect); } } else { byte[] messageBytes = GetNextBytes(stream, available, false); //获取由服务器,服务器发送的帧 //不发送被屏蔽的数据,只有客户端发送false为掩码 // Get the frame that was sent by the server, the server // does not send masked data, only the client so send false for mask FrameStream frame = Factory.DecodeMessage(messageBytes, false, MessageGroupIds.TCP_FIND_GROUP_ID, Server); FireRead(frame, Server); } return(ReadState.Void); }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> private void ReadClients() { CSteamID messageFrom = default(CSteamID); BMSByte packet = null; // Intentional infinite loop while (IsBound) { // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } try { // Read a packet from the network uint msgSize = 0; if (SteamNetworking.IsP2PPacketAvailable(out msgSize)) { packet = Client.Receive(msgSize, out messageFrom); } else { Thread.Sleep(1); continue; } if (PacketLossSimulation > 0.0f && new Random().NextDouble() <= PacketLossSimulation) { // Skip this message continue; } BandwidthIn += (ulong)packet.Size; } catch (Exception e) { Logging.BMSLog.LogException(e); SteamNetworkingPlayer player; if (steamPlayers.TryGetValue(messageFrom, out player)) { FinalizeRemovePlayer(player, true); } continue; } // Check to make sure a message was received if (packet == null || packet.Size <= 0) { continue; } if (!steamPlayers.ContainsKey(messageFrom)) { SetupClient(packet, messageFrom); continue; } else { currentReadingPlayer = steamPlayers[messageFrom]; if (!currentReadingPlayer.Accepted && !currentReadingPlayer.PendingAccepted) { // It is possible that the response validation was dropped so // check if the client is resending for a response byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); // The client has sent the connection request again if (response != null) { Client.Send(response, response.Length, messageFrom, EP2PSend.k_EP2PSendReliable); continue; } else { currentReadingPlayer.PendingAccepted = true; ReadPacket(packet); } } else { // Due to the Forge Networking protocol, the only time that packet 1 // will be 71 and the second packet be 69 is a forced disconnect reconnect if (packet[0] == 71 && packet[1] == 69) { Logging.BMSLog.LogFormat("Received packet[0]=71 & packet[1]=69"); steamPlayers.Remove(messageFrom); FinalizeRemovePlayer(currentReadingPlayer, true); continue; } currentReadingPlayer.Ping(); ReadPacket(packet); } } } }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> protected ReadState Read() { if (disconnectedSelf) { return(ReadState.Disconnect); } NetworkStream stream = client.GetStream(); if (stream == null) //Some reason the stream is null! { return(ReadState.Continue); } // If the stream no longer can read then disconnect if (!stream.CanRead) { Disconnect(true); return(ReadState.Disconnect); } // If there isn't any data available, then free up the CPU by sleeping the thread if (!stream.DataAvailable) { return(ReadState.Continue); } int available = client.Available; if (available == 0) { return(ReadState.Continue); } // Determine if this client has been accepted by the server yet if (!headerExchanged) { // Read all available bytes in the stream as this client hasn't connected yet byte[] bytes = new byte[available]; stream.Read(bytes, 0, bytes.Length); // The first packet response from the server is going to be a string if (Websockets.ValidateResponseHeader(headerHash, bytes)) { headerExchanged = true; // 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(ReadState.Disconnect); } } else { byte[] messageBytes = GetNextBytes(stream, available, false); // Get the frame that was sent by the server, the server // does not send masked data, only the client so send false for mask FrameStream frame = Factory.DecodeMessage(messageBytes, false, MessageGroupIds.TCP_FIND_GROUP_ID, Server); if (frame is ConnectionClose) { // Close our CachedUDPClient so that it can no longer be used client.Close(); return(ReadState.Disconnect); } // A message has been successfully read from the network so relay that // to all methods registered to the event OnMessageReceived(Server, frame); } return(ReadState.Void); }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> /// <summary> ///无限循环在单独的线程上监听来自所有连接客户端的新数据。 ///当readThreadCancel设置为true时,此循环中断 /// </ summary> private void ReadClients() { IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 0); string incomingEndpoint = string.Empty; BMSByte packet = null; // 故意无限循环 // Intentional infinite loop while (IsBound) { //如果读取已被标记为取消,则从此循环中断开 // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } try { //从网络读取数据包 // Read a packet from the network packet = Client.Receive(ref groupEP, ref incomingEndpoint); // 模拟丢包 if (PacketLossSimulation > 0.0f && new Random().NextDouble() <= PacketLossSimulation) { // 丢掉这个消息 // Skip this message continue; } // 统计 宽带接收数据大小 BandwidthIn += (ulong)packet.Size; } catch { // 如果出错, 就查找该IP PROT的玩家,将该玩家踢掉 UDPNetworkingPlayer player; if (udpPlayers.TryGetValue(incomingEndpoint, out player)) { FinalizeRemovePlayer(player, true); } continue; } //检查以确保收到消息 // Check to make sure a message was received if (packet == null || packet.Size <= 0) { continue; } //如果玩家列表里不包含该包的发送者 if (!udpPlayers.ContainsKey(incomingEndpoint)) { // 创建该发送者的结构体保存 UDPNetworkingPlayer SetupClient(packet, incomingEndpoint, groupEP); continue; } else { currentReadingPlayer = udpPlayers[incomingEndpoint]; if (!currentReadingPlayer.Accepted && !currentReadingPlayer.PendingAccepted) { //响应验证可能会被丢弃 //检查客户端是否正在重新发送响应 // It is possible that the response validation was dropped so // check if the client is resending for a response byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); //客户端再次发送连接请求 // The client has sent the connection request again if (response != null) { Client.Send(response, response.Length, groupEP); continue; } else { // 将该玩家设置为等待接受确认 currentReadingPlayer.PendingAccepted = true; // 读取该玩家发来的数据包 ReadPacket(packet); } } else { //由于Forge网络协议,数据包唯一的时间1 //将是71,第二个数据包是69是强制断开连接 // Due to the Forge Networking protocol, the only time that packet 1 // will be 71 and the second packet be 69 is a forced disconnect reconnect if (packet[0] == 71 && packet[1] == 69) { udpPlayers.Remove(currentReadingPlayer.Ip + "+" + currentReadingPlayer.Port); FinalizeRemovePlayer(currentReadingPlayer, true); continue; } // 设置玩家最好ping时间 currentReadingPlayer.Ping(); // 读取该玩家发来的数据包 ReadPacket(packet); } } } }
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); } }