internal bool Shutdown(byte[] data, int start, int length, bool force) { lock (_shutdownLock) { //trying to shutdown already disconnected if (_connectionState == ConnectionState.Disconnected || _connectionState == ConnectionState.ShutdownRequested) { return(false); } //don't send anything if (force) { _connectionState = ConnectionState.Disconnected; return(true); } //reset time for reconnect protection _timeSinceLastPacket = 0; //send shutdown packet _shutdownPacket = new NetPacket(PacketProperty.Disconnect, length); _shutdownPacket.ConnectionNumber = _connectNum; FastBitConverter.GetBytes(_shutdownPacket.RawData, 1, _connectTime); if (_shutdownPacket.Size >= _mtu) { //Drop additional data NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU - 8!"); } else if (data != null && length > 0) { Buffer.BlockCopy(data, start, _shutdownPacket.RawData, 9, length); } _connectionState = ConnectionState.ShutdownRequested; NetDebug.Write("[Peer] Send disconnect"); NetManager.SendRaw(_shutdownPacket, EndPoint); return(true); } }
private void ReceiveLogic(object state) { Socket socket = (Socket)state; EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0); while (IsActive()) { //Reading data try { if (socket.Available == 0 && !socket.Poll(ReceivePollingTime, SelectMode.SelectRead)) { continue; } NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize); packet.Size = socket.ReceiveFrom(packet.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None, ref bufferEndPoint); //NetDebug.Write(NetLogLevel.Trace, $"[R]Received data from {bufferEndPoint}, result: {packet.Size}"); OnMessageReceived(packet, (IPEndPoint)bufferEndPoint); } catch (SocketException ex) { if (ProcessError(ex)) { return; } } catch (ObjectDisposedException) { //socket closed return; } catch (Exception e) { //protects socket receive thread NetDebug.WriteError("[NM] SocketReceiveThread error: " + e); } } }
/// <summary> /// Flush all queued packets /// </summary> public void Flush() { if (_connectionState != ConnectionState.Connected) { return; } lock (_flushLock) { _reliableOrderedChannel.SendNextPackets(); _reliableUnorderedChannel.SendNextPackets(); _reliableSequencedChannel.SendNextPackets(); _sequencedChannel.SendNextPackets(); _unreliableChannel.SendNextPackets(); //If merging enabled if (_mergePos > 0) { if (_mergeCount > 1) { NetDebug.Write("[P]Send merged: " + _mergePos + ", count: " + _mergeCount); _netManager.SendRaw(_mergeData.RawData, 0, NetConstants.HeaderSize + _mergePos, _remoteEndPoint); #if STATS_ENABLED Statistics.PacketsSent++; Statistics.BytesSent += (ulong)(NetConstants.HeaderSize + _mergePos); #endif } else { //Send without length information and merging _netManager.SendRaw(_mergeData.RawData, NetConstants.HeaderSize + 2, _mergePos - 2, _remoteEndPoint); #if STATS_ENABLED Statistics.PacketsSent++; Statistics.BytesSent += (ulong)(_mergePos - 2); #endif } _mergePos = 0; _mergeCount = 0; } } }
//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 _socket.Ttl = 2; _socket.SendRaw(new[] { (byte)PacketProperty.Empty }, 0, 1, req.External); // 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); }
private void ReceiveLogic(object state) { Socket socket = (Socket)state; EndPoint bufferEndPoint = new IPEndPoint(socket.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, 0); byte[] 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) { if (ProcessError(ex, bufferEndPoint)) { return; } 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); } }
public NetPeer AcceptIfKey(string key) { if (!TryActivate()) { return(null); } try { if (Data.GetString() == key) { Result = ConnectionRequestResult.Accept; return(_listener.OnConnectionSolved(this, null, 0, 0)); } } catch { NetDebug.WriteError("[AC] Invalid incoming data"); } Result = ConnectionRequestResult.Reject; _listener.OnConnectionSolved(this, null, 0, 0); return(null); }
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); } } }
internal void SendUserData(NetPacket packet) { packet.ConnectionNumber = _connectNum; //2 - merge byte + minimal packet size + datalen(ushort) if (_netManager.MergeEnabled && _mergePos + packet.Size + NetConstants.HeaderSize * 2 + 2 < _mtu) { FastBitConverter.GetBytes(_mergeData.RawData, _mergePos + NetConstants.HeaderSize, (ushort)packet.Size); Buffer.BlockCopy(packet.RawData, 0, _mergeData.RawData, _mergePos + NetConstants.HeaderSize + 2, packet.Size); _mergePos += packet.Size + 2; _mergeCount++; //DebugWriteForce("Merged: " + _mergePos + "/" + (_mtu - 2) + ", count: " + _mergeCount); return; } NetDebug.Write(NetLogLevel.Trace, "[P]SendingPacket: " + packet.Property); _netManager.SendRaw(packet, _remoteEndPoint); #if STATS_ENABLED Statistics.PacketsSent++; Statistics.BytesSent += (ulong)packet.Size; #endif }
//Returns true if there is a pending packet inside public bool TrySend(long currentTime, NetPeer peer) { if (_packet == null) { return(false); } if (_isSent) //check send time { double resendDelay = peer.ResendDelay * TimeSpan.TicksPerMillisecond; double packetHoldTime = currentTime - _timeStamp; if (packetHoldTime < resendDelay) { return(true); } NetDebug.Write("[RC]Resend: {0} > {1}", (int)packetHoldTime, resendDelay); } _timeStamp = currentTime; _isSent = true; peer.SendUserData(_packet); return(true); }
private void HandleNatIntroduction(NetDataReader dr) { // read intro byte hostByte = dr.GetByte(); IPEndPoint remoteInternal = dr.GetNetEndPoint(); IPEndPoint remoteExternal = dr.GetNetEndPoint(); string token = dr.GetString(MaxTokenLength); NetDebug.Write(NetLogLevel.Trace, "[NAT] introduction received; we are designated " + (hostByte == HostByte ? "host" : "client")); NetDataWriter writer = new NetDataWriter(); // send internal punch writer.Put((byte)PacketProperty.NatPunchMessage); writer.Put(hostByte); writer.Put(token); SocketError errorCode = 0; _socket.SendTo(writer.Data, 0, writer.Length, remoteInternal, ref errorCode); NetDebug.Write(NetLogLevel.Trace, "[NAT] internal punch sent to " + remoteInternal); // send external punch writer.Reset(); writer.Put((byte)PacketProperty.NatPunchMessage); writer.Put(hostByte); writer.Put(token); if (hostByte == HostByte) { _socket.Ttl = 2; _socket.SendTo(writer.Data, 0, writer.Length, remoteExternal, ref errorCode); _socket.Ttl = NetConstants.SocketTTL; } else { _socket.SendTo(writer.Data, 0, writer.Length, remoteExternal, ref errorCode); } NetDebug.Write(NetLogLevel.Trace, "[NAT] external punch sent to " + remoteExternal); }
//Returns true if there is a pending packet inside public bool TrySend(DateTime currentTime, NetPeer peer) { if (_packet == null) { return(false); } if (_isSent) //check send time { TimeSpan resendDelay = peer.ResendDelay; TimeSpan packetHoldTime = currentTime - _timeStamp; if (packetHoldTime < resendDelay) { return(true); } NetDebug.Write("[RC]Resend: {0} > {1}", packetHoldTime, resendDelay); } _timeStamp = currentTime; _isSent = true; peer.SendUserData(_packet); return(true); }
private void HandleNatPunch(IPEndPoint senderEndPoint, NetDataReader dr) { byte fromHostByte = dr.GetByte(); if (fromHostByte != HostByte && fromHostByte != ClientByte) { //garbage return; } //Read info string additionalInfo = dr.GetString(MaxTokenLength); NetDebug.Write(NetLogLevel.Trace, "[NAT] punch received from {0} - additional info: {1}", senderEndPoint, additionalInfo); //Release punch success to client; enabling him to Connect() to msg.Sender if token is ok lock (_successEvents) { _successEvents.Enqueue(new SuccessEventData { TargetEndPoint = senderEndPoint, Token = additionalInfo }); } }
public int SendTo(byte[] data, int offset, int size, IPEndPoint remoteEndPoint, ref SocketError errorCode) { 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); } }
public bool SendBroadcast(byte[] data, int offset, int size, int port) { if (!IsActive()) { return(false); } var broadcastSuccess = false; var multicastSuccess = false; try { broadcastSuccess = _udpSocketv4.SendTo( data, offset, size, SocketFlags.None, new IPEndPoint(IPAddress.Broadcast, port)) > 0; if (_udpSocketv6 != null) { multicastSuccess = _udpSocketv6.SendTo( data, offset, size, SocketFlags.None, new IPEndPoint(MulticastAddressV6, port)) > 0; } } catch (Exception ex) { NetDebug.WriteError("[S][MCAST]" + ex); return(broadcastSuccess); } return(broadcastSuccess || multicastSuccess); }
private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress) { //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) { socket.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); } } //Bind try { socket.Bind(ep); NetDebug.Write(NetLogLevel.Trace, "[B]Successfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port); } catch (SocketException bindException) { switch (bindException.SocketErrorCode) { //IPv6 bind fix case SocketError.AddressAlreadyInUse: if (socket.AddressFamily == AddressFamily.InterNetworkV6) { try { 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); }
internal int SendRaw(byte[] message, int start, int length, IPEndPoint remoteEndPoint) { if (!IsRunning) { return(0); } NetPacket expandedPacket = null; if (_extraPacketLayer != null) { expandedPacket = PoolGetPacket(length + _extraPacketLayer.ExtraPacketSizeForLayer); Buffer.BlockCopy(message, start, expandedPacket.RawData, 0, length); start = 0; _extraPacketLayer.ProcessOutBoundPacket(ref remoteEndPoint, ref expandedPacket.RawData, ref start, ref length); message = expandedPacket.RawData; } var socket = _udpSocketv4; if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support) { socket = _udpSocketv6; if (socket == null) { return(0); } } int result; try { if (UseNativeSockets) { byte[] socketAddress; if (remoteEndPoint is NativeEndPoint nep) { socketAddress = nep.NativeAddress; } else //Convert endpoint to raw { if (_endPointBuffer == null) { _endPointBuffer = new byte[NativeSocket.IPv6AddrSize]; } socketAddress = _endPointBuffer; bool ipv4 = remoteEndPoint.AddressFamily == AddressFamily.InterNetwork; short addressFamily = NativeSocket.GetNativeAddressFamily(remoteEndPoint); socketAddress[0] = (byte)(addressFamily); socketAddress[1] = (byte)(addressFamily >> 8); socketAddress[2] = (byte)(remoteEndPoint.Port >> 8); socketAddress[3] = (byte)(remoteEndPoint.Port); if (ipv4) { #pragma warning disable 618 long addr = remoteEndPoint.Address.Address; #pragma warning restore 618 socketAddress[4] = (byte)(addr); socketAddress[5] = (byte)(addr >> 8); socketAddress[6] = (byte)(addr >> 16); socketAddress[7] = (byte)(addr >> 24); } else { #if NETCOREAPP || NETSTANDARD2_1 || NETSTANDARD2_1_OR_GREATER remoteEndPoint.Address.TryWriteBytes(new Span <byte>(socketAddress, 8, 16), out _); #else byte[] addrBytes = remoteEndPoint.Address.GetAddressBytes(); Buffer.BlockCopy(addrBytes, 0, socketAddress, 8, 16); #endif } } #if LITENETLIB_UNSAFE unsafe { fixed(byte *dataWithOffset = &message[start]) { result = NativeSocket.SendTo(socket.Handle, dataWithOffset, length, socketAddress, socketAddress.Length); } } #else if (start > 0) { if (_sendToBuffer == null) { _sendToBuffer = new byte[NetConstants.MaxPacketSize]; } Buffer.BlockCopy(message, start, _sendToBuffer, 0, length); message = _sendToBuffer; } result = NativeSocket.SendTo(socket.Handle, message, length, socketAddress, socketAddress.Length); #endif if (result == -1) { throw NativeSocket.GetSocketException(); } } else { result = socket.SendTo(message, start, length, SocketFlags.None, remoteEndPoint); } //NetDebug.WriteForce("[S]Send packet to {0}, result: {1}", remoteEndPoint, result); } catch (SocketException ex) { switch (ex.SocketErrorCode) { case SocketError.NoBufferSpaceAvailable: case SocketError.Interrupted: return(0); case SocketError.MessageSize: NetDebug.Write(NetLogLevel.Trace, "[SRD] 10040, datalen: {0}", length); return(0); case SocketError.HostUnreachable: case SocketError.NetworkUnreachable: if (DisconnectOnUnreachable && TryGetPeer(remoteEndPoint, out var fromPeer)) { DisconnectPeerForce( fromPeer, ex.SocketErrorCode == SocketError.HostUnreachable ? DisconnectReason.HostUnreachable : DisconnectReason.NetworkUnreachable, ex.SocketErrorCode, null); } CreateEvent(NetEvent.EType.Error, remoteEndPoint: remoteEndPoint, errorCode: ex.SocketErrorCode); return(-1); default: NetDebug.WriteError($"[S] {ex}"); return(-1); } } catch (Exception ex) { NetDebug.WriteError($"[S] {ex}"); return(0); } finally { if (expandedPacket != null) { PoolRecycle(expandedPacket); } } if (result <= 0) { return(0); } if (EnableStatistics) { Statistics.IncrementPacketsSent(); Statistics.AddBytesSent(length); } return(result); }
private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress) { //Setup socket socket.ReceiveTimeout = 500; socket.SendTimeout = 500; socket.ReceiveBufferSize = NetConstants.SocketBufferSize; socket.SendBufferSize = NetConstants.SocketBufferSize; try { //知识点1 在UDP通信过程中,如果客户端中途断开,服务器会收到一个SocketException,错 //误ID为10054,描述是“远程主机强迫关闭了一个现有的连接”,紧接着的事就可怕了,UDP //服务终止监听,所有客户端都受到了影响。也就是说一个客户端引起的异常导致了整个系统的崩溃。 //UDP是无连接的,可是当实际现场使用时,如果远程主机异常关闭,服务器则会抛出一个异常:远程主机强行关闭了一个连接 //而一但这个异常抛出后,又没有及时处理,那么整个UDP服务都将崩溃而不能接收任何数据 socket.IOControl(SioUdpConnreset, new byte[] { 0 }, null); } catch { //ignored } 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) { socket.Ttl = NetConstants.SocketTTL; #if NETSTANDARD2_0 || NETCOREAPP2_0 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); } } //Bind try { socket.Bind(ep); NetDebug.Write(NetLogLevel.Trace, "[B]Successfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port); } catch (SocketException bindException) { switch (bindException.SocketErrorCode) { //IPv6 bind fix case SocketError.AddressAlreadyInUse: if (socket.AddressFamily == AddressFamily.InterNetworkV6) { try { socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, true); socket.Bind(ep); } catch (SocketException ex) { NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", ex.ToString(), ex.SocketErrorCode); 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); }
internal void AddIncomingPacket(NetPacket p) { if (p.IsFragmented) { NetDebug.Write("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], ChannelId = p.ChannelId }; _holdedFragments.Add(packetFragId, incomingFragments); } //Cache var fragments = incomingFragments.Fragments; //Error check if (p.FragmentPart >= fragments.Length || fragments[p.FragmentPart] != null || p.ChannelId != incomingFragments.ChannelId) { _packetPool.Recycle(p); NetDebug.WriteError("Invalid fragment packet"); return; } //Fill array fragments[p.FragmentPart] = p; //Increase received fragments count incomingFragments.ReceivedCount++; //Increase total size incomingFragments.TotalSize += p.Size - NetConstants.FragmentTotalSize; //Check for finish if (incomingFragments.ReceivedCount != fragments.Length) { return; } NetPacket resultingPacket = _packetPool.GetWithProperty(p.Property, incomingFragments.TotalSize); resultingPacket.ChannelId = incomingFragments.ChannelId; int resultingPacketOffset = resultingPacket.GetHeaderSize(); int firstFragmentSize = fragments[0].Size - NetConstants.FragmentTotalSize; for (int i = 0; i < incomingFragments.ReceivedCount; i++) { //Create resulting big packet int fragmentSize = fragments[i].Size - NetConstants.FragmentTotalSize; Buffer.BlockCopy( fragments[i].RawData, NetConstants.FragmentTotalSize, resultingPacket.RawData, resultingPacketOffset + firstFragmentSize * i, fragmentSize); //Free memory _packetPool.Recycle(fragments[i]); fragments[i] = null; } //Send to process NetManager.ReceiveFromPeer(resultingPacket, this); //Clear memory _holdedFragments.Remove(packetFragId); } else //Just simple packet { NetManager.ReceiveFromPeer(p, this); } }
//Process incoming packet internal void ProcessPacket(NetPacket packet) { //not initialized if (_connectionState == ConnectionState.Outgoing) { _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, 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: 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; } }
internal void Update(int deltaTime) { _timeSinceLastPacket += deltaTime; switch (_connectionState) { case ConnectionState.Connected: if (_timeSinceLastPacket > NetManager.DisconnectTimeout) { NetDebug.Write( "[UPDATE] Disconnect by timeout: {0} > {1}", _timeSinceLastPacket, NetManager.DisconnectTimeout); NetManager.DisconnectPeerForce(this, DisconnectReason.Timeout, 0, null); return; } break; case ConnectionState.ShutdownRequested: if (_timeSinceLastPacket > NetManager.DisconnectTimeout) { _connectionState = ConnectionState.Disconnected; } else { _shutdownTimer += deltaTime; if (_shutdownTimer >= ShutdownDelay) { _shutdownTimer = 0; NetManager.SendRaw(_shutdownPacket, EndPoint); } } return; case ConnectionState.Outgoing: _connectTimer += deltaTime; if (_connectTimer > NetManager.ReconnectDelay) { _connectTimer = 0; _connectAttempts++; if (_connectAttempts > NetManager.MaxConnectAttempts) { NetManager.DisconnectPeerForce(this, DisconnectReason.ConnectionFailed, 0, null); return; } //else send connect again NetManager.SendRaw(_connectRequestPacket, EndPoint); } return; case ConnectionState.Disconnected: return; } //Send ping _pingSendTimer += deltaTime; if (_pingSendTimer >= NetManager.PingInterval) { NetDebug.Write("[PP] Send ping..."); //reset timer _pingSendTimer = 0; //send ping _pingPacket.Sequence++; //ping timeout if (_pingTimer.IsRunning) { UpdateRoundTripTime((int)_pingTimer.ElapsedMilliseconds); } _pingTimer.Reset(); _pingTimer.Start(); NetManager.SendRaw(_pingPacket, EndPoint); } //RTT - round trip time _rttResetTimer += deltaTime; if (_rttResetTimer >= NetManager.PingInterval * 3) { _rttResetTimer = 0; _rtt = _avgRtt; _rttCount = 1; } UpdateMtuLogic(deltaTime); //Pending send Flush(); }
private void SendInternal( byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (_connectionState == ConnectionState.ShutdownRequested || _connectionState == ConnectionState.Disconnected) { return; } if (channelNumber >= _channels.Length) { return; } //Select channel PacketProperty property; BaseChannel channel; if (deliveryMethod == DeliveryMethod.Unreliable) { property = PacketProperty.Unreliable; channel = _unreliableChannel; } else { property = PacketProperty.Channeled; channel = CreateChannel((byte)(channelNumber * 4 + (byte)deliveryMethod)); } //Prepare NetDebug.Write("[RS]Packet: " + property); //Check fragmentation int headerSize = NetPacket.GetHeaderSize(property); //Save mtu for multithread int mtu = _mtu; if (length + headerSize > mtu) { //if cannot be fragmented if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) { throw new TooBigPacketException("Unreliable packet size exceeded maximum of " + (_mtu - headerSize) + " bytes"); } int packetFullSize = mtu - headerSize; int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize; int totalPackets = length / packetDataSize + (length % packetDataSize == 0 ? 0 : 1); NetDebug.Write("FragmentSend:\n" + " MTU: {0}\n" + " headerSize: {1}\n" + " packetFullSize: {2}\n" + " packetDataSize: {3}\n" + " totalPackets: {4}", mtu, headerSize, packetFullSize, packetDataSize, totalPackets); if (totalPackets > ushort.MaxValue) { throw new TooBigPacketException("Data was split in " + totalPackets + " fragments, which exceeds " + ushort.MaxValue); } lock (_sendLock) { for (ushort partIdx = 0; partIdx < totalPackets; partIdx++) { int sendLength = length > packetDataSize ? packetDataSize : length; NetPacket p = _packetPool.GetWithProperty(property, sendLength + NetConstants.FragmentHeaderSize); p.UserData = userData; p.FragmentId = _fragmentId; p.FragmentPart = partIdx; p.FragmentsTotal = (ushort)totalPackets; p.MarkFragmented(); Buffer.BlockCopy(data, partIdx * packetDataSize, p.RawData, NetConstants.FragmentTotalSize, sendLength); channel.AddToQueue(p); length -= sendLength; } _fragmentId++; } return; } //Else just send NetPacket packet = _packetPool.GetWithData(property, data, start, length); packet.UserData = userData; channel.AddToQueue(packet); }
private void SendInternal( byte[] data, int start, int length, byte channelNumber, DeliveryMethod deliveryMethod, object userData) { if (_connectionState != ConnectionState.Connected || channelNumber >= _channels.Length) { return; } //Select channel PacketProperty property; BaseChannel channel = null; if (deliveryMethod == DeliveryMethod.Unreliable) { property = PacketProperty.Unreliable; } else { property = PacketProperty.Channeled; channel = CreateChannel((byte)(channelNumber * 4 + (byte)deliveryMethod)); } //Prepare NetDebug.Write("[RS]Packet: " + property); //Check fragmentation int headerSize = NetPacket.GetHeaderSize(property); //Save mtu for multithread int mtu = _mtu; if (length + headerSize > mtu) { //if cannot be fragmented if (deliveryMethod != DeliveryMethod.ReliableOrdered && deliveryMethod != DeliveryMethod.ReliableUnordered) { throw new TooBigPacketException("Unreliable or ReliableSequenced packet size exceeded maximum of " + (mtu - headerSize) + " bytes, Check allowed size by GetMaxSinglePacketSize()"); } int packetFullSize = mtu - headerSize; int packetDataSize = packetFullSize - NetConstants.FragmentHeaderSize; int totalPackets = length / packetDataSize + (length % packetDataSize == 0 ? 0 : 1); NetDebug.Write("FragmentSend:\n" + " MTU: {0}\n" + " headerSize: {1}\n" + " packetFullSize: {2}\n" + " packetDataSize: {3}\n" + " totalPackets: {4}", mtu, headerSize, packetFullSize, packetDataSize, totalPackets); if (totalPackets > ushort.MaxValue) { throw new TooBigPacketException("Data was split in " + totalPackets + " fragments, which exceeds " + ushort.MaxValue); } ushort currentFragmentId = (ushort)Interlocked.Increment(ref _fragmentId); for (ushort partIdx = 0; partIdx < totalPackets; partIdx++) { int sendLength = length > packetDataSize ? packetDataSize : length; NetPacket p = _packetPool.GetPacket(headerSize + sendLength + NetConstants.FragmentHeaderSize); p.Property = property; p.UserData = userData; p.FragmentId = currentFragmentId; p.FragmentPart = partIdx; p.FragmentsTotal = (ushort)totalPackets; p.MarkFragmented(); Buffer.BlockCopy(data, partIdx * packetDataSize, p.RawData, NetConstants.FragmentedHeaderTotalSize, sendLength); channel.AddToQueue(p); length -= sendLength; } return; } //Else just send NetPacket packet = _packetPool.GetPacket(headerSize + length); packet.Property = property; Buffer.BlockCopy(data, start, packet.RawData, headerSize, length); packet.UserData = userData; if (channel == null) //unreliable { lock (_unreliableChannel) _unreliableChannel.Enqueue(packet); } else { channel.AddToQueue(packet); } }
internal void AddReliablePacket(DeliveryMethod method, NetPacket p) { if (p.IsFragmented) { NetDebug.Write("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], ChannelId = p.ChannelId }; _holdedFragments.Add(packetFragId, incomingFragments); } //Cache var fragments = incomingFragments.Fragments; //Error check if (p.FragmentPart >= fragments.Length || fragments[p.FragmentPart] != null || p.ChannelId != incomingFragments.ChannelId) { _packetPool.Recycle(p); NetDebug.WriteError("Invalid fragment packet"); return; } //Fill array fragments[p.FragmentPart] = p; //Increase received fragments count incomingFragments.ReceivedCount++; //Increase total size incomingFragments.TotalSize += p.Size - NetConstants.FragmentedHeaderTotalSize; //Check for finish if (incomingFragments.ReceivedCount != fragments.Length) { return; } //just simple packet NetPacket resultingPacket = _packetPool.GetPacket(incomingFragments.TotalSize); int firstFragmentSize = fragments[0].Size - NetConstants.FragmentedHeaderTotalSize; for (int i = 0; i < incomingFragments.ReceivedCount; i++) { var fragment = fragments[i]; //Create resulting big packet Buffer.BlockCopy( fragment.RawData, NetConstants.FragmentedHeaderTotalSize, resultingPacket.RawData, firstFragmentSize * i, fragment.Size - NetConstants.FragmentedHeaderTotalSize); //Free memory _packetPool.Recycle(fragment); } Array.Clear(fragments, 0, incomingFragments.ReceivedCount); //Send to process NetManager.CreateReceiveEvent(resultingPacket, method, 0, this); //Clear memory _holdedFragments.Remove(packetFragId); } else //Just simple packet { NetManager.CreateReceiveEvent(p, method, NetConstants.ChanneledHeaderSize, this); } }
internal void Update(int deltaTime) { Interlocked.Add(ref _timeSinceLastPacket, deltaTime); switch (_connectionState) { case ConnectionState.Connected: if (_timeSinceLastPacket > NetManager.DisconnectTimeout) { NetDebug.Write( "[UPDATE] Disconnect by timeout: {0} > {1}", _timeSinceLastPacket, NetManager.DisconnectTimeout); NetManager.DisconnectPeerForce(this, DisconnectReason.Timeout, 0, null); return; } break; case ConnectionState.ShutdownRequested: if (_timeSinceLastPacket > NetManager.DisconnectTimeout) { _connectionState = ConnectionState.Disconnected; } else { _shutdownTimer += deltaTime; if (_shutdownTimer >= ShutdownDelay) { _shutdownTimer = 0; NetManager.SendRaw(_shutdownPacket, EndPoint); } } return; case ConnectionState.Outgoing: _connectTimer += deltaTime; if (_connectTimer > NetManager.ReconnectDelay) { _connectTimer = 0; _connectAttempts++; if (_connectAttempts > NetManager.MaxConnectAttempts) { NetManager.DisconnectPeerForce(this, DisconnectReason.ConnectionFailed, 0, null); return; } //else send connect again NetManager.SendRaw(_connectRequestPacket, EndPoint); } return; case ConnectionState.Disconnected: return; } //Send ping _pingSendTimer += deltaTime; if (_pingSendTimer >= NetManager.PingInterval) { NetDebug.Write("[PP] Send ping..."); //reset timer _pingSendTimer = 0; //send ping _pingPacket.Sequence++; //ping timeout if (_pingTimer.IsRunning) { UpdateRoundTripTime((int)_pingTimer.ElapsedMilliseconds); } _pingTimer.Reset(); _pingTimer.Start(); NetManager.SendRaw(_pingPacket, EndPoint); } //RTT - round trip time _rttResetTimer += deltaTime; if (_rttResetTimer >= NetManager.PingInterval * 3) { _rttResetTimer = 0; _rtt = _avgRtt; _rttCount = 1; } UpdateMtuLogic(deltaTime); //Pending send if (_channelSendQueue.Count > 0) { lock (_channelSendQueue) { var count = _channelSendQueue.Count; while (count-- > 0) { BaseChannel channel = _channelSendQueue.Dequeue(); if (channel.SendAndCheckQueue()) { // still has something to send, re-add it to the send queue _channelSendQueue.Enqueue(channel); } } } } if (_unreliableChannel.Count > 0) { lock (_unreliableChannel) { while (_unreliableChannel.Count > 0) { NetPacket packet = _unreliableChannel.Dequeue(); SendUserData(packet); NetManager.NetPacketPool.Recycle(packet); } } } SendMerged(); }
/// <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="channelNumber">Number of channel (from 0 to channelsCount - 1)</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, byte channelNumber, DeliveryMethod options) { if (_connectionState == ConnectionState.ShutdownRequested || _connectionState == ConnectionState.Disconnected) { return; } if (channelNumber >= _channelsTotalCount) { return; } //Select channel PacketProperty property; BaseChannel channel; if (options == DeliveryMethod.Unreliable) { property = PacketProperty.Unreliable; channel = _unreliableChannel; } else { property = PacketProperty.Channeled; channel = CreateChannel(NetConstants.ChannelNumberToId(options, channelNumber, _channelsCount)); } //Prepare NetDebug.Write("[RS]Packet: " + property); //Check fragmentation int headerSize = NetPacket.GetHeaderSize(property); //Save mtu for multithread int mtu = _mtu; if (length + headerSize > mtu) { //if cannot be fragmented if (options != DeliveryMethod.ReliableOrdered && options != DeliveryMethod.ReliableUnordered) { 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); NetDebug.Write("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); }
//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); } AddToPeerChannelSendQueue(); //detailed check if (seq == _remoteSequence) { NetDebug.Write("[RR]ReliableInOrder packet succes"); Peer.AddReliablePacket(_deliveryMethod, 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(_deliveryMethod, 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(_deliveryMethod, packet); } return(true); }
private bool BindSocket(Socket socket, IPEndPoint ep, bool reuseAddress) { //Setup socket socket.ReceiveTimeout = 500; socket.SendTimeout = 500; socket.ReceiveBufferSize = NetConstants.SocketBufferSize; socket.SendBufferSize = NetConstants.SocketBufferSize; try { socket.ExclusiveAddressUse = !reuseAddress; socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress); } catch { NetDebug.WriteError("IL2CPP SetSocketOption error"); } if (socket.AddressFamily == AddressFamily.InterNetwork) { socket.Ttl = NetConstants.SocketTTL; #if NETCORE 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); } } //Bind try { socket.Bind(ep); NetDebug.Write(NetLogLevel.Trace, "[B]Successfully binded to port: {0}", ((IPEndPoint)socket.LocalEndPoint).Port); } catch (SocketException bindException) { switch (bindException.SocketErrorCode) { //IPv6 bind fix case SocketError.AddressAlreadyInUse: if (socket.AddressFamily == AddressFamily.InterNetworkV6) { try { socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, true); socket.Bind(ep); } catch (SocketException ex) { NetDebug.WriteError("[B]Bind exception: {0}, errorCode: {1}", ex.ToString(), ex.SocketErrorCode); 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); }
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 (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { try { socket.IOControl(SioUdpConnreset, new byte[] { 0 }, null); } catch { //ignored } } 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 (ep.AddressFamily == AddressFamily.InterNetwork || ipv6Mode == IPv6Mode.DualMode) { Ttl = NetConstants.SocketTTL; try { socket.EnableBroadcast = true; } catch (SocketException e) { NetDebug.WriteError($"[B]Broadcast error: {e.SocketErrorCode}"); } if (ipv6Mode == IPv6Mode.DualMode) { try { socket.DualMode = true; } catch (Exception e) { NetDebug.WriteError($"[B]Bind exception (dualmode setting): {e}"); } } else if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { try { socket.DontFragment = true; } catch (SocketException e) { NetDebug.WriteError($"[B]DontFragment error: {e.SocketErrorCode}"); } } } //Bind try { socket.Bind(ep); NetDebug.Write(NetLogLevel.Trace, $"[B]Successfully binded to port: {((IPEndPoint)socket.LocalEndPoint).Port}, AF: {socket.AddressFamily}"); //join multicast if (ep.AddressFamily == AddressFamily.InterNetworkV6) { try { #if !UNITY_2018_3_OR_NEWER 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.DualMode = false; socket.Bind(ep); } catch (SocketException ex) { //because its fixed in 2018_3 NetDebug.WriteError($"[B]Bind exception: {ex}, errorCode: {ex.SocketErrorCode}"); return(false); } return(true); } break; //hack for iOS (Unity3D) case SocketError.AddressFamilyNotSupported: return(true); } NetDebug.WriteError($"[B]Bind exception: {bindException}, errorCode: {bindException.SocketErrorCode}"); return(false); } return(true); }
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; if (socket == null) { return(0); } } int result; if (_useNativeSockets) { byte[] socketAddress; if (remoteEndPoint is NativeEndPoint nep) { socketAddress = nep.NativeAddress; } else //Convert endpoint to raw { if (_endPointBuffer == null) { _endPointBuffer = new byte[NativeSocket.IPv6AddrSize]; } socketAddress = _endPointBuffer; bool ipv4 = remoteEndPoint.AddressFamily == AddressFamily.InterNetwork; short addressFamily = NativeSocket.GetNativeAddressFamily(remoteEndPoint); socketAddress[0] = (byte)(addressFamily); socketAddress[1] = (byte)(addressFamily >> 8); socketAddress[2] = (byte)(remoteEndPoint.Port >> 8); socketAddress[3] = (byte)(remoteEndPoint.Port); if (ipv4) { #pragma warning disable 618 long addr = remoteEndPoint.Address.Address; #pragma warning restore 618 socketAddress[4] = (byte)(addr); socketAddress[5] = (byte)(addr >> 8); socketAddress[6] = (byte)(addr >> 16); socketAddress[7] = (byte)(addr >> 24); } else { #if NETCOREAPP || NETSTANDARD2_1 || NETSTANDARD2_1_OR_GREATER remoteEndPoint.Address.TryWriteBytes(new Span <byte>(socketAddress, 8, 16), out _); #else byte[] addrBytes = remoteEndPoint.Address.GetAddressBytes(); Buffer.BlockCopy(addrBytes, 0, socketAddress, 8, 16); #endif } } #if LITENETLIB_UNSAFE unsafe { fixed(byte *dataWithOffset = &data[offset]) { result = NativeSocket.SendTo(socket.Handle, dataWithOffset, size, socketAddress, socketAddress.Length); } } #else if (offset > 0) { if (_sendToBuffer == null) { _sendToBuffer = new byte[NetConstants.MaxPacketSize]; } Buffer.BlockCopy(data, offset, _sendToBuffer, 0, size); data = _sendToBuffer; } result = NativeSocket.SendTo(socket.Handle, data, size, socketAddress, socketAddress.Length); #endif if (result == -1) { throw NativeSocket.GetSocketException(); } } else { result = socket.SendTo(data, offset, size, SocketFlags.None, remoteEndPoint); } //NetDebug.WriteForce("[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); } }
//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); } } } }