public bool Bind(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool reuseAddress) { _udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _udpSocketv4.Blocking = false; _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 = false; _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); }
//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; } }
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); } } }
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); } }