private void ReceiveLogic(object state) { Socket socket = (Socket)state; EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0); var receiveBuffer = new byte[NetConstants.MaxPacketSize]; while (IsActive()) { int result; //Reading data try { if (socket.Available == 0 && !socket.Poll(ReceivePollingTime, SelectMode.SelectRead)) { continue; } result = socket.ReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref bufferEndPoint); } catch (SocketException ex) { switch (ex.SocketErrorCode) { #if UNITY_IOS && !UNITY_EDITOR case SocketError.NotConnected: #endif case SocketError.Interrupted: case SocketError.NotSocket: return; case SocketError.ConnectionReset: case SocketError.MessageSize: case SocketError.TimedOut: NetDebug.Write(NetLogLevel.Trace, "[R]Ignored error: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString()); break; default: NetDebug.WriteError("[R]Error code: {0} - {1}", (int)ex.SocketErrorCode, ex.ToString()); _listener.OnMessageReceived(null, 0, ex.SocketErrorCode, (IPEndPoint)bufferEndPoint); break; } continue; } catch (ObjectDisposedException) { return; } //All ok! NetDebug.Write(NetLogLevel.Trace, "[R]Received data from {0}, result: {1}", bufferEndPoint.ToString(), result); _listener.OnMessageReceived(receiveBuffer, result, 0, (IPEndPoint)bufferEndPoint); } }
//We got punch and can connect private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint) { //Read info NetDebug.Write(NetLogLevel.Trace, "[NAT] punch received from {0} - additional info: {1}", senderEndPoint, req.Token); //Release punch success to client; enabling him to Connect() to Sender if token is ok lock (_successEvents) { _successEvents.Enqueue(new SuccessEventData { TargetEndPoint = senderEndPoint, Type = req.IsExternal ? NatAddressType.External : NatAddressType.Internal, Token = req.Token }); } }
public void TrySend(long currentTime, NetPeer peer) { if (_packet == null) { return; } if (_isSent) //check send time { double resendDelay = peer.ResendDelay * TimeSpan.TicksPerMillisecond; double packetHoldTime = currentTime - _timeStamp; if (packetHoldTime < resendDelay) { return; } NetDebug.Write("[RC]Resend: {0} > {1}", (int)packetHoldTime, resendDelay); } _timeStamp = currentTime; _isSent = true; peer.SendUserData(_packet); }
public override void SendNextPackets() { if (_mustSendAcks) { _mustSendAcks = false; NetDebug.Write("[RR]SendAcks"); lock (_outgoingAcks) Peer.SendUserData(_outgoingAcks); } long currentTime = DateTime.UtcNow.Ticks; lock (_pendingPackets) { //get packets from queue lock (OutgoingQueue) { while (OutgoingQueue.Count > 0) { int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart); if (relate >= _windowSize) { break; } var netPacket = OutgoingQueue.Dequeue(); netPacket.Sequence = (ushort)_localSeqence; netPacket.ChannelId = _id; _pendingPackets[_localSeqence % _windowSize].Init(netPacket); _localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence; } } //send for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence) { _pendingPackets[pendingSeq % _windowSize].TrySend(currentTime, Peer); } } }
public int SendTo(byte[] data, int offset, int size, IPEndPoint remoteEndPoint, ref SocketError errorCode) { if (!IsActive()) { return(0); } try { var socket = _udpSocketv4; if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support) { socket = _udpSocketv6; } int result = socket.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint); NetDebug.Write(NetLogLevel.Trace, "[S]Send packet to {0}, result: {1}", remoteEndPoint, result); return(result); } catch (SocketException ex) { switch (ex.SocketErrorCode) { case SocketError.NoBufferSpaceAvailable: case SocketError.Interrupted: return(0); case SocketError.MessageSize: //do nothing break; default: NetDebug.WriteError("[S]" + ex); break; } errorCode = ex.SocketErrorCode; return(-1); } catch (Exception ex) { NetDebug.WriteError("[S]" + ex); return(-1); } }
//We got introduce and must punch private void OnNatIntroductionResponse(NatIntroduceResponsePacket req) { NetDebug.Write(NetLogLevel.Trace, "[NAT] introduction received"); // send internal punch var punchPacket = new NatPunchPacket { Token = req.Token }; Send(punchPacket, req.Internal); NetDebug.Write(NetLogLevel.Trace, "[NAT] internal punch sent to " + req.Internal); // hack for some routers SocketError errorCode = 0; _socket.Ttl = 2; _socket.SendTo(new[] { (byte)PacketProperty.Empty }, 0, 1, req.External, ref errorCode); // send external punch _socket.Ttl = NetConstants.SocketTTL; punchPacket.IsExternal = true; Send(punchPacket, req.External); NetDebug.Write(NetLogLevel.Trace, "[NAT] external punch sent to " + req.External); }
//Process incoming packet public override bool ProcessPacket(NetPacket packet) { if (packet.Property == PacketProperty.Ack) { ProcessAck(packet); return(false); } int seq = packet.Sequence; if (seq >= NetConstants.MaxSequence) { NetDebug.Write("[RR]Bad sequence"); return(false); } int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart); int relateSeq = NetUtils.RelativeSequenceNumber(seq, _remoteSequence); if (relateSeq > _windowSize) { NetDebug.Write("[RR]Bad sequence"); return(false); } //Drop bad packets if (relate < 0) { //Too old packet doesn't ack NetDebug.Write("[RR]ReliableInOrder too old"); return(false); } if (relate >= _windowSize * 2) { //Some very new packet NetDebug.Write("[RR]ReliableInOrder too new"); return(false); } //If very new - move window int ackIdx; int ackByte; int ackBit; lock (_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.ChanneledHeaderSize + 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.ChanneledHeaderSize + ackIdx / BitsInByte; ackBit = ackIdx % BitsInByte; if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0) { NetDebug.Write("[RR]ReliableInOrder duplicate"); return(false); } //save ack _outgoingAcks.RawData[ackByte] |= (byte)(1 << ackBit); } //detailed check if (seq == _remoteSequence) { NetDebug.Write("[RR]ReliableInOrder packet succes"); Peer.AddReliablePacket(_sendType, packet); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; if (_ordered) { NetPacket p; while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null) { //process holden packet _receivedPackets[_remoteSequence % _windowSize] = null; Peer.AddReliablePacket(_sendType, p); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } else { while (_earlyReceived[_remoteSequence % _windowSize]) { //process early packet _earlyReceived[_remoteSequence % _windowSize] = false; _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } return(true); } //holden packet if (_ordered) { _receivedPackets[ackIdx] = packet; } else { _earlyReceived[ackIdx] = true; Peer.AddReliablePacket(_sendType, packet); } return(true); }
//ProcessAck in packet private 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; lock (_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.ChanneledHeaderSize + pendingIdx / BitsInByte; int currentBit = pendingIdx % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { if (Peer.NetManager.EnableStatistics) { Peer.Statistics.IncrementPacketLoss(); Peer.NetManager.Statistics.IncrementPacketLoss(); } //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 bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress, IPv6Mode ipv6Mode) { //Setup socket socket.ReceiveTimeout = 500; socket.SendTimeout = 500; socket.ReceiveBufferSize = NetConstants.SocketBufferSize; socket.SendBufferSize = NetConstants.SocketBufferSize; #if !UNITY || UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN #if NETSTANDARD || NETCOREAPP if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) #endif try { socket.IOControl(SioUdpConnreset, new byte[] { 0 }, null); } catch { //ignored } #endif try { socket.ExclusiveAddressUse = !reuseAddress; socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress); } catch { //Unity with IL2CPP throws an exception here, it doesn't matter in most cases so just ignore it } if (socket.AddressFamily == AddressFamily.InterNetwork) { Ttl = NetConstants.SocketTTL; #if NETSTANDARD || NETCOREAPP if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) #endif try { socket.DontFragment = true; } catch (SocketException e) { NetDebug.WriteError("[B]DontFragment error: {0}", e.SocketErrorCode); } try { socket.EnableBroadcast = true; } catch (SocketException e) { NetDebug.WriteError("[B]Broadcast error: {0}", e.SocketErrorCode); } } else //IPv6 specific { if (ipv6Mode == IPv6Mode.DualMode) { try { //Disable IPv6 only mode socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false); } catch (Exception e) { NetDebug.WriteError("[B]Bind exception (dualmode setting): {0}", e.ToString()); } } } //Bind try { socket.Bind(ep); NetDebug.Write(NetLogLevel.Trace, "[B]Successfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port); //join multicast if (socket.AddressFamily == AddressFamily.InterNetworkV6) { try { #if !UNITY socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(MulticastAddressV6)); #endif } catch (Exception) { // Unity3d throws exception - ignored } } } catch (SocketException bindException) { switch (bindException.SocketErrorCode) { //IPv6 bind fix case SocketError.AddressAlreadyInUse: if (socket.AddressFamily == AddressFamily.InterNetworkV6 && ipv6Mode != IPv6Mode.DualMode) { try { //Set IPv6Only socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, true); socket.Bind(ep); } #if UNITY_2018_3_OR_NEWER catch (SocketException ex) { //because its fixed in 2018_3 NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", ex.ToString(), ex.SocketErrorCode); #else catch (SocketException) { #endif return(false); } return(true); } break; //hack for iOS (Unity3D) case SocketError.AddressFamilyNotSupported: return(true); } NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", bindException.ToString(), bindException.SocketErrorCode); return(false); } return(true); }