//Process incoming packet public override void ProcessPacket(NetPacket packet) { int seq = packet.Sequence; if (seq >= NetConstants.MaxSequence) { NetDebug.Write("[RR]Bad sequence"); return; } int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart); int relateSeq = NetUtils.RelativeSequenceNumber(seq, _remoteSequence); if (relateSeq > _windowSize) { NetDebug.Write("[RR]Bad sequence"); return; } //Drop bad packets if (relate < 0) { //Too old packet doesn't ack NetDebug.Write("[RR]ReliableInOrder too old"); return; } if (relate >= _windowSize * 2) { //Some very new packet NetDebug.Write("[RR]ReliableInOrder too new"); return; } //If very new - move window int ackIdx; int ackByte; int ackBit; Monitor.Enter(_outgoingAcks); if (relate >= _windowSize) { //New window position int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence; _outgoingAcks.Sequence = (ushort)newWindowStart; //Clean old data while (_remoteWindowStart != newWindowStart) { ackIdx = _remoteWindowStart % _windowSize; ackByte = NetConstants.SequencedHeaderSize + ackIdx / BitsInByte; ackBit = ackIdx % BitsInByte; _outgoingAcks.RawData[ackByte] &= (byte)~(1 << ackBit); _remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence; } } //Final stage - process valid packet //trigger acks send _mustSendAcks = true; ackIdx = seq % _windowSize; ackByte = NetConstants.SequencedHeaderSize + ackIdx / BitsInByte; ackBit = ackIdx % BitsInByte; if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0) { NetDebug.Write("[RR]ReliableInOrder duplicate"); Monitor.Exit(_outgoingAcks); return; } //save ack _outgoingAcks.RawData[ackByte] |= (byte)(1 << ackBit); Monitor.Exit(_outgoingAcks); //detailed check if (seq == _remoteSequence) { NetDebug.Write("[RR]ReliableInOrder packet succes"); Peer.AddIncomingPacket(packet); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; if (_ordered) { NetPacket p; while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null) { //process holded packet _receivedPackets[_remoteSequence % _windowSize] = null; Peer.AddIncomingPacket(p); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } else { while (_earlyReceived[_remoteSequence % _windowSize]) { //process early packet _earlyReceived[_remoteSequence % _windowSize] = false; _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } return; } //holded packet if (_ordered) { _receivedPackets[ackIdx] = packet; } else { _earlyReceived[ackIdx] = true; Peer.AddIncomingPacket(packet); } }
private void ReceiveLogic() { while (_running) { int errorCode = 0; //Receive some info byte[] reusableBuffer = null; int result = _socket.ReceiveFrom(ref reusableBuffer, ref _remoteEndPoint, ref errorCode); if (result > 0) { #if DEBUG bool receivePacket = true; if (SimulatePacketLoss && _randomGenerator.Next(100 / SimulationPacketLossChance) == 0) { receivePacket = false; } else if (SimulateLatency) { int latency = _randomGenerator.Next(SimulationMaxLatency); if (latency > 5) { byte[] holdedData = new byte[result]; Buffer.BlockCopy(reusableBuffer, 0, holdedData, 0, result); _pingSimulationList.AddFirst(new IncomingData { Data = holdedData, EndPoint = _remoteEndPoint, TimeWhenGet = DateTime.UtcNow.AddMilliseconds(latency) }); receivePacket = false; } } if (receivePacket) //DataReceived #endif //ProcessEvents DataReceived(reusableBuffer, result, _remoteEndPoint); } else if (result < 0) { //10054 - remote close (not error) //10040 - message too long (impossible now) if (errorCode != 10054) { NetUtils.DebugWrite(ConsoleColor.Red, "(NB)Socket error!"); ProcessError("Receive socket error: " + errorCode); Stop(); return; } } #if DEBUG if (SimulateLatency) { var node = _pingSimulationList.First; var time = DateTime.UtcNow; while (node != null) { var incomingData = node.Value; if (incomingData.TimeWhenGet <= time) { DataReceived(incomingData.Data, incomingData.Data.Length, incomingData.EndPoint); var nodeToRemove = node; node = node.Next; _pingSimulationList.Remove(nodeToRemove); } else { node = node.Next; } } } #endif } }
//ProcessAck in packet private void ProcessAck(NetPacket packet) { if (packet.Size != _outgoingAcks.Size) { NetDebug.Write("[PA]Invalid acks packet size"); return; } var ackWindowStart = packet.Sequence; var windowRel = NetUtils.RelativeSequenceNumber(_localWindowStart, ackWindowStart); if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0) { NetDebug.Write("[PA]Bad window start"); return; } //check relevance if (windowRel >= _windowSize) { NetDebug.Write("[PA]Old acks"); return; } var acksData = packet.RawData; lock (_pendingPackets) { for (var pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) { var rel = NetUtils.RelativeSequenceNumber(pendingSeq, ackWindowStart); if (rel >= _windowSize) { NetDebug.Write("[PA]REL: " + rel); break; } var pendingIdx = pendingSeq % _windowSize; var currentByte = NetConstants.ChanneledHeaderSize + pendingIdx / BitsInByte; var currentBit = pendingIdx % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { #if DEBUG Peer.Statistics.PacketLoss++; #else if (Peer.NetManager.EnableStatistics) { Peer.Statistics.PacketLoss++; } #endif //Skip false ack NetDebug.Write("[PA]False ack: {0}", pendingSeq); continue; } if (pendingSeq == _localWindowStart) { //Move window _localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; } //clear packet if (_pendingPackets[pendingIdx].Clear(Peer)) { NetDebug.Write("[PA]Removing reliableInOrder ack: {0} - true", pendingSeq); } } } }
private void ReceiveLogic(object state) { Socket socket = (Socket)state; EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0); NetEndPoint bufferNetEndPoint = new NetEndPoint((IPEndPoint)bufferEndPoint); #if WIN32 && UNSAFE int saddrSize = 32; byte[] prevAddress = new byte[saddrSize]; byte[] socketAddress = new byte[saddrSize]; byte[] addrBuffer = new byte[16]; //IPAddress.IPv6AddressBytes #endif byte[] receiveBuffer = new byte[NetConstants.PacketSizeLimit]; while (_running) { int result; //Reading data try { #if WIN32 && UNSAFE result = recvfrom( socket.Handle, receiveBuffer, receiveBuffer.Length, SocketFlags.None, socketAddress, ref saddrSize); if ((SocketError)result == SocketError.SocketError) { throw new SocketException(Marshal.GetLastWin32Error()); } bool recreate = false; for (int i = 0; i < saddrSize; i++) { if (socketAddress[i] != prevAddress[i]) { prevAddress[i] = socketAddress[i]; recreate = true; } } if (recreate) { if (socket.AddressFamily == AddressFamily.InterNetwork) { int port = (socketAddress[2] << 8 & 0xFF00) | socketAddress[3]; long address = ( (socketAddress[4] & 0x000000FF) | (socketAddress[5] << 8 & 0x0000FF00) | (socketAddress[6] << 16 & 0x00FF0000) | (socketAddress[7] << 24) ) & 0x00000000FFFFFFFF; bufferNetEndPoint = new NetEndPoint(new IPEndPoint(address, port)); } else { for (int i = 0; i < addrBuffer.Length; i++) { addrBuffer[i] = socketAddress[i + 8]; } int port = (socketAddress[2] << 8 & 0xFF00) | (socketAddress[3]); long scope = (socketAddress[27] << 24) + (socketAddress[26] << 16) + (socketAddress[25] << 8) + (socketAddress[24]); bufferNetEndPoint = new NetEndPoint(new IPEndPoint(new IPAddress(addrBuffer, scope), port)); } } #else result = socket.ReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref bufferEndPoint); if (!bufferNetEndPoint.EndPoint.Equals(bufferEndPoint)) { bufferNetEndPoint = new NetEndPoint((IPEndPoint)bufferEndPoint); } #endif } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.ConnectionReset || ex.SocketErrorCode == SocketError.MessageSize || ex.SocketErrorCode == SocketError.Interrupted) { //10040 - message too long //10054 - remote close (not error) //Just UDP NetUtils.DebugWrite(ConsoleColor.DarkRed, "[R] Ingored error: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString()); continue; } NetUtils.DebugWriteError("[R]Error code: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString()); lock (_receiveLock) { _onMessageReceived(null, 0, (int)ex.SocketErrorCode, bufferNetEndPoint); } continue; } //All ok! NetUtils.DebugWrite(ConsoleColor.Blue, "[R]Received data from {0}, result: {1}", bufferNetEndPoint.ToString(), result); lock (_receiveLock) { _onMessageReceived(receiveBuffer, result, 0, bufferNetEndPoint); } } }
/// <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>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; } //get peer //Check normal packets NetPeer netPeer; //old packets protection bool peerFound = _peers.TryGetValue(remoteEndPoint, out netPeer); //Check unconnected switch (packet.Property) { case PacketProperty.DiscoveryRequest: if (!DiscoveryEnabled) { break; } CreateEvent(NetEvent.EType.DiscoveryRequest, remoteEndPoint: remoteEndPoint, readerSource: packet); break; case PacketProperty.DiscoveryResponse: CreateEvent(NetEvent.EType.DiscoveryResponse, remoteEndPoint: remoteEndPoint, readerSource: packet); break; case PacketProperty.UnconnectedMessage: if (!UnconnectedMessagesEnabled) { break; } CreateEvent(NetEvent.EType.ReceiveUnconnected, remoteEndPoint: remoteEndPoint, readerSource: packet); break; case PacketProperty.NatIntroduction: case PacketProperty.NatIntroductionRequest: case PacketProperty.NatPunchMessage: if (NatPunchEnabled) { NatPunchModule.ProcessMessage(remoteEndPoint, packet); } break; case PacketProperty.Disconnect: if (peerFound) { var disconnectResult = netPeer.ProcessDisconnect(packet); if (disconnectResult == DisconnectResult.None) { NetPacketPool.Recycle(packet); return; } if (disconnectResult == DisconnectResult.Disconnect) { _connectedPeersCount--; } CreateEvent( NetEvent.EType.Disconnect, netPeer, disconnectReason: disconnectResult == DisconnectResult.Disconnect ? DisconnectReason.RemoteConnectionClose : DisconnectReason.ConnectionRejected, readerSource: packet ); } else { NetPacketPool.Recycle(packet); } //Send shutdown SendRaw(new[] { (byte)PacketProperty.ShutdownOk }, 0, 1, remoteEndPoint); break; case PacketProperty.ConnectAccept: var connAccept = NetConnectAcceptPacket.FromData(packet); if (connAccept != null && peerFound && netPeer.ProcessConnectAccept(connAccept)) { CreateEvent(NetEvent.EType.Connect, netPeer); } break; case PacketProperty.ConnectRequest: var connRequest = NetConnectRequestPacket.FromData(packet); if (connRequest != null) { ProcessConnectRequest(remoteEndPoint, netPeer, connRequest); } break; default: if (peerFound) { netPeer.ProcessPacket(packet); } break; } }
//Process incoming packet internal void ProcessPacket(NetPacket packet) { _timeSinceLastPacket = 0; if (packet.ConnectionNumber != _connectNum && packet.Property != PacketProperty.ShutdownOk) //withou connectionNum { NetUtils.DebugWrite(ConsoleColor.Red, "[RR]Old packet"); _packetPool.Recycle(packet); return; } NetUtils.DebugWrite("[RR]PacketProperty: {0}", packet.Property); switch (packet.Property) { 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; case PacketProperty.AckReliableSequenced: _reliableSequencedChannel.ProcessAck(packet); _packetPool.Recycle(packet); break; //Simple packet without acks case PacketProperty.Unreliable: AddIncomingPacket(packet); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; case PacketProperty.ShutdownOk: if (_connectionState == ConnectionState.ShutdownRequested) { _connectionState = ConnectionState.Disconnected; } _packetPool.Recycle(packet); break; default: NetUtils.DebugWriteError("Error! Unexpected packet type: " + packet.Property); break; } }
//Process incoming packet internal void ProcessPacket(NetPacket packet) { //not initialized if (_connectionState == ConnectionState.Outgoing || _connectionState == ConnectionState.Disconnected) { _packetPool.Recycle(packet); return; } if (packet.Property == PacketProperty.ShutdownOk) { if (_connectionState == ConnectionState.ShutdownRequested) { _connectionState = ConnectionState.Disconnected; } _packetPool.Recycle(packet); return; } if (packet.ConnectionNumber != _connectNum) { NetDebug.Write(NetLogLevel.Trace, "[RR]Old packet"); _packetPool.Recycle(packet); return; } Interlocked.Exchange(ref _timeSinceLastPacket, 0); NetDebug.Write("[RR]PacketProperty: {0}", packet.Property); switch (packet.Property) { case PacketProperty.Merged: int pos = NetConstants.HeaderSize; while (pos < packet.Size) { ushort size = BitConverter.ToUInt16(packet.RawData, pos); pos += 2; if (packet.RawData.Length - pos < size) { break; } NetPacket mergedPacket = _packetPool.GetPacket(size); Buffer.BlockCopy(packet.RawData, pos, mergedPacket.RawData, 0, size); mergedPacket.Size = size; if (!mergedPacket.Verify()) { break; } pos += size; ProcessPacket(mergedPacket); } _packetPool.Recycle(packet); break; //If we get ping, send pong case PacketProperty.Ping: if (NetUtils.RelativeSequenceNumber(packet.Sequence, _pongPacket.Sequence) > 0) { NetDebug.Write("[PP]Ping receive, send pong"); FastBitConverter.GetBytes(_pongPacket.RawData, 3, DateTime.UtcNow.Ticks); _pongPacket.Sequence = packet.Sequence; NetManager.SendRaw(_pongPacket, EndPoint); } _packetPool.Recycle(packet); break; //If we get pong, calculate ping time and rtt case PacketProperty.Pong: if (packet.Sequence == _pingPacket.Sequence) { _pingTimer.Stop(); int elapsedMs = (int)_pingTimer.ElapsedMilliseconds; _remoteDelta = BitConverter.ToInt64(packet.RawData, 3) + (elapsedMs * TimeSpan.TicksPerMillisecond) / 2 - DateTime.UtcNow.Ticks; UpdateRoundTripTime(elapsedMs); NetManager.ConnectionLatencyUpdated(this, elapsedMs / 2); NetDebug.Write("[PP]Ping: {0} - {1} - {2}", packet.Sequence, elapsedMs, _remoteDelta); } _packetPool.Recycle(packet); break; case PacketProperty.Ack: case PacketProperty.Channeled: if (packet.ChannelId > _channels.Length) { _packetPool.Recycle(packet); break; } var channel = _channels[packet.ChannelId] ?? (packet.Property == PacketProperty.Ack ? null : CreateChannel(packet.ChannelId)); if (channel != null) { if (!channel.ProcessPacket(packet)) { _packetPool.Recycle(packet); } } break; //Simple packet without acks case PacketProperty.Unreliable: NetManager.CreateReceiveEvent(packet, DeliveryMethod.Unreliable, NetConstants.HeaderSize, this); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; default: NetDebug.WriteError("Error! Unexpected packet type: " + packet.Property); break; } }
//ProcessAck in packet public void ProcessAck(NetPacket packet) { int validPacketSize = (_windowSize - 1) / BitsInByte + 1 + NetConstants.SequencedHeaderSize; if (packet.Size != validPacketSize) { NetUtils.DebugWrite("[PA]Invalid acks packet size"); return; } ushort ackWindowStart = packet.Sequence; if (ackWindowStart > NetConstants.MaxSequence) { NetUtils.DebugWrite("[PA]Bad window start"); return; } //check relevance if (NetUtils.RelativeSequenceNumber(ackWindowStart, _localWindowStart) <= -_windowSize) { NetUtils.DebugWrite("[PA]Old acks"); return; } byte[] acksData = packet.RawData; NetUtils.DebugWrite("[PA]AcksStart: {0}", ackWindowStart); int startByte = NetConstants.SequencedHeaderSize; Monitor.Enter(_pendingPackets); PendingPacket pendingPacket = _headPendingPacket; PendingPacket prevPacket = null; while (pendingPacket != null) { int seq = pendingPacket.Packet.Sequence; int rel = NetUtils.RelativeSequenceNumber(seq, ackWindowStart); if (rel < 0) { prevPacket = pendingPacket; pendingPacket = pendingPacket.Next; continue; } if (rel >= _windowSize) { break; } int idx = (ackWindowStart + seq) % _windowSize; int currentByte = startByte + idx / BitsInByte; int currentBit = idx % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { #if STATS_ENABLED || DEBUG _peer.Statistics.PacketLoss++; #endif //Skip false ack prevPacket = pendingPacket; pendingPacket = pendingPacket.Next; continue; } if (seq == _localWindowStart) { //Move window _headPendingPacket = _headPendingPacket.Next; _localWindowStart = _headPendingPacket != null ? _headPendingPacket.Packet.Sequence : (_localWindowStart + 1) % NetConstants.MaxSequence; } if (pendingPacket == _tailPendingPacket) { _tailPendingPacket = prevPacket; } var packetToClear = pendingPacket; //move forward pendingPacket = pendingPacket.Next; if (prevPacket != null) { prevPacket.Next = pendingPacket; } //clear acked packet _peer.Recycle(packetToClear.Packet); packetToClear.Clear(); NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - true", seq); } Monitor.Exit(_pendingPackets); }
/// <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 || options == DeliveryMethod.ReliableSequenced) { 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.MarkFragmented(); 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.MarkFragmented(); 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); }
public void SendNextPackets() { //check sending acks if (_mustSendAcks) { _mustSendAcks = false; NetUtils.DebugWrite("[RR]SendAcks"); Monitor.Enter(_outgoingAcks); _peer.SendRawData(_outgoingAcks); Monitor.Exit(_outgoingAcks); } long currentTime = DateTime.UtcNow.Ticks; Monitor.Enter(_pendingPackets); //get packets from queue Monitor.Enter(_outgoingPackets); while (_outgoingPackets.Count > 0) { int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart); if (relate < _windowSize) { PendingPacket pendingPacket = _pendingPackets[_localSeqence % _windowSize]; pendingPacket.Packet = _outgoingPackets.Dequeue(); pendingPacket.Packet.Sequence = (ushort)_localSeqence; if (_headPendingPacket == null) { _headPendingPacket = pendingPacket; } else { _tailPendingPacket.Next = pendingPacket; } _tailPendingPacket = pendingPacket; _localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence; } else //Queue filled { break; } } Monitor.Exit(_outgoingPackets); //if no pending packets return if (_headPendingPacket == null) { Monitor.Exit(_pendingPackets); return; } //send double resendDelay = _peer.ResendDelay; PendingPacket currentPacket = _headPendingPacket; do { if (currentPacket.Sended) //check send time { double packetHoldTime = currentTime - currentPacket.TimeStamp; if (packetHoldTime < resendDelay * TimeSpan.TicksPerMillisecond) { continue; } NetUtils.DebugWrite("[RC]Resend: {0} > {1}", (int)packetHoldTime, resendDelay); } currentPacket.TimeStamp = currentTime; currentPacket.Sended = true; _peer.SendRawData(currentPacket.Packet); } while ((currentPacket = currentPacket.Next) != null); Monitor.Exit(_pendingPackets); }
public void SendNatIntroduceRequest(string host, int port, string additionalInfo) { SendNatIntroduceRequest(NetUtils.MakeEndPoint(host, port), additionalInfo); }
public int SendTo(byte[] data, int offset, int size, NetEndPoint remoteEndPoint, ref int errorCode) { try { var socket = _udpSocketv4; if (remoteEndPoint.EndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support) { socket = _udpSocketv6; } int result; #if WIN32 && UNSAFE var handle = socket.Handle; IntPtr[] fileDescriptorSet = { (IntPtr)1, handle }; int socketCount = select(0, null, fileDescriptorSet, null, ref SendPollTime); if ((SocketError)socketCount == SocketError.SocketError) { throw new SocketException(Marshal.GetLastWin32Error()); } if ((int)fileDescriptorSet[0] == 0 || fileDescriptorSet[1] != handle) { return(0); } unsafe { fixed(byte *pinnedBuffer = data) { result = sendto( handle, pinnedBuffer + offset, size, SocketFlags.None, remoteEndPoint.SocketAddr, remoteEndPoint.SocketAddr.Length); } } if ((SocketError)result == SocketError.SocketError) { throw new SocketException(Marshal.GetLastWin32Error()); } #else if (!socket.Poll(SocketSendPollTime, SelectMode.SelectWrite)) { return(-1); } result = socket.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint.EndPoint); #endif NetUtils.DebugWrite(ConsoleColor.Blue, "[S]Send packet to {0}, result: {1}", remoteEndPoint.EndPoint, result); return(result); } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable) { return(0); } if (ex.SocketErrorCode != SocketError.MessageSize) { NetUtils.DebugWriteError("[S]" + ex); } errorCode = (int)ex.SocketErrorCode; return(-1); } catch (Exception ex) { NetUtils.DebugWriteError("[S]" + ex); return(-1); } }
internal void Update(int deltaTime) { if (_connectionState == ConnectionState.Disconnected) { return; } _timeSinceLastPacket += deltaTime; if (_connectionState == ConnectionState.InProgress) { _connectTimer += deltaTime; if (_connectTimer > _peerListener.ReconnectDelay) { _connectTimer = 0; _connectAttempts++; if (_connectAttempts > _peerListener.MaxConnectAttempts) { _connectionState = ConnectionState.Disconnected; return; } //else send connect again SendConnectRequest(); } return; } //Get current flow mode int maxSendPacketsCount = _peerListener.GetPacketsPerSecond(_currentFlowMode); int currentMaxSend; if (maxSendPacketsCount > 0) { int availableSendPacketsCount = maxSendPacketsCount - _sendedPacketsCount; currentMaxSend = Math.Min(availableSendPacketsCount, (maxSendPacketsCount * deltaTime) / NetConstants.FlowUpdateTime); } else { currentMaxSend = int.MaxValue; } //DebugWrite("[UPDATE]Delta: {0}ms, MaxSend: {1}", deltaTime, currentMaxSend); //Pending acks _reliableOrderedChannel.SendAcks(); _reliableUnorderedChannel.SendAcks(); //ResetFlowTimer _flowTimer += deltaTime; if (_flowTimer >= NetConstants.FlowUpdateTime) { NetUtils.DebugWrite("[UPDATE]Reset flow timer, _sendedPackets - {0}", _sendedPacketsCount); _sendedPacketsCount = 0; _flowTimer = 0; } //Send ping _pingSendTimer += deltaTime; if (_pingSendTimer >= _peerListener.PingInterval) { NetUtils.DebugWrite("[PP] Send ping..."); //reset timer _pingSendTimer = 0; //send ping CreateAndSend(PacketProperty.Ping, _pingSequence); //reset timer _pingTimeStart = DateTime.UtcNow; } //RTT - round trip time _rttResetTimer += deltaTime; if (_rttResetTimer >= RttResetDelay) { _rttResetTimer = 0; //Rtt update _rtt = _avgRtt; _ping = _avgRtt; _peerListener.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.Get(PacketProperty.MtuCheck, newMtu); p.RawData[1] = (byte)(_mtuIdx + 1); SendPacket(p); } } } } } //MTU - end //Pending send lock (_flushLock) { SendQueuedPackets(currentMaxSend); } }
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: case ConnectionState.Incoming: 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(); }
public bool Bind(int port) { _udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _udpSocketv4.Blocking = false; _udpSocketv4.ReceiveBufferSize = NetConstants.SocketBufferSize; _udpSocketv4.SendBufferSize = NetConstants.SocketBufferSize; _udpSocketv4.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, NetConstants.SocketTTL); #if !NETCORE _udpSocketv4.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.DontFragment, true); #endif try { _udpSocketv4.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); } catch (SocketException e) { NetUtils.DebugWriteError("Broadcast error: {0}", e.ToString()); } if (!BindSocket(_udpSocketv4, new IPEndPoint(IPAddress.Any, port))) { return(false); } _localEndPoint = new NetEndPoint((IPEndPoint)_udpSocketv4.LocalEndPoint); _running = true; _threadv4 = new Thread(ReceiveLogic); _threadv4.Name = "SocketThreadv4(" + port + ")"; _threadv4.IsBackground = true; _threadv4.Start(_udpSocketv4); //Check IPv6 support if (!IPv6Support) { return(true); } //Use one port for two sockets port = _localEndPoint.Port; _udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); _udpSocketv6.Blocking = false; _udpSocketv6.ReceiveBufferSize = NetConstants.SocketBufferSize; _udpSocketv6.SendBufferSize = NetConstants.SocketBufferSize; if (BindSocket(_udpSocketv6, new IPEndPoint(IPAddress.IPv6Any, port))) { _localEndPoint = new NetEndPoint((IPEndPoint)_udpSocketv6.LocalEndPoint); try { _udpSocketv6.SetSocketOption( SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(MulticastAddressV6)); } catch { // Unity3d throws exception - ignored } _threadv6 = new Thread(ReceiveLogic); _threadv6.Name = "SocketThreadv6(" + port + ")"; _threadv6.IsBackground = true; _threadv6.Start(_udpSocketv6); } return(true); }
/// <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); int headerSize = NetPacket.GetHeaderSize(property); int mtu = _mtu; //Check fragmentation 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.Get(property, packetFullSize); p.FragmentId = _fragmentId; p.FragmentPart = i; p.FragmentsTotal = (ushort)totalPackets; p.IsFragmented = true; Buffer.BlockCopy(data, i * packetDataSize, p.RawData, dataOffset, packetDataSize); SendPacket(p); } if (lastPacketSize > 0) { NetPacket p = _packetPool.Get(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); SendPacket(p); } _fragmentId++; } return; } //Else just send NetPacket packet = _packetPool.GetWithData(property, data, start, length); SendPacket(packet); }
/// <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>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) { if (_connectionState == ConnectionState.Connected && _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; } if (_connectionState == ConnectionState.ShutdownRequested) { if (_timeSinceLastPacket > _netManager.DisconnectTimeout) { _connectionState = ConnectionState.Disconnected; } else { _netManager.SendRaw(_shutdownPacket.RawData, 0, _shutdownPacket.Size, _remoteEndPoint); } return; } if (_connectionState == ConnectionState.Disconnected) { return; } _timeSinceLastPacket += deltaTime; if (_connectionState == 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 SendConnectRequest(); } return; } //Send ping _pingSendTimer += deltaTime; if (_pingSendTimer >= _netManager.PingInterval) { NetUtils.DebugWrite("[PP] Send ping..."); //reset timer _pingSendTimer = 0; //send ping CreateAndSend(PacketProperty.Ping, _pingSequence); //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; } //Pending send Flush(); }
private void ProcessConnectRequest( IPEndPoint remoteEndPoint, NetPeer netPeer, NetConnectRequestPacket connRequest) { byte connectionNumber = connRequest.ConnectionNumber; //if we have peer if (netPeer != null) { NetUtils.DebugWrite("ConnectRequest LastId: {0}, NewId: {1}, EP: {2}", netPeer.ConnectId, connRequest.ConnectionId, remoteEndPoint); var processResult = netPeer.ProcessConnectRequest(connRequest); switch (processResult) { case ConnectRequestResult.Reconnection: _connectedPeersCount--; CreateEvent(NetEvent.EType.Disconnect, netPeer, disconnectReason: DisconnectReason.RemoteConnectionClose); _peers.RemovePeer(netPeer); //go to new connection break; case ConnectRequestResult.NewConnection: _peers.RemovePeer(netPeer); //go to new connection break; case ConnectRequestResult.P2PConnection: CreateEvent( NetEvent.EType.ConnectionRequest, connectionRequest: new ConnectionRequest( netPeer.ConnectId, connectionNumber, ConnectionRequestType.PeerToPeer, connRequest.Data, netPeer, this) ); return; default: //no operations needed return; } //ConnectRequestResult.NewConnection //Set next connection number connectionNumber = (byte)((netPeer.ConnectionNum + 1) % NetConstants.MaxConnectionNumber); //To reconnect peer } else { NetUtils.DebugWrite("ConnectRequest Id: {0}, EP: {1}", connRequest.ConnectionId, remoteEndPoint); } //Add new peer and craete ConnectRequest event NetUtils.DebugWrite("[NM] Creating request event: " + connRequest.ConnectionId); netPeer = new NetPeer(this, remoteEndPoint); if (_peers.TryAdd(netPeer) == netPeer) { CreateEvent(NetEvent.EType.ConnectionRequest, connectionRequest: new ConnectionRequest( connRequest.ConnectionId, connectionNumber, ConnectionRequestType.Incoming, connRequest.Data, netPeer, this)); } }
protected override void ReceiveFromSocket(byte[] reusableBuffer, int count, NetEndPoint remoteEndPoint) { NetPacket packet; NetPeer netPeer; //Check peers if (_peers.TryGetValue(remoteEndPoint, out netPeer)) { packet = netPeer.GetPacketFromPool(init: false); //Bad packet check if (!packet.FromBytes(reusableBuffer, count)) { netPeer.Recycle(packet); return; } //Send if (packet.Property == PacketProperty.Disconnect) { if (BitConverter.ToUInt64(packet.RawData, 1) != _peerConnectionIds[remoteEndPoint]) { //Old or incorrect disconnect netPeer.Recycle(packet); return; } RemovePeer(netPeer); var netEvent = CreateEvent(NetEventType.Disconnect); netEvent.Peer = netPeer; netEvent.AdditionalInfo = "successfuly disconnected"; EnqueueEvent(netEvent); } else if (packet.Property == PacketProperty.ConnectRequest) //response with connect { ulong lastId = _peerConnectionIds[remoteEndPoint]; ulong newId = BitConverter.ToUInt64(packet.RawData, 1); if (newId > lastId) { _peerConnectionIds[remoteEndPoint] = newId; } NetUtils.DebugWrite(ConsoleColor.Cyan, "ConnectRequest LastId: {0}, NewId: {1}, EP: {2}", lastId, newId, remoteEndPoint); SendConnectAccept(netPeer, _peerConnectionIds[remoteEndPoint]); netPeer.Recycle(packet); } else //throw out garbage packets { netPeer.ProcessPacket(packet); } return; } //Else add new peer packet = new NetPacket(); if (!packet.FromBytes(reusableBuffer, count)) { //Bad packet return; } if (_peers.Count < _maxClients && packet.Property == PacketProperty.ConnectRequest) { string peerKey = Encoding.UTF8.GetString(packet.RawData, 9, packet.RawData.Length - 9); if (peerKey != _connectKey) { NetUtils.DebugWrite(ConsoleColor.Cyan, "[NS] Peer connect reject. Invalid key: " + peerKey); return; } //Getting new id for peer netPeer = CreatePeer(remoteEndPoint); //response with id ulong connectionId = BitConverter.ToUInt64(packet.RawData, 1); NetUtils.DebugWrite(ConsoleColor.Cyan, "[NS] Received peer connect request Id: {0}, EP: {1}", connectionId, remoteEndPoint); SendConnectAccept(netPeer, connectionId); //clean incoming packet netPeer.Recycle(packet); lock (_peers) { _peers.Add(remoteEndPoint, netPeer); _peerConnectionIds.Add(remoteEndPoint, connectionId); } var netEvent = CreateEvent(NetEventType.Connect); netEvent.Peer = netPeer; EnqueueEvent(netEvent); } }
//Process incoming packet internal void ProcessPacket(NetPacket packet) { //not initialized if (_connectionState == ConnectionState.Incoming) { _packetPool.Recycle(packet); return; } if (packet.ConnectionNumber != _connectNum && packet.Property != PacketProperty.ShutdownOk) //without connectionNum { NetDebug.Write(NetLogLevel.Trace, "[RR]Old packet"); _packetPool.Recycle(packet); return; } _timeSinceLastPacket = 0; NetDebug.Write("[RR]PacketProperty: {0}", packet.Property); switch (packet.Property) { 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, _pongPacket.Sequence) > 0) { NetDebug.Write("[PP]Ping receive, send pong"); FastBitConverter.GetBytes(_pongPacket.RawData, 3, DateTime.UtcNow.Ticks); _pongPacket.Sequence = packet.Sequence; _netManager.SendRaw(_pongPacket, _remoteEndPoint); } _packetPool.Recycle(packet); break; //If we get pong, calculate ping time and rtt case PacketProperty.Pong: if (packet.Sequence == _pingPacket.Sequence) { _pingTimer.Stop(); int elapsedMs = (int)_pingTimer.ElapsedMilliseconds; _remoteDelta = BitConverter.ToInt64(packet.RawData, 3) + (elapsedMs * TimeSpan.TicksPerMillisecond) / 2 - DateTime.UtcNow.Ticks; UpdateRoundTripTime(elapsedMs); _netManager.ConnectionLatencyUpdated(this, elapsedMs / 2); NetDebug.Write("[PP]Ping: {0} - {1} - {2}", packet.Sequence, elapsedMs, _remoteDelta); } _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; case PacketProperty.AckReliableSequenced: _reliableSequencedChannel.ProcessAck(packet); _packetPool.Recycle(packet); break; //Simple packet without acks case PacketProperty.Unreliable: AddIncomingPacket(packet); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; case PacketProperty.ShutdownOk: if (_connectionState == ConnectionState.ShutdownRequested) { _connectionState = ConnectionState.Disconnected; } _packetPool.Recycle(packet); break; default: NetDebug.WriteError("Error! Unexpected packet type: " + packet.Property); break; } }
/// <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> public void Send(byte[] data, int start, int length, SendOptions options) { //Prepare PacketProperty property = SendOptionsToProperty(options); int headerSize = NetPacket.GetHeaderSize(property); //Check fragmentation if (length + headerSize > _mtu) { if (options == SendOptions.Sequenced || options == SendOptions.Unreliable) { throw new Exception("Unreliable packet size > allowed (" + (_mtu - headerSize) + ")"); } 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 Exception("Too many fragments: " + totalPackets + " > " + ushort.MaxValue); } int dataOffset = headerSize + NetConstants.FragmentHeaderSize; for (ushort i = 0; i < fullPacketsCount; i++) { NetPacket p = _packetPool.Get(property, packetFullSize); p.FragmentId = _fragmentId; p.FragmentPart = i; p.FragmentsTotal = (ushort)totalPackets; p.IsFragmented = true; Buffer.BlockCopy(data, i * packetDataSize, p.RawData, dataOffset, packetDataSize); SendPacket(p); } if (lastPacketSize > 0) { NetPacket p = _packetPool.Get(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); SendPacket(p); } _fragmentId++; return; } //Else just send NetPacket packet = _packetPool.GetWithData(property, data, start, length); SendPacket(packet); }
public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool reuseAddress) { _udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _udpSocketv4.Blocking = true; _udpSocketv4.ReceiveBufferSize = NetConstants.SocketBufferSize; _udpSocketv4.SendBufferSize = NetConstants.SocketBufferSize; _udpSocketv4.Ttl = NetConstants.SocketTTL; if (reuseAddress) { _udpSocketv4.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); } #if !NETCORE _udpSocketv4.DontFragment = true; #endif try { _udpSocketv4.EnableBroadcast = true; } catch (SocketException e) { NetUtils.DebugWriteError("Broadcast error: {0}", e.ToString()); } if (!BindSocket(_udpSocketv4, new IPEndPoint(addressIPv4, port))) { return(false); } LocalPort = ((IPEndPoint)_udpSocketv4.LocalEndPoint).Port; _running = true; _threadv4 = new Thread(ReceiveLogic); _threadv4.Name = "SocketThreadv4(" + LocalPort + ")"; _threadv4.IsBackground = true; _threadv4.Start(_udpSocketv4); //Check IPv6 support if (!IPv6Support) { return(true); } _udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); _udpSocketv6.Blocking = true; _udpSocketv6.ReceiveBufferSize = NetConstants.SocketBufferSize; _udpSocketv6.SendBufferSize = NetConstants.SocketBufferSize; //_udpSocketv6.Ttl = NetConstants.SocketTTL; if (reuseAddress) { _udpSocketv6.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); } //Use one port for two sockets if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort))) { try { #if !ENABLE_IL2CPP _udpSocketv6.SetSocketOption( SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(MulticastAddressV6)); #endif } catch (Exception) { // Unity3d throws exception - ignored } _threadv6 = new Thread(ReceiveLogic); _threadv6.Name = "SocketThreadv6(" + LocalPort + ")"; _threadv6.IsBackground = true; _threadv6.Start(_udpSocketv6); } return(true); }
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.Get(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 _peerListener.ReceiveFromPeer(resultingPacket, _remoteEndPoint); //Clear memory _packetPool.Recycle(resultingPacket); _holdedFragments.Remove(packetFragId); } else //Just simple packet { _peerListener.ReceiveFromPeer(p, _remoteEndPoint); _packetPool.Recycle(p); } }
public override void SendNextPackets() { //check sending acks if (_mustSendAcks) { _mustSendAcks = false; NetDebug.Write("[RR]SendAcks"); Monitor.Enter(_outgoingAcks); Peer.SendUserData(_outgoingAcks); Monitor.Exit(_outgoingAcks); } long currentTime = DateTime.UtcNow.Ticks; Monitor.Enter(_pendingPackets); //get packets from queue Monitor.Enter(OutgoingQueue); while (OutgoingQueue.Count > 0) { int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart); if (relate < _windowSize) { PendingPacket pendingPacket = _pendingPackets[_localSeqence % _windowSize]; pendingPacket.Sended = false; pendingPacket.Packet = OutgoingQueue.Dequeue(); pendingPacket.Packet.Sequence = (ushort)_localSeqence; _localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence; } else //Queue filled { break; } } Monitor.Exit(OutgoingQueue); //send double resendDelay = Peer.ResendDelay; for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) { PendingPacket currentPacket = _pendingPackets[pendingSeq % _windowSize]; if (currentPacket.Packet == null) { continue; } if (currentPacket.Sended) //check send time { double packetHoldTime = currentTime - currentPacket.TimeStamp; if (packetHoldTime < resendDelay * TimeSpan.TicksPerMillisecond) { continue; } NetDebug.Write("[RC]Resend: {0} > {1}", (int)packetHoldTime, resendDelay); } currentPacket.TimeStamp = currentTime; currentPacket.Sended = true; Peer.SendUserData(currentPacket.Packet); } Monitor.Exit(_pendingPackets); }
//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 long newId = BitConverter.ToInt64(packet.RawData, NetConstants.RequestConnectIdIndex); if (newId > _connectId) { _connectId = newId; } NetUtils.DebugWrite("ConnectRequest LastId: {0}, NewId: {1}, EP: {2}", ConnectId, newId, _remoteEndPoint); 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.GetAndRead(packet.RawData, pos, size); if (mergedPacket == null) { _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 CreateAndSend(PacketProperty.Pong, _remotePingSequence); 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.Reliable: _reliableUnorderedChannel.ProcessPacket(packet); break; case PacketProperty.ReliableOrdered: _reliableOrderedChannel.ProcessPacket(packet); break; //Simple packet without acks case PacketProperty.Unreliable: AddIncomingPacket(packet); return; case PacketProperty.MtuCheck: case PacketProperty.MtuOk: ProcessMtuPacket(packet); break; default: NetUtils.DebugWriteError("Error! Unexpected packet type: " + packet.Property); break; } }
//ProcessAck in packet public void ProcessAck(NetPacket packet) { if (packet.Size != _outgoingAcks.Size) { NetDebug.Write("[PA]Invalid acks packet size"); return; } ushort ackWindowStart = packet.Sequence; int windowRel = NetUtils.RelativeSequenceNumber(_localWindowStart, ackWindowStart); if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0) { NetDebug.Write("[PA]Bad window start"); return; } //check relevance if (windowRel >= _windowSize) { NetDebug.Write("[PA]Old acks"); return; } byte[] acksData = packet.RawData; Monitor.Enter(_pendingPackets); for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) { int rel = NetUtils.RelativeSequenceNumber(pendingSeq, ackWindowStart); if (rel >= _windowSize) { NetDebug.Write("[PA]REL: " + rel); break; } int pendingIdx = pendingSeq % _windowSize; int currentByte = NetConstants.SequencedHeaderSize + pendingIdx / BitsInByte; int currentBit = pendingIdx % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { #if STATS_ENABLED || DEBUG Peer.Statistics.PacketLoss++; #endif //Skip false ack NetDebug.Write("[PA]False ack: {0}", pendingSeq); continue; } if (pendingSeq == _localWindowStart) { //Move window _localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; } //clear packet var pendingPacket = _pendingPackets[pendingIdx]; if (pendingPacket.Packet != null) { Peer.Recycle(pendingPacket.Packet); pendingPacket.Packet = null; NetDebug.Write("[PA]Removing reliableInOrder ack: {0} - true", pendingSeq); } } Monitor.Exit(_pendingPackets); }
private void DataReceived(byte[] reusableBuffer, int count, NetEndPoint remoteEndPoint) { #if STATS_ENABLED Statistics.PacketsReceived++; Statistics.BytesReceived += (uint)count; #endif //Try read packet NetPacket packet = NetPacketPool.GetAndRead(reusableBuffer, 0, count); if (packet == null) { 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; lock (_peers) { _peers.TryGetValue(remoteEndPoint, out netPeer); } if (netPeer != null && netPeer.ConnectionState != ConnectionState.Disconnected) { 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; } var netEvent = CreateEvent(NetEventType.Disconnect); netEvent.Peer = netPeer; netEvent.DataReader.SetSource(packet.RawData, 9, packet.Size); netEvent.DisconnectReason = DisconnectReason.RemoteConnectionClose; EnqueueEvent(netEvent); } break; case PacketProperty.ShutdownOk: if (netPeer.ConnectionState != ConnectionState.ShutdownRequested) { return; } netPeer.ProcessPacket(packet); NetUtils.DebugWriteForce(ConsoleColor.Cyan, "[NM] ShutdownOK!"); break; case PacketProperty.ConnectAccept: if (netPeer.ProcessConnectAccept(packet)) { var connectEvent = CreateEvent(NetEventType.Connect); connectEvent.Peer = netPeer; EnqueueEvent(connectEvent); } NetPacketPool.Recycle(packet); return; default: netPeer.ProcessPacket(packet); return; } return; } //Unacked shutdown if (packet.Property == PacketProperty.Disconnect) { byte[] data = { (byte)PacketProperty.ShutdownOk }; SendRaw(data, 0, 1, remoteEndPoint); return; } if (packet.Property == PacketProperty.ConnectRequest && packet.Size >= 12) { int peersCount = GetPeersCount(ConnectionState.Connected | ConnectionState.InProgress); lock (_connectingPeers) { if (_connectingPeers.Contains(remoteEndPoint)) { return; } if (peersCount < _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); } _connectingPeers.Add(remoteEndPoint); var netEvent = CreateEvent(NetEventType.ConnectionRequest); netEvent.ConnectionRequest = new ConnectionRequest(connectionId, remoteEndPoint, reader, OnConnectionSolved); EnqueueEvent(netEvent); } } } }