/// <summary> /// Connect to remote host /// </summary> /// <param name="address">Server IP or hostname</param> /// <param name="port">Server Port</param> /// <param name="connectionData">Additional data for remote peer</param> /// <returns>Null if connections limit reached, New NetPeer if new connection, Old NetPeer if already connected</returns> /// <exception cref="InvalidOperationException">Manager is not running. Call <see cref="Start()"/></exception> public NetPeer Connect(string address, int port, NetDataWriter connectionData) { var ep = NetUtils.MakeEndPoint(address, port); return(Connect(ep, connectionData)); }
private void DataReceived(byte[] reusableBuffer, int count, IPEndPoint remoteEndPoint) { #if STATS_ENABLED Statistics.PacketsReceived++; Statistics.BytesReceived += (uint)count; #endif //Try read packet NetPacket packet = NetPacketPool.GetPacket(count, false); if (!packet.FromBytes(reusableBuffer, 0, count)) { NetPacketPool.Recycle(packet); NetUtils.DebugWriteError("[NM] DataReceived: bad!"); return; } //Check unconnected switch (packet.Property) { case PacketProperty.DiscoveryRequest: if (DiscoveryEnabled) { var netEvent = CreateEvent(NetEventType.DiscoveryRequest); netEvent.RemoteEndPoint = remoteEndPoint; netEvent.DataReader.SetSource(packet.RawData, NetConstants.HeaderSize, count); EnqueueEvent(netEvent); } return; case PacketProperty.DiscoveryResponse: { var netEvent = CreateEvent(NetEventType.DiscoveryResponse); netEvent.RemoteEndPoint = remoteEndPoint; netEvent.DataReader.SetSource(packet.RawData, NetConstants.HeaderSize, count); EnqueueEvent(netEvent); } return; case PacketProperty.UnconnectedMessage: if (UnconnectedMessagesEnabled) { var netEvent = CreateEvent(NetEventType.ReceiveUnconnected); netEvent.RemoteEndPoint = remoteEndPoint; netEvent.DataReader.SetSource(packet.RawData, NetConstants.HeaderSize, count); EnqueueEvent(netEvent); } return; case PacketProperty.NatIntroduction: case PacketProperty.NatIntroductionRequest: case PacketProperty.NatPunchMessage: { if (NatPunchEnabled) { NatPunchModule.ProcessMessage(remoteEndPoint, packet); } return; } } //Check normal packets NetPeer netPeer; bool isPeerConnecting; lock (connectingPeers) { isPeerConnecting = connectingPeers.Contains(remoteEndPoint); peers.TryGetValue(remoteEndPoint, out netPeer); } if (netPeer != null) { if (netPeer.ConnectionState == ConnectionState.Disconnected) { return; } NetEvent netEvent; switch (packet.Property) { case PacketProperty.Disconnect: if (netPeer.ConnectionState == ConnectionState.InProgress || netPeer.ConnectionState == ConnectionState.Connected) { if (BitConverter.ToInt64(packet.RawData, 1) != netPeer.ConnectId) { //Old or incorrect disconnect NetPacketPool.Recycle(packet); return; } netEvent = CreateEvent(NetEventType.Disconnect); netEvent.Peer = netPeer; netEvent.DataReader.SetSource(packet.RawData, 9, packet.Size); netEvent.DisconnectReason = DisconnectReason.RemoteConnectionClose; EnqueueEvent(netEvent); netPeer.ProcessPacket(packet); SendRaw(new[] { (byte)PacketProperty.ShutdownOk }, 0, 1, remoteEndPoint); } return; case PacketProperty.ConnectAccept: if (netPeer.ProcessConnectAccept(packet)) { var connectEvent = CreateEvent(NetEventType.Connect); connectEvent.Peer = netPeer; EnqueueEvent(connectEvent); } return; case PacketProperty.ConnectRequest: long newId = BitConverter.ToInt64(packet.RawData, NetConstants.RequestConnectIdIndex); NetUtils.DebugWrite("ConnectRequest LastId: {0}, NewId: {1}, EP: {2}", netPeer.ConnectId, newId, remoteEndPoint); //Remove old peer and connect new if (newId > netPeer.ConnectId) { netEvent = CreateEvent(NetEventType.Disconnect); netEvent.Peer = netPeer; netEvent.DataReader.SetSource(packet.RawData, 9, packet.Size); netEvent.DisconnectReason = DisconnectReason.RemoteConnectionClose; peers.RemovePeer(netPeer); break; //To reconnect peer } else { //Just answer accept netPeer.ProcessPacket(packet); return; } default: netPeer.ProcessPacket(packet); return; } } //Unacked shutdown if (packet.Property == PacketProperty.Disconnect) { SendRaw(new[] { (byte)PacketProperty.ShutdownOk }, 0, 1, remoteEndPoint); return; } if (packet.Property == PacketProperty.ConnectRequest && packet.Size >= 12) { NetUtils.DebugWrite("[NM] Received ConnectionRequest"); if (isPeerConnecting) { NetUtils.DebugWrite("[NM] Peer already connecting"); return; } if (GetPeersCount(ConnectionState.Connected | ConnectionState.InProgress) < maxConnections) { int protoId = BitConverter.ToInt32(packet.RawData, 1); if (protoId != NetConstants.ProtocolId) { NetUtils.DebugWrite(ConsoleColor.Cyan, "[NM] Peer connect reject. Invalid protocol ID: " + protoId); return; } //Getting new id for peer long connectionId = BitConverter.ToInt64(packet.RawData, 5); // Read data and create request var reader = new NetDataReader(null, 0, 0); if (packet.Size > 12) { reader.SetSource(packet.RawData, 13, packet.Size); } NetUtils.DebugWrite("[NM] Creating request event: " + connectionId); lock (connectingPeers) { connectingPeers.Add(remoteEndPoint); } var netEvent = CreateEvent(NetEventType.ConnectionRequest); netEvent.ConnectionRequest = new ConnectionRequest(connectionId, remoteEndPoint, reader, OnConnectionSolved); EnqueueEvent(netEvent); } } }
/// <summary> /// Connect to remote host /// </summary> /// <param name="address">Server IP or hostname</param> /// <param name="port">Server Port</param> /// <param name="key">Connection key</param> /// <returns>Null if connections limit reached, New NetPeer if new connection, Old NetPeer if already connected</returns> /// <exception cref="InvalidOperationException">Manager is not running. Call <see cref="Start()"/></exception> public NetPeer Connect(string address, int port, string key) { var ep = NetUtils.MakeEndPoint(address, port); return(Connect(ep, key)); }
internal void Update(int deltaTime) { _timeSinceLastPacket += deltaTime; switch (_connectionState) { case ConnectionState.Connected: if (_timeSinceLastPacket > _netManager.DisconnectTimeout) { NetUtils.DebugWrite( "[UPDATE] Disconnect by timeout: {0} > {1}", _timeSinceLastPacket, _netManager.DisconnectTimeout); _netManager.DisconnectPeer(this, DisconnectReason.Timeout, 0, true, null, 0, 0); return; } break; case ConnectionState.ShutdownRequested: if (_timeSinceLastPacket > _netManager.DisconnectTimeout) { _connectionState = ConnectionState.Disconnected; } else { _netManager.SendRaw(_shutdownPacket, _remoteEndPoint); } return; case ConnectionState.InProgress: _connectTimer += deltaTime; if (_connectTimer > _netManager.ReconnectDelay) { _connectTimer = 0; _connectAttempts++; if (_connectAttempts > _netManager.MaxConnectAttempts) { _netManager.DisconnectPeer(this, DisconnectReason.ConnectionFailed, 0, true, null, 0, 0); return; } //else send connect again _netManager.SendRaw(_connectRequestPacket, _remoteEndPoint); } return; case ConnectionState.Disconnected: return; } //Send ping _pingSendTimer += deltaTime; if (_pingSendTimer >= _netManager.PingInterval) { NetUtils.DebugWrite("[PP] Send ping..."); //reset timer _pingSendTimer = 0; //send ping _pingPacket.Sequence = _pingSequence; SendRawData(_pingPacket); //reset timer _pingTimeStart = DateTime.UtcNow; } //RTT - round trip time _rttResetTimer += deltaTime; if (_rttResetTimer >= RttResetDelay) { _rttResetTimer = 0; //Rtt update _rtt = _avgRtt; _ping = _avgRtt; _netManager.ConnectionLatencyUpdated(this, _ping); _rttCount = 1; } //MTU - Maximum transmission unit if (!_finishMtu) { _mtuCheckTimer += deltaTime; if (_mtuCheckTimer >= MtuCheckDelay) { _mtuCheckTimer = 0; _mtuCheckAttempts++; if (_mtuCheckAttempts >= MaxMtuCheckAttempts) { _finishMtu = true; } else { lock (_mtuMutex) { //Send increased packet if (_mtuIdx < NetConstants.PossibleMtu.Length - 1) { int newMtu = NetConstants.PossibleMtu[_mtuIdx + 1] - NetConstants.HeaderSize; var p = _packetPool.GetWithProperty(PacketProperty.MtuCheck, newMtu); p.RawData[1] = (byte)(_mtuIdx + 1); //Must check result for MTU fix if (!_netManager.SendRawAndRecycle(p, _remoteEndPoint)) { _finishMtu = true; } } } } } } //MTU - end //Pending send Flush(); }
//Process incoming packet internal void ProcessPacket(NetPacket packet) { _timeSinceLastPacket = 0; NetUtils.DebugWrite("[RR]PacketProperty: {0}", packet.Property); switch (packet.Property) { case PacketProperty.ConnectRequest: //response with connect SendConnectAccept(); _packetPool.Recycle(packet); break; case PacketProperty.Merged: int pos = NetConstants.HeaderSize; while (pos < packet.Size) { ushort size = BitConverter.ToUInt16(packet.RawData, pos); pos += 2; NetPacket mergedPacket = _packetPool.GetPacket(size, false); if (!mergedPacket.FromBytes(packet.RawData, pos, size)) { _packetPool.Recycle(packet); break; } pos += size; ProcessPacket(mergedPacket); } break; //If we get ping, send pong case PacketProperty.Ping: if (NetUtils.RelativeSequenceNumber(packet.Sequence, _remotePingSequence) < 0) { _packetPool.Recycle(packet); break; } NetUtils.DebugWrite("[PP]Ping receive, send pong"); _remotePingSequence = packet.Sequence; _packetPool.Recycle(packet); //send _pongPacket.Sequence = _remotePingSequence; _netManager.SendRaw(_pongPacket, _remoteEndPoint); break; //If we get pong, calculate ping time and rtt case PacketProperty.Pong: if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pingSequence) < 0) { _packetPool.Recycle(packet); break; } _pingSequence = packet.Sequence; int rtt = (int)(DateTime.UtcNow - _pingTimeStart).TotalMilliseconds; UpdateRoundTripTime(rtt); NetUtils.DebugWrite("[PP]Ping: {0}", rtt); _packetPool.Recycle(packet); break; //Process ack case PacketProperty.AckReliable: _reliableUnorderedChannel.ProcessAck(packet); _packetPool.Recycle(packet); break; case PacketProperty.AckReliableOrdered: _reliableOrderedChannel.ProcessAck(packet); _packetPool.Recycle(packet); break; //Process in order packets case PacketProperty.Sequenced: _sequencedChannel.ProcessPacket(packet); break; case PacketProperty.ReliableUnordered: _reliableUnorderedChannel.ProcessPacket(packet); break; case PacketProperty.ReliableOrdered: _reliableOrderedChannel.ProcessPacket(packet); break; case PacketProperty.ReliableSequenced: _reliableSequencedChannel.ProcessPacket(packet); break; //Simple packet without acks case PacketProperty.Unreliable: AddIncomingPacket(packet); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; case PacketProperty.ShutdownOk: case PacketProperty.Disconnect: _connectionState = ConnectionState.Disconnected; _packetPool.Recycle(packet); break; default: NetUtils.DebugWriteError("Error! Unexpected packet type: " + packet.Property); break; } }
internal void AddIncomingPacket(NetPacket p) { if (p.IsFragmented) { NetUtils.DebugWrite("Fragment. Id: {0}, Part: {1}, Total: {2}", p.FragmentId, p.FragmentPart, p.FragmentsTotal); //Get needed array from dictionary ushort packetFragId = p.FragmentId; IncomingFragments incomingFragments; if (!_holdedFragments.TryGetValue(packetFragId, out incomingFragments)) { incomingFragments = new IncomingFragments { Fragments = new NetPacket[p.FragmentsTotal] }; _holdedFragments.Add(packetFragId, incomingFragments); } //Cache var fragments = incomingFragments.Fragments; //Error check if (p.FragmentPart >= fragments.Length || fragments[p.FragmentPart] != null) { _packetPool.Recycle(p); NetUtils.DebugWriteError("Invalid fragment packet"); return; } //Fill array fragments[p.FragmentPart] = p; //Increase received fragments count incomingFragments.ReceivedCount++; //Increase total size int dataOffset = p.GetHeaderSize() + NetConstants.FragmentHeaderSize; incomingFragments.TotalSize += p.Size - dataOffset; //Check for finish if (incomingFragments.ReceivedCount != fragments.Length) { return; } NetUtils.DebugWrite("Received all fragments!"); NetPacket resultingPacket = _packetPool.GetWithProperty(p.Property, incomingFragments.TotalSize); int resultingPacketOffset = resultingPacket.GetHeaderSize(); int firstFragmentSize = fragments[0].Size - dataOffset; for (int i = 0; i < incomingFragments.ReceivedCount; i++) { //Create resulting big packet int fragmentSize = fragments[i].Size - dataOffset; Buffer.BlockCopy( fragments[i].RawData, dataOffset, resultingPacket.RawData, resultingPacketOffset + firstFragmentSize * i, fragmentSize); //Free memory _packetPool.Recycle(fragments[i]); fragments[i] = null; } //Send to process _netManager.ReceiveFromPeer(resultingPacket, _remoteEndPoint); //Clear memory _packetPool.Recycle(resultingPacket); _holdedFragments.Remove(packetFragId); } else //Just simple packet { _netManager.ReceiveFromPeer(p, _remoteEndPoint); _packetPool.Recycle(p); } }
/// <summary> /// Send data to peer /// </summary> /// <param name="data">Data</param> /// <param name="start">Start of data</param> /// <param name="length">Length of data</param> /// <param name="options">Send options (reliable, unreliable, etc.)</param> /// <exception cref="TooBigPacketException"> /// If size exceeds maximum limit:<para/> /// MTU - headerSize bytes for Unreliable<para/> /// Fragment count exceeded ushort.MaxValue<para/> /// </exception> public void Send(byte[] data, int start, int length, DeliveryMethod options) { if (_connectionState == ConnectionState.ShutdownRequested || _connectionState == ConnectionState.Disconnected) { return; } //Prepare PacketProperty property = SendOptionsToProperty(options); NetUtils.DebugWrite("[RS]Packet: " + property); //Select channel BaseChannel channel; switch (property) { case PacketProperty.ReliableUnordered: channel = _reliableUnorderedChannel; break; case PacketProperty.Sequenced: channel = _sequencedChannel; break; case PacketProperty.ReliableOrdered: channel = _reliableOrderedChannel; break; case PacketProperty.Unreliable: channel = _unreliableChannel; break; case PacketProperty.ReliableSequenced: channel = _reliableSequencedChannel; break; default: throw new InvalidPacketException("Unknown packet property: " + property); } //Check fragmentation int headerSize = NetPacket.GetHeaderSize(property); //Save mtu for multithread int mtu = _mtu; if (length + headerSize > mtu) { if (options == DeliveryMethod.Sequenced || options == DeliveryMethod.Unreliable) { throw new TooBigPacketException("Unreliable packet size exceeded maximum of " + (_mtu - headerSize) + " bytes"); } int packetFullSize = mtu - headerSize; int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize; int fullPacketsCount = length / packetDataSize; int lastPacketSize = length % packetDataSize; int totalPackets = fullPacketsCount + (lastPacketSize == 0 ? 0 : 1); NetUtils.DebugWrite("FragmentSend:\n" + " MTU: {0}\n" + " headerSize: {1}\n" + " packetFullSize: {2}\n" + " packetDataSize: {3}\n" + " fullPacketsCount: {4}\n" + " lastPacketSize: {5}\n" + " totalPackets: {6}", mtu, headerSize, packetFullSize, packetDataSize, fullPacketsCount, lastPacketSize, totalPackets); if (totalPackets > ushort.MaxValue) { throw new TooBigPacketException("Data was split in " + totalPackets + " fragments, which exceeds " + ushort.MaxValue); } int dataOffset = headerSize + NetConstants.FragmentHeaderSize; lock (_sendLock) { for (ushort i = 0; i < fullPacketsCount; i++) { NetPacket p = _packetPool.GetWithProperty(property, packetFullSize); p.FragmentId = _fragmentId; p.FragmentPart = i; p.FragmentsTotal = (ushort)totalPackets; p.IsFragmented = true; Buffer.BlockCopy(data, i * packetDataSize, p.RawData, dataOffset, packetDataSize); channel.AddToQueue(p); } if (lastPacketSize > 0) { NetPacket p = _packetPool.GetWithProperty(property, lastPacketSize + NetConstants.FragmentHeaderSize); p.FragmentId = _fragmentId; p.FragmentPart = (ushort)fullPacketsCount; //last p.FragmentsTotal = (ushort)totalPackets; p.IsFragmented = true; Buffer.BlockCopy(data, fullPacketsCount * packetDataSize, p.RawData, dataOffset, lastPacketSize); channel.AddToQueue(p); } _fragmentId++; } return; } //Else just send NetPacket packet = _packetPool.GetWithData(property, data, start, length); channel.AddToQueue(packet); }