/// <summary> /// Pushes a single system message onto the wire directly /// </summary> internal void SendSingleUnreliableSystemMessage( NetSystemType tp, NetBuffer data, NetworkEndPoint remoteEP, NetConnection connection) { // packet number var sendBuffer = m_sendBuffer; sendBuffer.Reset(); // message type and channel sendBuffer.Write((byte)((int)NetMessageLibraryType.System | ((int)NetChannel.Unreliable << 3))); //TODO: remove this line (David) sendBuffer.Write((ushort)0); // payload length; variable byte encoded if (data == null) { sendBuffer.WriteVariableUInt32((uint)1); sendBuffer.Write((byte)tp); } else { int dataLen = data.LengthBytes; sendBuffer.WriteVariableUInt32((uint)(dataLen + 1)); sendBuffer.Write((byte)tp); sendBuffer.Write(data.Data, 0, dataLen); } SendPacket(m_sendBuffer.Data, m_sendBuffer.LengthBytes, remoteEP, connection); }
internal OutgoingNetMessage CreateSystemMessage(NetSystemType systemType) { OutgoingNetMessage msg = CreateOutgoingMessage(); msg.m_type = NetMessageLibraryType.System; msg.m_sequenceChannel = NetChannel.Unreliable; msg.m_sequenceNumber = 0; msg.m_data.Write((byte)systemType); return(msg); }
/// <summary> /// Thread-safe SendSingleUnreliableSystemMessage() /// </summary> internal void QueueSingleUnreliableSystemMessage( NetSystemType tp, NetBuffer data, NetworkEndPoint remoteEP, bool useBroadcast) { var susm = new SUSystemMessage(); susm.Type = tp; susm.Data = data; susm.Destination = remoteEP; susm.UseBroadcast = useBroadcast; m_susmQueue.Enqueue(susm); }
/// <summary> /// Pushes a single system message onto the wire directly /// </summary> internal void SendSingleUnreliableSystemMessage( NetSystemType tp, NetBuffer data, IPEndPoint remoteEP, bool useBroadcast) { // packet number m_sendBuffer.Reset(); // message type and channel m_sendBuffer.Write((byte)((int)NetMessageLibraryType.System | ((int)NetChannel.Unreliable << 3))); m_sendBuffer.Write((ushort)0); // payload length; variable byte encoded if (data == null) { m_sendBuffer.WriteVariableUInt32((uint)1); m_sendBuffer.Write((byte)tp); } else { int dataLen = data.LengthBytes; m_sendBuffer.WriteVariableUInt32((uint)(dataLen + 1)); m_sendBuffer.Write((byte)tp); m_sendBuffer.Write(data.Data, 0, dataLen); } if (useBroadcast) { bool wasSSL = m_suppressSimulatedLag; try { m_suppressSimulatedLag = true; m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); SendPacket(remoteEP); } finally { m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false); m_suppressSimulatedLag = wasSSL; } } else { SendPacket(remoteEP); } }
/* * internal void HandleUserMessage(NetMessage msg) * { * int seqNr = msg.m_sequenceNumber; * int chanNr = (int)msg.m_sequenceChannel; * bool isDuplicate = false; * * int relation = RelateToExpected(seqNr, chanNr, out isDuplicate); * * // * // Unreliable * // * if (msg.m_sequenceChannel == NetChannel.Unreliable) * { * // It's all good; add message * if (isDuplicate) * { * m_statistics.CountDuplicateMessage(msg); * m_owner.LogVerbose("Rejecting duplicate " + msg, this); * } * else * { * AcceptMessage(msg); * } * return; * } * * // * // Reliable unordered * // * if (msg.m_sequenceChannel == NetChannel.ReliableUnordered) * { * // send acknowledge (even if duplicate) * m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber); * * if (isDuplicate) * { * m_statistics.CountDuplicateMessage(msg); * m_owner.LogVerbose("Rejecting duplicate " + msg, this); * return; // reject duplicates * } * * // It's good; add message * AcceptMessage(msg); * * return; * } * * ushort nextSeq = (ushort)(seqNr + 1); * * if (chanNr < (int)NetChannel.ReliableInOrder1) * { * // * // Sequenced * // * if (relation < 0) * { * // late sequenced message * m_statistics.CountDroppedSequencedMessage(); * m_owner.LogVerbose("Dropping late sequenced " + msg, this); * return; * } * * // It's good; add message * AcceptMessage(msg); * * m_nextExpectedSequence[chanNr] = nextSeq; * return; * } * else * { * // * // Ordered * // * * // send ack (regardless) * m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber); * * if (relation < 0) * { * // late ordered message #if DEBUG * if (!isDuplicate) * m_owner.LogWrite("Ouch, weird! Late ordered message that's NOT a duplicate?! seqNr: " + seqNr + " expecting: " + m_nextExpectedSequence[chanNr], this); #endif * // must be duplicate * m_owner.LogVerbose("Dropping duplicate message " + seqNr, this); * m_statistics.CountDuplicateMessage(msg); * return; // rejected; don't advance next expected * } * * if (relation > 0) * { * // early message; withhold ordered * m_owner.LogVerbose("Withholding " + msg + " (expecting " + m_nextExpectedSequence[chanNr] + ")", this); * m_withheldMessages.Add(msg); * return; // return without advancing next expected * } * * // It's right on time! * AcceptMessage(msg); * * // ordered; release other withheld messages? * bool released = false; * do * { * released = false; * foreach (NetMessage wm in m_withheldMessages) * { * if ((int)wm.m_sequenceChannel == chanNr && wm.m_sequenceNumber == nextSeq) * { * m_owner.LogVerbose("Releasing withheld message " + wm, this); * m_withheldMessages.Remove(wm); * AcceptMessage(wm); * // no need to set rounds for this message; it was one when first related() and withheld * nextSeq++; * if (nextSeq >= NetConstants.NumSequenceNumbers) * nextSeq -= NetConstants.NumSequenceNumbers; * released = true; * break; * } * } * } while (released); * } * * // Common to Sequenced and Ordered * * //m_owner.LogVerbose("Setting next expected for " + (NetChannel)chanNr + " to " + nextSeq); * m_nextExpectedSequence[chanNr] = nextSeq; * * return; * } */ internal void HandleSystemMessage(IncomingNetMessage msg, double now) { msg.m_data.Position = 0; NetSystemType sysType = (NetSystemType)msg.m_data.ReadByte(); OutgoingNetMessage response = null; switch (sysType) { case NetSystemType.Disconnect: if (m_status == NetConnectionStatus.Disconnected) { return; } Disconnect(msg.m_data.ReadString(), 0.75f + ((float)m_currentAvgRoundtrip * 4), false, false); break; case NetSystemType.ConnectionRejected: string reason = msg.m_data.ReadString(); m_owner.NotifyApplication(NetMessageType.ConnectionRejected, reason, msg.m_sender, msg.m_senderEndPoint); Disconnect(reason, 0.0f, false, true); break; case NetSystemType.Connect: // ConnectReponse must have been losts string appIdent = msg.m_data.ReadString(); if (appIdent != m_owner.m_config.ApplicationIdentifier) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Connect for different application identification received: " + appIdent, null, msg.m_senderEndPoint); } return; } // read random identifer byte[] rnd = msg.m_data.ReadBytes(8); if (NetUtility.CompareElements(rnd, m_owner.m_randomIdentifier)) { // don't allow self-connect if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, msg.m_senderEndPoint); } return; } // read hail data m_remoteHailData = null; int hailBytesCount = (msg.m_data.LengthBits - msg.m_data.Position) / 8; if (hailBytesCount > 0) { m_remoteHailData = msg.m_data.ReadBytes(hailBytesCount); } // finalize disconnect if it's in process if (m_status == NetConnectionStatus.Disconnecting) { FinalizeDisconnect(); } // send response; even if connected response = m_owner.CreateSystemMessage(NetSystemType.ConnectResponse); if (m_localHailData != null) { response.m_data.Write(m_localHailData); } m_unsentMessages.Enqueue(response); break; case NetSystemType.ConnectResponse: if (m_status != NetConnectionStatus.Connecting && m_status != NetConnectionStatus.Connected) { m_owner.LogWrite("Received connection response but we're not connecting...", this); return; } // read hail data m_remoteHailData = null; int numHailBytes = (msg.m_data.LengthBits - msg.m_data.Position) / 8; if (numHailBytes > 0) { m_remoteHailData = msg.m_data.ReadBytes(numHailBytes); } // Send connectionestablished response = m_owner.CreateSystemMessage(NetSystemType.ConnectionEstablished); if (m_localHailData != null) { response.m_data.Write(m_localHailData); } m_unsentMessages.Enqueue(response); // send first ping 250ms after connected m_lastSentPing = now - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f); m_statistics.Reset(); SetInitialAveragePing(now - m_handshakeInitiated); SetStatus(NetConnectionStatus.Connected, "Connected"); break; case NetSystemType.ConnectionEstablished: if (m_status != NetConnectionStatus.Connecting) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Received connection response but we're not connecting...", this, msg.m_senderEndPoint); } return; } // read hail data if (m_remoteHailData == null) { int hbc = (msg.m_data.LengthBits - msg.m_data.Position) / 8; if (hbc > 0) { m_remoteHailData = msg.m_data.ReadBytes(hbc); } } // send first ping 100-350ms after connected m_lastSentPing = now - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f); m_statistics.Reset(); SetInitialAveragePing(now - m_handshakeInitiated); SetStatus(NetConnectionStatus.Connected, "Connected"); break; case NetSystemType.Ping: // also accepted as ConnectionEstablished if (m_isInitiator == false && m_status == NetConnectionStatus.Connecting) { m_owner.LogWrite("Received ping; interpreted as ConnectionEstablished", this); m_statistics.Reset(); SetInitialAveragePing(now - m_handshakeInitiated); SetStatus(NetConnectionStatus.Connected, "Connected"); } //LogWrite("Received ping; sending pong..."); SendPong(m_owner, m_remoteEndPoint, now); break; case NetSystemType.Pong: double twoWayLatency = now - m_lastSentPing; if (twoWayLatency < 0) { break; } ReceivedPong(twoWayLatency, msg); break; case NetSystemType.StringTableAck: ushort val = msg.m_data.ReadUInt16(); StringTableAcknowledgeReceived(val); break; default: m_owner.LogWrite("Undefined behaviour in NetConnection for system message " + sysType, this); break; } }
internal override void HandleReceivedMessage(IncomingNetMessage message, NetworkEndPoint senderEndpoint, double localTimeRecv) { //LogWrite("NetClient received message " + message); double now = NetTime.Now; int payLen = message.m_data.LengthBytes; // Discovery response? if (message.m_type == NetMessageLibraryType.System && payLen > 0) { NetSystemType sysType = (NetSystemType)message.m_data.PeekByte(); if (sysType == NetSystemType.DiscoveryResponse) { message.m_data.ReadByte(); // step past system type byte IncomingNetMessage resMsg = m_discovery.HandleResponse(message, senderEndpoint); if (resMsg != null) { resMsg.m_senderEndPoint = senderEndpoint; EnqueueReceivedMessage(resMsg); } return; } } // Out of band? if (message.m_type == NetMessageLibraryType.OutOfBand) { if ((m_enabledMessageTypes & NetMessageType.OutOfBandData) != NetMessageType.OutOfBandData) { return; // drop } // just deliver message.m_msgType = NetMessageType.OutOfBandData; message.m_senderEndPoint = senderEndpoint; EnqueueReceivedMessage(message); return; } if (message.m_sender != m_serverConnection && m_serverConnection != null) { return; // don't talk to strange senders after this } if (message.m_type == NetMessageLibraryType.Acknowledge) { m_serverConnection.HandleAckMessage(now, message); return; } // Handle system types if (message.m_type == NetMessageLibraryType.System) { if (payLen < 1) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Received malformed system message: " + message, m_serverConnection, senderEndpoint); } return; } NetSystemType sysType = (NetSystemType)message.m_data.Data[0]; switch (sysType) { case NetSystemType.ConnectResponse: case NetSystemType.Ping: case NetSystemType.Pong: case NetSystemType.Disconnect: case NetSystemType.ConnectionRejected: case NetSystemType.StringTableAck: if (m_serverConnection != null) { m_serverConnection.HandleSystemMessage(message, localTimeRecv); } return; case NetSystemType.Connect: case NetSystemType.ConnectionEstablished: case NetSystemType.Discovery: case NetSystemType.Error: default: if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for client and " + sysType, m_serverConnection, senderEndpoint); } return; } } Debug.Assert( message.m_type == NetMessageLibraryType.User || message.m_type == NetMessageLibraryType.UserFragmented ); if (m_serverConnection.Status == NetConnectionStatus.Connecting) { m_serverConnection.Disconnect("Received user message before ConnectResponse", 0.0f, true, true); return; /* * // lost connectresponse packet? * // Emulate it; * LogVerbose("Received user message before ConnectResponse; emulating ConnectResponse...", m_serverConnection); * IncomingNetMessage emuMsg = CreateIncomingMessage(); * emuMsg.m_type = NetMessageLibraryType.System; * emuMsg.m_data.Reset(); * emuMsg.m_data.Write((byte)NetSystemType.ConnectResponse); * m_serverConnection.HandleSystemMessage(emuMsg, now); */ // ... and proceed to pick up user message } // add to pick-up queue m_serverConnection.HandleUserMessage(message, now); }
internal override void HandleReceivedMessage(IncomingNetMessage message, NetworkEndPoint senderEndpoint, double timestamp) { double now = NetTime.Now; int payLen = message.m_data.LengthBytes; // Out of band? if (message.m_type == NetMessageLibraryType.OutOfBand) { if ((m_enabledMessageTypes & NetMessageType.OutOfBandData) != NetMessageType.OutOfBandData) { return; // drop } // just deliver message.m_msgType = NetMessageType.OutOfBandData; message.m_senderEndPoint = senderEndpoint; EnqueueReceivedMessage(message); return; } if (message.m_sender == null) { // // Handle unconnected message // // not a connected sender; only allow System messages if (message.m_type != NetMessageLibraryType.System) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Rejecting non-system message from unconnected source: " + message, null, message.m_senderEndPoint); } return; } // read type of system message NetSystemType sysType = (NetSystemType)message.m_data.ReadByte(); switch (sysType) { case NetSystemType.Connect: LogVerbose("Connection request received from " + senderEndpoint); NetConnection conn; if (m_pendingLookup.TryGetValue(senderEndpoint, out conn)) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Ignore connection request received because already pending", conn, senderEndpoint); } return; } if (m_connectionLookup.TryGetValue(senderEndpoint, out conn)) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Ignore connection request received because already connected", conn, senderEndpoint); } return; } // check app id if (payLen < 4) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Malformed Connect message received from " + senderEndpoint, null, senderEndpoint); } return; } string appIdent = message.m_data.ReadString(); if (appIdent != m_config.ApplicationIdentifier) { if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { NotifyApplication(NetMessageType.ConnectionRejected, "Bad app id", null, senderEndpoint); } // send connection rejected NetBuffer rejreason = new NetBuffer("Bad app id"); QueueSingleUnreliableSystemMessage( NetSystemType.ConnectionRejected, rejreason, senderEndpoint, false ); return; } // read random identifer var rndSignature = message.m_data.ReadBytes(NetConstants.SignatureByteSize); if (NetUtility.CompareElements(rndSignature, m_localRndSignature)) { // don't allow self-connect if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, senderEndpoint); } return; } int rndSeqNr = message.m_data.ReadInt32(); double localTimeSent = message.m_data.ReadDouble(); double remoteTimeRecv = timestamp + m_localTimeOffset; int bytesReadSoFar = message.m_data.PositionBytes; int hailLen = message.m_data.LengthBytes - bytesReadSoFar; byte[] hailData = null; if (hailLen > 0) { hailData = new byte[hailLen]; Buffer.BlockCopy(message.m_data.Data, bytesReadSoFar, hailData, 0, hailLen); } if (m_config.m_maxConnections != -1 && m_connections.Count >= m_config.m_maxConnections) { if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { NotifyApplication(NetMessageType.ConnectionRejected, "Max connections", null, senderEndpoint); } // send connection rejected NetBuffer rejreason = new NetBuffer("Server full"); QueueSingleUnreliableSystemMessage( NetSystemType.ConnectionRejected, rejreason, senderEndpoint, false ); return; } #if LIMITED_BUILD if (m_connections.Count >= 2) { if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { NotifyApplication(NetMessageType.ConnectionRejected, "I'm special", null, senderEndpoint); } // send connection rejected NetBuffer rejreason = new NetBuffer("Special server"); QueueSingleUnreliableSystemMessage( NetSystemType.ConnectionRejected, rejreason, senderEndpoint, false ); return; } #endif // Create connection LogWrite("New connection: " + senderEndpoint); conn = new NetConnection(this, senderEndpoint, null, hailData, rndSignature, rndSeqNr); m_pendingLookup.Add(senderEndpoint, conn); conn.m_connectLocalSentTime = localTimeSent; conn.m_connectRemoteRecvTime = remoteTimeRecv; // Connection approval? if ((m_enabledMessageTypes & NetMessageType.ConnectionApproval) == NetMessageType.ConnectionApproval) { // Ask application if this connection is allowed to proceed IncomingNetMessage app = CreateIncomingMessage(); app.m_msgType = NetMessageType.ConnectionApproval; if (hailData != null) { app.m_data.Write(hailData); } app.m_sender = conn; conn.m_approved = false; EnqueueReceivedMessage(app); // Don't add connection; it's done as part of the approval procedure return; } // it's ok AddConnection(now, conn); break; case NetSystemType.ConnectionEstablished: if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Connection established received from non-connection! " + senderEndpoint, null, senderEndpoint); } return; case NetSystemType.Discovery: if (m_config.AnswerDiscoveryRequests) { m_discovery.HandleRequest(message, senderEndpoint); } break; case NetSystemType.DiscoveryResponse: if (m_allowOutgoingConnections) { // NetPeer IncomingNetMessage resMsg = m_discovery.HandleResponse(message, senderEndpoint); if (resMsg != null) { resMsg.m_senderEndPoint = senderEndpoint; EnqueueReceivedMessage(resMsg); } } break; case NetSystemType.Ping: { var tempBuffer = GetTempBuffer(); tempBuffer.Write("We're not connected"); SendSingleUnreliableSystemMessage( NetSystemType.Disconnect, tempBuffer, senderEndpoint, null ); if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Ignore " + this + " receiving system type " + sysType + ": " + message + " from unconnected source", null, senderEndpoint); } break; } default: if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Ignore " + this + " receiving system type " + sysType + ": " + message + " from unconnected source", null, senderEndpoint); } break; } // done return; } // ok, we have a sender if (message.m_type == NetMessageLibraryType.Acknowledge) { message.m_sender.HandleAckMessage(now, message); return; } if (message.m_type == NetMessageLibraryType.System) { // // Handle system messages from connected source // if (payLen < 1) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Received malformed system message; payload length less than 1 byte", null, senderEndpoint); } return; } NetSystemType sysType = (NetSystemType)message.m_data.ReadByte(); switch (sysType) { case NetSystemType.Connect: case NetSystemType.ConnectionEstablished: case NetSystemType.Ping: case NetSystemType.Pong: case NetSystemType.Disconnect: case NetSystemType.ConnectionRejected: case NetSystemType.StringTableAck: message.m_sender.HandleSystemMessage(message, now); break; case NetSystemType.ConnectResponse: if (m_allowOutgoingConnections) { message.m_sender.HandleSystemMessage(message, now); } else { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for server and system type " + sysType, null, senderEndpoint); } } break; case NetSystemType.Discovery: // Allow discovery even if connected if (m_config.AnswerDiscoveryRequests) { m_discovery.HandleRequest(message, senderEndpoint); } break; default: if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for server and system type " + sysType, null, senderEndpoint); } break; } return; } message.m_sender.HandleUserMessage(message, now); }
/// <summary> /// Pushes a single system message onto the wire directly /// </summary> internal void SendSingleUnreliableSystemMessage( NetSystemType tp, NetBuffer data, IPEndPoint remoteEP, bool useBroadcast) { // packet number m_sendBuffer.Reset(); // message type and channel m_sendBuffer.Write((byte)((int)NetMessageLibraryType.System | ((int)NetChannel.Unreliable << 3))); m_sendBuffer.Write((ushort)0); // payload length; variable byte encoded if (data == null) { m_sendBuffer.WriteVariableUInt32((uint)1); m_sendBuffer.Write((byte)tp); } else { int dataLen = data.LengthBytes; m_sendBuffer.WriteVariableUInt32((uint)(dataLen + 1)); m_sendBuffer.Write((byte)tp); m_sendBuffer.Write(data.Data, 0, dataLen); } if (useBroadcast) { bool wasSSL = m_suppressSimulatedLag; try { m_suppressSimulatedLag = true; m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); SendPacket(remoteEP); } finally { m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false); m_suppressSimulatedLag = wasSSL; } } else { SendPacket(remoteEP); } }
/// <summary> /// Thread-safe SendSingleUnreliableSystemMessage() /// </summary> internal void QueueSingleUnreliableSystemMessage( NetSystemType tp, NetBuffer data, IPEndPoint remoteEP, bool useBroadcast) { SUSystemMessage susm = new SUSystemMessage(); susm.Type = tp; susm.Data = data; susm.Destination = remoteEP; susm.UseBroadcast = useBroadcast; lock (m_susmQueue) m_susmQueue.Enqueue(susm); }
internal OutgoingNetMessage CreateSystemMessage(NetSystemType systemType) { OutgoingNetMessage msg = CreateOutgoingMessage(); msg.m_type = NetMessageLibraryType.System; msg.m_sequenceChannel = NetChannel.Unreliable; msg.m_sequenceNumber = 0; msg.m_data.Write((byte)systemType); return msg; }
internal override void HandleReceivedMessage(IncomingNetMessage message, IPEndPoint senderEndpoint) { double now = NetTime.Now; int payLen = message.m_data.LengthBytes; // NAT introduction? if (HandleNATIntroduction(message)) { return; } // Out of band? if (message.m_type == NetMessageLibraryType.OutOfBand) { if ((m_enabledMessageTypes & NetMessageType.OutOfBandData) != NetMessageType.OutOfBandData) { return; // drop } // just deliever message.m_msgType = NetMessageType.OutOfBandData; message.m_senderEndPoint = senderEndpoint; lock (m_receivedMessages) m_receivedMessages.Enqueue(message); return; } if (message.m_sender == null) { // // Handle unconnected message // // not a connected sender; only allow System messages if (message.m_type != NetMessageLibraryType.System) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Rejecting non-system message from unconnected source: " + message, null, message.m_senderEndPoint); } return; } // read type of system message NetSystemType sysType = (NetSystemType)message.m_data.ReadByte(); switch (sysType) { case NetSystemType.Connect: LogVerbose("Connection request received from " + senderEndpoint); // check app ident if (payLen < 4) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Malformed Connect message received from " + senderEndpoint, null, senderEndpoint); } return; } string appIdent = message.m_data.ReadString(); if (appIdent != m_config.ApplicationIdentifier) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Connect for different application identification received: " + appIdent, null, senderEndpoint); } return; } // read random identifer byte[] rnd = message.m_data.ReadBytes(8); if (NetUtility.CompareElements(rnd, m_randomIdentifier)) { // don't allow self-connect if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, senderEndpoint); } return; } int bytesReadSoFar = (message.m_data.Position / 8); int hailLen = message.m_data.LengthBytes - bytesReadSoFar; byte[] hailData = null; if (hailLen > 0) { hailData = new byte[hailLen]; Buffer.BlockCopy(message.m_data.Data, bytesReadSoFar, hailData, 0, hailLen); } if (m_connections.Count >= m_config.m_maxConnections) { if ((m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { NotifyApplication(NetMessageType.ConnectionRejected, "Server full; rejecting connect from " + senderEndpoint, null, senderEndpoint); } return; } // Create connection LogWrite("New connection: " + senderEndpoint); NetConnection conn = new NetConnection(this, senderEndpoint, null, hailData); // Connection approval? if ((m_enabledMessageTypes & NetMessageType.ConnectionApproval) == NetMessageType.ConnectionApproval) { // Ask application if this connection is allowed to proceed IncomingNetMessage app = CreateIncomingMessage(); app.m_msgType = NetMessageType.ConnectionApproval; if (hailData != null) { app.m_data.Write(hailData); } app.m_sender = conn; conn.m_approved = false; lock (m_receivedMessages) m_receivedMessages.Enqueue(app); // Don't add connection; it's done as part of the approval procedure return; } // it's ok AddConnection(now, conn); break; case NetSystemType.ConnectionEstablished: if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Connection established received from non-connection! " + senderEndpoint, null, senderEndpoint); } return; case NetSystemType.Discovery: if (m_config.AnswerDiscoveryRequests) { m_discovery.HandleRequest(message, senderEndpoint); } break; case NetSystemType.DiscoveryResponse: if (m_allowOutgoingConnections) { // NetPeer IncomingNetMessage resMsg = m_discovery.HandleResponse(message, senderEndpoint); resMsg.m_senderEndPoint = senderEndpoint; if (resMsg != null) { lock (m_receivedMessages) m_receivedMessages.Enqueue(resMsg); } } break; default: if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for " + this + " receiving system type " + sysType + ": " + message + " from unconnected source", null, senderEndpoint); } break; } // done return; } // ok, we have a sender if (message.m_type == NetMessageLibraryType.Acknowledge) { message.m_sender.HandleAckMessage(message); return; } if (message.m_type == NetMessageLibraryType.System) { // // Handle system messages from connected source // if (payLen < 1) { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Received malformed system message; payload length less than 1 byte", null, senderEndpoint); } return; } NetSystemType sysType = (NetSystemType)message.m_data.ReadByte(); switch (sysType) { case NetSystemType.Connect: case NetSystemType.ConnectionEstablished: case NetSystemType.Ping: case NetSystemType.Pong: case NetSystemType.Disconnect: case NetSystemType.ConnectionRejected: case NetSystemType.StringTableAck: message.m_sender.HandleSystemMessage(message, now); break; case NetSystemType.ConnectResponse: if (m_allowOutgoingConnections) { message.m_sender.HandleSystemMessage(message, now); } else { if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for server and system type " + sysType, null, senderEndpoint); } } break; case NetSystemType.Discovery: // Allow discovery even if connected if (m_config.AnswerDiscoveryRequests) { m_discovery.HandleRequest(message, senderEndpoint); } break; default: if ((m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { NotifyApplication(NetMessageType.BadMessageReceived, "Undefined behaviour for server and system type " + sysType, null, senderEndpoint); } break; } return; } message.m_sender.HandleUserMessage(message); }
/* * internal void HandleUserMessage(NetMessage msg) * { * int seqNr = msg.m_sequenceNumber; * int chanNr = (int)msg.m_sequenceChannel; * bool isDuplicate = false; * * int relation = RelateToExpected(seqNr, chanNr, out isDuplicate); * * // * // Unreliable * // * if (msg.m_sequenceChannel == NetChannel.Unreliable) * { * // It's all good; add message * if (isDuplicate) * { * m_statistics.CountDuplicateMessage(msg); * m_owner.LogVerbose("Rejecting duplicate " + msg, this); * } * else * { * AcceptMessage(msg); * } * return; * } * * // * // Reliable unordered * // * if (msg.m_sequenceChannel == NetChannel.ReliableUnordered) * { * // send acknowledge (even if duplicate) * m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber); * * if (isDuplicate) * { * m_statistics.CountDuplicateMessage(msg); * m_owner.LogVerbose("Rejecting duplicate " + msg, this); * return; // reject duplicates * } * * // It's good; add message * AcceptMessage(msg); * * return; * } * * ushort nextSeq = (ushort)(seqNr + 1); * * if (chanNr < (int)NetChannel.ReliableInOrder1) * { * // * // Sequenced * // * if (relation < 0) * { * // late sequenced message * m_statistics.CountDroppedSequencedMessage(); * m_owner.LogVerbose("Dropping late sequenced " + msg, this); * return; * } * * // It's good; add message * AcceptMessage(msg); * * m_nextExpectedSequence[chanNr] = nextSeq; * return; * } * else * { * // * // Ordered * // * * // send ack (regardless) * m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber); * * if (relation < 0) * { * // late ordered message #if DEBUG * if (!isDuplicate) * m_owner.LogWrite("Ouch, weird! Late ordered message that's NOT a duplicate?! seqNr: " + seqNr + " expecting: " + m_nextExpectedSequence[chanNr], this); #endif * // must be duplicate * m_owner.LogVerbose("Dropping duplicate message " + seqNr, this); * m_statistics.CountDuplicateMessage(msg); * return; // rejected; don't advance next expected * } * * if (relation > 0) * { * // early message; withhold ordered * m_owner.LogVerbose("Withholding " + msg + " (expecting " + m_nextExpectedSequence[chanNr] + ")", this); * m_withheldMessages.Add(msg); * return; // return without advancing next expected * } * * // It's right on time! * AcceptMessage(msg); * * // ordered; release other withheld messages? * bool released = false; * do * { * released = false; * foreach (NetMessage wm in m_withheldMessages) * { * if ((int)wm.m_sequenceChannel == chanNr && wm.m_sequenceNumber == nextSeq) * { * m_owner.LogVerbose("Releasing withheld message " + wm, this); * m_withheldMessages.Remove(wm); * AcceptMessage(wm); * // no need to set rounds for this message; it was one when first related() and withheld * nextSeq++; * if (nextSeq >= NetConstants.NumSequenceNumbers) * nextSeq -= NetConstants.NumSequenceNumbers; * released = true; * break; * } * } * } while (released); * } * * // Common to Sequenced and Ordered * * //m_owner.LogVerbose("Setting next expected for " + (NetChannel)chanNr + " to " + nextSeq); * m_nextExpectedSequence[chanNr] = nextSeq; * * return; * } */ internal void HandleSystemMessage(IncomingNetMessage msg, double timestamp) { msg.m_data.PositionBits = 0; NetSystemType sysType = (NetSystemType)msg.m_data.ReadByte(); switch (sysType) { case NetSystemType.Disconnect: if (m_status == NetConnectionStatus.Disconnected) { return; } Disconnect(msg.m_data.ReadString(), NetConstants.DisconnectLingerTime, false, false); break; case NetSystemType.ConnectionRejected: string reason = msg.m_data.ReadString(); m_owner.NotifyApplication(NetMessageType.ConnectionRejected, reason, msg.m_sender, msg.m_senderEndPoint); Disconnect(reason, 0.0f, false, true); break; case NetSystemType.Connect: { // ConnectReponse must have been losts /*if (m_isInitiator) // this might be useful in the future * { * m_owner.LogWrite("Received connect but we're the initiator...", this); * return; // ignore * }*/ if (Status != NetConnectionStatus.Connecting && Status != NetConnectionStatus.Connected) { m_owner.LogWrite("Received connect but we're not connecting or connected...", this); return; // ignore if disconnecting } string appIdent = msg.m_data.ReadString(); if (appIdent != m_owner.m_config.ApplicationIdentifier) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Connect for different application identification received: " + appIdent, null, msg.m_senderEndPoint); } return; } // read random identifier var remoteRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize); if (!m_owner.Configuration.AllowConnectionToSelf && NetUtility.CompareElements(remoteRndSignature, m_owner.m_localRndSignature)) { // don't allow self-connect if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, msg.m_senderEndPoint); } return; } int remoteRndSeqNr = msg.m_data.ReadInt32(); if (m_remoteRndSignature != null) { if (!NetUtility.CompareElements(remoteRndSignature, m_remoteRndSignature) || remoteRndSeqNr != m_remoteRndSeqNr) { // this is not the same connection. Disconnect("Not the same connection", 0, false, true); return; } } else // this might happen if both try and connect at the same time. { m_remoteRndSignature = remoteRndSignature; m_remoteRndSeqNr = remoteRndSeqNr; ResetReliability(); } m_connectLocalSentTime = msg.m_data.ReadDouble(); m_connectRemoteRecvTime = timestamp + m_owner.m_localTimeOffset; // read hail data m_remoteHailData = null; int hailBytesCount = (msg.m_data.LengthBits - msg.m_data.PositionBits) / 8; if (hailBytesCount > 0) { m_remoteHailData = msg.m_data.ReadBytes(hailBytesCount); } // send response; even if connected var responseBuffer = m_owner.GetTempBuffer(); responseBuffer.Write(m_owner.m_localRndSignature); responseBuffer.Write(m_localRndSeqNr); responseBuffer.Write(m_remoteRndSignature); responseBuffer.Write(m_remoteRndSeqNr); double localTimeSent = m_connectLocalSentTime; responseBuffer.Write(localTimeSent); double remoteTimeRecv = m_connectRemoteRecvTime; responseBuffer.Write(remoteTimeRecv); double remoteTimeSent = NetTime.Now + m_owner.m_localTimeOffset; responseBuffer.Write(remoteTimeSent); if (m_localHailData != null) { responseBuffer.Write(m_localHailData); } m_handshakeInitiated = remoteTimeSent; SendSingleUnreliableSystemMessage(NetSystemType.ConnectResponse, responseBuffer); break; } case NetSystemType.ConnectResponse: { /*if (m_isInitiator) // this might be useful in the future * { * m_owner.LogWrite("Received connect but we're the initiator...", this); * return; // ignore * }*/ if (m_status != NetConnectionStatus.Connecting && m_status != NetConnectionStatus.Connected) { m_owner.LogWrite("Received connection response but we're not connecting or connected...", this); return; } var remoteRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize); if (!m_owner.Configuration.AllowConnectionToSelf && NetUtility.CompareElements(remoteRndSignature, m_owner.m_localRndSignature)) { // don't allow self-connect if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, msg.m_senderEndPoint); } return; } int remoteRndSeqNr = msg.m_data.ReadInt32(); if (m_remoteRndSignature != null) { if (!NetUtility.CompareElements(remoteRndSignature, m_remoteRndSignature) || remoteRndSeqNr != m_remoteRndSeqNr) { // this is not the same connection. Disconnect("Not the same connection", 0, false, true); return; } } else // this might happen if both try and connect at the same time. { m_remoteRndSignature = remoteRndSignature; m_remoteRndSeqNr = remoteRndSeqNr; ResetReliability(); } var myRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize); var myRndSeqNr = msg.m_data.ReadInt32(); if (!NetUtility.CompareElements(myRndSignature, m_owner.m_localRndSignature) || myRndSeqNr != m_localRndSeqNr) { // this is not the same connection. Disconnect("Not the same connection", 0, false, true); return; } double localTimeSent = msg.m_data.ReadDouble(); double remoteTimeRecv = msg.m_data.ReadDouble(); double remoteTimeSent = msg.m_data.ReadDouble(); double localTimeRecv = timestamp + m_owner.m_localTimeOffset; // read hail data m_remoteHailData = null; int numHailBytes = (msg.m_data.LengthBits - msg.m_data.PositionBits) / 8; if (numHailBytes > 0) { m_remoteHailData = msg.m_data.ReadBytes(numHailBytes); } // Send connection established var responseBuffer = m_owner.GetTempBuffer(); responseBuffer.Write(m_owner.m_localRndSignature); responseBuffer.Write(m_localRndSeqNr); responseBuffer.Write(m_remoteRndSignature); responseBuffer.Write(m_remoteRndSeqNr); double intiatorLocalTimeSent = remoteTimeSent; responseBuffer.Write(intiatorLocalTimeSent); double intiatorRemoteTimeRecv = localTimeRecv; responseBuffer.Write(intiatorRemoteTimeRecv); double intiatorRemoteTimeSent = NetTime.Now + m_owner.m_localTimeOffset; responseBuffer.Write(intiatorRemoteTimeSent); if (m_localHailData != null) { responseBuffer.Write(m_localHailData); } SendSingleUnreliableSystemMessage(NetSystemType.ConnectionEstablished, responseBuffer); // send first ping 250ms after connected m_lastSentPing = timestamp - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f); m_statistics.Reset(); SetInitialPongEntry(new PongEntry(localTimeSent, remoteTimeRecv, remoteTimeSent, localTimeRecv)); SetStatus(NetConnectionStatus.Connected, "Connected"); break; } case NetSystemType.ConnectionEstablished: { if (m_status != NetConnectionStatus.Connecting) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Received connection response but we're not connecting...", this, msg.m_senderEndPoint); } return; } var remoteRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize); if (!m_owner.Configuration.AllowConnectionToSelf && NetUtility.CompareElements(remoteRndSignature, m_owner.m_localRndSignature)) { // don't allow self-connect if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, msg.m_senderEndPoint); } return; } int remoteRndSeqNr = msg.m_data.ReadInt32(); if (m_remoteRndSignature != null) { if (!NetUtility.CompareElements(remoteRndSignature, m_remoteRndSignature) || remoteRndSeqNr != m_remoteRndSeqNr) { // this is not the same connection. Disconnect("Not the same connection", 0, false, true); return; } } else // this might happen if both try and connect at the same time. { m_remoteRndSignature = remoteRndSignature; m_remoteRndSeqNr = remoteRndSeqNr; ResetReliability(); } var myRndSignature = msg.m_data.ReadBytes(NetConstants.SignatureByteSize); var myRndSeqNr = msg.m_data.ReadInt32(); if (!NetUtility.CompareElements(myRndSignature, m_owner.m_localRndSignature) || myRndSeqNr != m_localRndSeqNr) { // this is not the same connection. Disconnect("Not the same connection", 0, false, true); return; } double localTimeSent = msg.m_data.ReadDouble(); double remoteTimeRecv = msg.m_data.ReadDouble(); double remoteTimeSent = msg.m_data.ReadDouble(); double localTimeRecv = timestamp + m_owner.m_localTimeOffset; // read hail data if (m_remoteHailData == null) { int hbc = (msg.m_data.LengthBits - msg.m_data.PositionBits) / 8; if (hbc > 0) { m_remoteHailData = msg.m_data.ReadBytes(hbc); } } // send first ping 100-350ms after connected m_lastSentPing = timestamp - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f); m_statistics.Reset(); SetInitialPongEntry(new PongEntry(localTimeSent, remoteTimeRecv, remoteTimeSent, localTimeRecv)); SetStatus(NetConnectionStatus.Connected, "Connected"); break; } case NetSystemType.Ping: // also accepted as ConnectionEstablished if (m_isInitiator == false && m_status == NetConnectionStatus.Connecting) { m_owner.LogWrite("Received ping; interpreted as ConnectionEstablished", this); m_statistics.Reset(); SetInitialPongEntryApprox(timestamp - m_handshakeInitiated); SetStatus(NetConnectionStatus.Connected, "Connected"); } if (m_status != NetConnectionStatus.Connected) { m_owner.LogWrite("Received ping but we're not connected...", this); if (m_status == NetConnectionStatus.Disconnected) { // this might cause a issue (especially if both are trying to connect at the same time). Disconnect("We're not connected", 0, true, true); } return; } ReceivedPing(msg, timestamp + m_owner.m_localTimeOffset); break; case NetSystemType.Pong: ReceivedPong(msg, timestamp + m_owner.m_localTimeOffset); break; case NetSystemType.StringTableAck: ushort val = msg.m_data.ReadUInt16(); StringTableAcknowledgeReceived(val); break; default: m_owner.LogWrite("Undefined behavior in NetConnection for system message " + sysType, this); break; } }
internal void SendSingleUnreliableSystemMessage(NetSystemType tp, NetBuffer data) { m_owner.SendSingleUnreliableSystemMessage(tp, data, m_remoteEndPoint, this); }