internal void StoreMessage(double now, OutgoingNetMessage msg) { int chanBufIdx = (int)msg.m_sequenceChannel - (int)NetChannel.ReliableUnordered; List <OutgoingNetMessage> list = m_storedMessages[chanBufIdx]; if (list == null) { list = new List <OutgoingNetMessage>(); m_storedMessages[chanBufIdx] = list; } list.Add(msg); // schedule resend double nextResend = now + m_owner.Configuration.ResendFunction(msg.m_numSent, m_currentAvgRoundtrip); msg.m_nextResend = nextResend; // if (msg.m_numSent >= 10) UnityEngine.Debug.Log(now + ": Try no " + msg.m_numSent + ": " + NetUtility.BytesToHex(msg.m_data.ToArray())); m_owner.LogVerbose("Stored " + msg + " @ " + NetTime.ToMillis(now) + " next resend in " + NetTime.ToMillis(msg.m_nextResend - now) + " ms", this); // earliest? if (nextResend < m_earliestResend[chanBufIdx]) { m_earliestResend[chanBufIdx] = nextResend; } }
private void ReceivedPong(double rtSeconds, NetMessage pong) { double now = NetTime.Now; m_lastPongReceived = now; if (pong != null) { ushort remote = pong.m_data.ReadUInt16(); ushort local = NetTime.Encoded(now); int diff = local - remote - (int)(rtSeconds * 1000.0); if (diff < 0) { diff += ushort.MaxValue; } m_remoteTimeOffset = diff; // TODO: slowly go towards? (m_remoteTimeOffset + diff) / 2; m_owner.LogVerbose("Got pong; roundtrip was " + (int)(rtSeconds * 1000) + " ms"); } m_latencyHistory[2] = m_latencyHistory[1]; m_latencyHistory[1] = m_latencyHistory[0]; m_latencyHistory[0] = rtSeconds; m_currentAvgRoundtrip = ((rtSeconds * 3) + (m_latencyHistory[1] * 2) + m_latencyHistory[2]) / 6.0; m_ackMaxDelayTime = (float)( m_owner.m_config.m_maxAckWithholdTime * rtSeconds * m_owner.m_config.m_resendTimeMultiplier ); }
/// <summary> /// Returns a string that represents this object /// </summary> public override string ToString() { // TODO: add custom format support var sb = new StringBuilder(); sb.AppendFormat("Average roundtrip time: {0}", NetTime.ToReadable(_connection.AverageRoundtripTime)).AppendLine(); sb.AppendFormat("Current MTU: {0}", _connection.CurrentMTU).AppendLine(); sb.AppendFormat( "Sent {0} bytes in {1} messages in {2} packets", _sentBytes, _sentMessages, _sentPackets).AppendLine(); sb.AppendFormat( "Received {0} bytes in {1} messages ({2} fragments) in {3} packets", _receivedBytes, _receivedMessages, _receivedFragments, _receivedPackets).AppendLine(); sb.AppendLine(); sb.AppendFormat("Queued: {0}", QueuedMessages).AppendLine(); sb.AppendFormat("Stored: {0}", StoredMessages).AppendLine(); sb.AppendFormat("Witheld: {0}", WithheldMessages).AppendLine(); sb.AppendFormat("Resent (by delay): {0}", _resentMessagesDueToDelay).AppendLine(); sb.AppendFormat("Resent (by hole): {0}", _resentMessagesDueToHole).AppendLine(); return(sb.ToString()); }
internal void StoreMessage(double now, OutgoingNetMessage msg) { int chanBufIdx = (int)msg.m_sequenceChannel - (int)NetChannel.ReliableUnordered; List <OutgoingNetMessage> list = m_storedMessages[chanBufIdx]; if (list == null) { list = new List <OutgoingNetMessage>(); m_storedMessages[chanBufIdx] = list; } list.Add(msg); // schedule resend float multiplier = (1 + (msg.m_numSent * msg.m_numSent)) * m_owner.m_config.m_resendTimeMultiplier; double nextResend = now + (0.025f + (float)m_currentAvgRoundtrip * 1.1f * multiplier); msg.m_nextResend = nextResend; m_owner.LogVerbose("Stored " + msg + " @ " + NetTime.ToMillis(now) + " next resend in " + NetTime.ToMillis(msg.m_nextResend - now) + " ms", this); // earliest? if (nextResend < m_earliestResend[chanBufIdx]) { m_earliestResend[chanBufIdx] = nextResend; } }
internal void ReceivedPong(float now, int pongNumber, float remoteSendTime) { if ((byte)pongNumber != (byte)m_sentPingNumber) { m_peer.LogVerbose("Ping/Pong mismatch; dropped message?"); return; } m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout; float rtt = now - m_sentPingTime; NetException.Assert(rtt >= 0); double diff = (remoteSendTime + (rtt / 2.0)) - now; if (m_averageRoundtripTime < 0) { m_remoteTimeOffset = diff; m_averageRoundtripTime = rtt; m_peer.LogDebug("Initiated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + " Remote time is: " + (now + diff)); } else { m_averageRoundtripTime = (m_averageRoundtripTime * 0.7f) + (float)(rtt * 0.3f); m_remoteTimeOffset = ((m_remoteTimeOffset * (double)(m_sentPingNumber - 1)) + diff) / (double)m_sentPingNumber; m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + ", remote time to " + (now + m_remoteTimeOffset) + " (ie. diff " + m_remoteTimeOffset + ")"); } // update resend delay for all channels float resendDelay = GetResendDelay(); int ct = m_sendChannels.Length; for (int i = 0; i < ct; ++i) { var chan = m_sendChannels[i]; var rchan = chan as NetReliableSenderChannel; if (rchan != null) { rchan.m_resendDelay = resendDelay; } } // m_peer.LogVerbose("Timeout deadline pushed to " + m_timeoutDeadline); // notify the application that average rtt changed if (m_peer.m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionLatencyUpdated)) { NetIncomingMessage update = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionLatencyUpdated, 4); update.m_senderConnection = this; update.m_senderEndPoint = this.m_remoteEndPoint; update.Write(rtt); m_peer.ReleaseMessage(update); } }
public static double FromEncoded( double now, int remoteMillisOffset, ushort encodedRemoteTimestamp, out int adjustRemoteMillis) { // my encoded time ushort localNow = (ushort)(now * 1000 % ushort.MaxValue); ushort localStamp = NetTime.NormalizeEncoded(encodedRemoteTimestamp + remoteMillisOffset); int elapsedMillis = NetTime.GetElapsedMillis(localStamp, localNow); adjustRemoteMillis = 0; return(now - ((float)elapsedMillis / 1000.0f)); }
internal static void QueuePing(NetBase netBase, IPEndPoint toEndPoint, double now) { ushort nowEnc = NetTime.Encoded(now); NetBuffer buffer = netBase.m_scratchBuffer; buffer.Reset(); buffer.Write(nowEnc); netBase.QueueSingleUnreliableSystemMessage( NetSystemType.Ping, buffer, toEndPoint, false ); }
internal void ResendMessages(double now) { for (int i = 0; i < m_storedMessages.Length; i++) { List <OutgoingNetMessage> list = m_storedMessages[i]; if (list == null || list.Count < 1) { continue; } if (now > m_earliestResend[i]) { //TODO: try to find a way to avoid this, this is a quick fix to avoid quadratic time complexity var newlist = new List <OutgoingNetMessage>(list.Count); double newEarliest = double.MaxValue; foreach (OutgoingNetMessage msg in list) { double resend = msg.m_nextResend; if (now > resend) { // Re-enqueue message in unsent list m_owner.LogVerbose("Resending " + msg + " now: " + NetTime.ToMillis(now) + " nextResend: " + NetTime.ToMillis(msg.m_nextResend), this); m_statistics.CountMessageResent(msg.m_type); m_unsentMessages.Enqueue(msg); } else { newlist.Add(msg); } if (resend < newEarliest) { newEarliest = resend; } } m_storedMessages[i] = newlist; m_earliestResend[i] = newEarliest; } } }
internal void ResendMessages(double now) { for (int i = 0; i < m_storedMessages.Length; i++) { List <OutgoingNetMessage> list = m_storedMessages[i]; if (list == null || list.Count < 1) { continue; } if (now > m_earliestResend[i]) { double newEarliest = double.MaxValue; foreach (OutgoingNetMessage msg in list) { double resend = msg.m_nextResend; if (now > resend) { // Re-enqueue message in unsent list m_owner.LogVerbose("Resending " + msg + " now: " + NetTime.ToMillis(now) + " nextResend: " + NetTime.ToMillis(msg.m_nextResend), this); m_statistics.CountMessageResent(msg.m_type); m_removeList.Add(msg); m_unsentMessages.Enqueue(msg); } if (resend < newEarliest) { newEarliest = resend; } } m_earliestResend[i] = newEarliest; foreach (OutgoingNetMessage msg in m_removeList) { list.Remove(msg); } m_removeList.Clear(); } } }
internal void ReceivedPong(float now, int pongNumber) { if (pongNumber != m_sentPingNumber) { m_peer.LogVerbose("Ping/Pong mismatch; dropped message?"); return; } m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout; float rtt = now - m_sentPingTime; NetException.Assert(rtt >= 0); if (m_averageRoundtripTime < 0) { m_averageRoundtripTime = rtt; // initial estimate m_peer.LogDebug("Initiated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime)); } else { m_averageRoundtripTime = (m_averageRoundtripTime * 0.7f) + (float)(rtt * 0.3f); m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime)); } // update resend delay for all channels float resendDelay = GetResendDelay(); foreach (var chan in m_sendChannels) { var rchan = chan as NetReliableSenderChannel; if (rchan != null) { rchan.m_resendDelay = resendDelay; } } m_peer.LogVerbose("Timeout deadline pushed to " + m_timeoutDeadline); }
internal void HandleAckMessage(double now, IncomingNetMessage ackMessage) { int len = ackMessage.m_data.LengthBytes; if ((len % 3) != 0) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; length must be multiple of 3; it's " + len, this, ackMessage.m_senderEndPoint); } return; } for (int i = 0; i < len; i += 3) //for each channel + seq nbr in ACK { NetChannel chan = (NetChannel)ackMessage.m_data.ReadByte(); int seqNr = ackMessage.m_data.ReadUInt16(); // LogWrite("Acknowledgement received: " + chan + "|" + seqNr); m_statistics.CountAcknowledgesReceived(1); // remove saved message int relChanNr = (int)chan - (int)NetChannel.ReliableUnordered; if (relChanNr < 0) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; indicated netchannel " + chan, this, ackMessage.m_senderEndPoint); } continue; } List <OutgoingNetMessage> list = m_storedMessages[relChanNr]; if (list != null) { int cnt = list.Count; if (cnt > 0) { for (int j = 0; j < cnt; j++) //for each stored message on channel { OutgoingNetMessage msg = list[j]; if (msg.m_sequenceNumber == seqNr) //find correct message { // if (msg.m_numSent >= 10) UnityEngine.Debug.Log(now + ": Ack for " + msg.m_numSent + ": " + NetUtility.BytesToHex(msg.m_data.ToArray())); //LogWrite("Removed stored message: " + msg); list.RemoveAt(j); // reduce estimated amount of packets on wire //CongestionCountAck(msg.m_packetNumber); // fire receipt if (msg.m_receiptData != null) { m_owner.LogVerbose("Got ack, removed from storage: " + msg + " firing receipt; " + msg.m_receiptData, this); m_owner.FireReceipt(this, msg.m_receiptData); } else { m_owner.LogVerbose("Got ack, removed from storage: " + msg, this); } // recycle msg.m_data.m_refCount--; if (msg.m_data.m_refCount <= 0) { m_owner.RecycleBuffer(msg.m_data); // time to recycle buffer } msg.m_data = null; //m_owner.m_messagePool.Push(msg); #if !NO_NAK if (j > 0) { int k; for (k = 0; k < j; k++) //for each message stored prior to the one matching seq nbr { var m = list[k]; if (m.m_sequenceNumber > seqNr) { break; } // Re-enqueue message in unsent list m_owner.LogVerbose("Implicit NAK Resending " + m + " now: " + NetTime.NowInMillis + " nextResend: " + NetTime.ToMillis(m.m_nextResend), this); m_statistics.CountMessageResent(m.m_type); m_unsentMessages.Enqueue(m); } list.RemoveRange(0, k); } #endif break; //exit stored message loop since this was the message corresponding to seq nbr //now returning to next sequence number in ACK packet } } } } } // recycle NetBuffer rb = ackMessage.m_data; rb.m_refCount = 0; // ack messages can't be used by more than one message ackMessage.m_data = null; m_owner.RecycleBuffer(rb); //m_owner.m_messagePool.Push(ackMessage); }
public string GetStatisticsString(NetConnection connection) { double now = NetTime.Now; string retval = "--- Application wide statistics ---" + Environment.NewLine + "Heartbeats: " + this.HeartbeatAverageFrequency + "/sec" + Environment.NewLine + "Packets sent: " + m_statistics.PacketsSent + " (" + m_statistics.GetPacketsSentPerSecond(now).ToString("N1") + "/sec)" + Environment.NewLine + "Bytes sent: " + m_statistics.BytesSent + " (" + m_statistics.GetBytesSentPerSecond(now).ToString("N1") + "/sec)" + Environment.NewLine + "Packets received: " + m_statistics.PacketsReceived + " (" + m_statistics.GetPacketsReceivedPerSecond(now).ToString("N1") + "/sec)" + Environment.NewLine + "Bytes received: " + m_statistics.BytesReceived + " (" + m_statistics.GetBytesReceivedPerSecond(now).ToString("N1") + "/sec)" + Environment.NewLine; if (m_simulatedLoss > 0.0f) { retval = retval + "Simulated dropped packets: " + m_statistics.SimulatedDroppedPackets + Environment.NewLine + "Simulated delayed packets: " + m_delayedPackets.Count + Environment.NewLine ; } if (connection != null) { NetConnectionStatistics connStats = connection.Statistics; retval += Environment.NewLine + "--- Connection wide statistics ---" + Environment.NewLine + "Status: " + connection.Status + Environment.NewLine + "Received -----" + Environment.NewLine + "Messages: " + connStats.GetMessagesReceived(true) + "/sec)" + Environment.NewLine + " User/type: " + connStats.GetUserUnreliableReceived() + "/" + connStats.GetUserSequencedReceived() + "/" + connStats.GetUserReliableUnorderedReceived() + "/" + connStats.GetUserOrderedReceived() + Environment.NewLine + "Packets: " + connStats.PacketsReceived + Environment.NewLine + "Bytes: " + connStats.GetBytesReceived(true) + Environment.NewLine + "Acks: " + connStats.AcknowledgesReceived + Environment.NewLine + Environment.NewLine + "Sent ------" + Environment.NewLine + "Messages: " + connStats.GetMessagesSent(true) + " (excl. resends " + (connStats.GetMessagesSent(true) - connStats.MessagesResent) + ")" + Environment.NewLine + " User/type: " + connStats.GetUserUnreliableSent() + "/" + connStats.GetUserSequencedSent() + "/" + connStats.GetUserReliableUnorderedSent() + "/" + connStats.GetUserOrderedSent() + Environment.NewLine + "Packets: " + connStats.PacketsSent + Environment.NewLine + "Bytes: " + connStats.GetBytesSent(true) + Environment.NewLine + "Acks: " + connStats.AcknowledgesSent + Environment.NewLine + Environment.NewLine + "Resent: " + connStats.MessagesResent + Environment.NewLine + "Duplicates: " + connStats.DuplicateMessagesRejected + Environment.NewLine + "Dropped sequenced: " + connStats.SequencedMessagesRejected + Environment.NewLine + Environment.NewLine + "Unsent messages: " + connection.m_unsentMessages.Count + Environment.NewLine + "Stored messages: " + connStats.CurrentlyStoredMessagesCount + Environment.NewLine + "Withheld messages: " + connStats.CurrentlyWithheldMessagesCount + Environment.NewLine + "Average roundtrip: " + NetTime.ToMillis(connection.AverageRoundtripTime) + " ms" + Environment.NewLine //"Window: " + connection.GetEstimatedPacketsOnWire() + " of " + connection.m_congestionWindowSize ; } return(retval); }
/// <summary> /// Called to bind to socket and start heartbeat thread. /// The socket will be bound to listen on any network interface unless the <see cref="NetConfiguration.Address"/> explicitly specifies an interface. /// </summary> public void Start() { if (m_isBound) { return; } // TODO: this check should be done somewhere earlier, in uLink preferably. if (m_config.StartPort > m_config.EndPort) { throw new NetException("The start port (" + m_config.StartPort + ") must be less or equal to the end port (" + m_config.EndPort + ")"); } //by WuNan @2016/09/28 14:26:22 var bindIP = String.IsNullOrEmpty(m_config.AddressStr) ? #if (UNITY_IOS || UNITY_TVOS) && !UNITY_EDITOR (uLink.NetworkUtility.IsSupportIPv6() ? IPAddress.IPv6Any : IPAddress.Any) : NetUtility.Resolve(m_config.AddressStr); #else IPAddress.Any : NetUtility.Resolve(m_config.AddressStr); #endif Log.Debug(LogFlags.Socket, "Creating non-blocking UDP socket"); var sock = NetworkSocket.Create(m_config.SocketType); Log.Debug(LogFlags.Socket, "Successfully created Socket"); for (int port = m_config.StartPort; port <= m_config.EndPort; port++) { try { sock.Bind(new NetworkEndPoint(bindIP, port)); m_isBound = true; break; } catch (SocketException ex) { Log.Debug(LogFlags.Socket, "Failed to bind to specific port ", port, ": ", ex); if (port == m_config.EndPort) { try { sock.Close(0); } catch { } throw new NetException("Failed to bind to port range " + m_config.StartPort + "-" + m_config.EndPort, ex); } } } m_socket = sock; LogWrite("Listening on " + m_socket.listenEndPoint); if (m_config.ReceiveBufferSize != 0) { try { m_socket.receiveBufferSize = m_config.ReceiveBufferSize; m_config.ReceiveBufferSize = m_socket.receiveBufferSize; // make sure we have the actual size } catch (Exception ex) { Log.Warning(LogFlags.Socket, "Unable to set socket ", SocketOptionName.ReceiveBuffer, " size to ", m_config.ReceiveBufferSize, ": ", ex); } } else { try { m_config.ReceiveBufferSize = m_socket.receiveBufferSize; Log.Debug(LogFlags.Socket, "Socket ", SocketOptionName.ReceiveBuffer, " is set to OS-specific default ", m_config.ReceiveBufferSize); } catch (Exception ex) { Log.Warning(LogFlags.Socket, "Unable to get socket ", SocketOptionName.ReceiveBuffer, ": ", ex); } } if (m_config.SendBufferSize != 0) { try { m_socket.sendBufferSize = m_config.SendBufferSize; m_config.SendBufferSize = m_socket.sendBufferSize; // make sure we have the actual size } catch (Exception ex) { Log.Warning(LogFlags.Socket, "Unable to set socket ", SocketOptionName.SendBuffer, " size to ", m_config.SendBufferSize, ": ", ex); } } else { try { m_config.SendBufferSize = m_socket.sendBufferSize; Log.Debug(LogFlags.Socket, "Socket ", SocketOptionName.SendBuffer, " is set to OS-specific default ", m_config.SendBufferSize); } catch (Exception ex) { Log.Warning(LogFlags.Socket, "Unable to get socket ", SocketOptionName.SendBuffer, ": ", ex); } } m_receiveBuffer.EnsureBufferSizeInBytes(m_config.ReceiveBufferSize); m_sendBuffer.EnsureBufferSizeInBytes(m_config.SendBufferSize); // TODO: ugly hack to determine if server if (this is NetServer) { m_socket.Listen(m_config.MaxConnections); } // display simulated networking conditions in debug log if (m_simulatedLoss > 0.0f) { LogWrite("Simulating " + (m_simulatedLoss * 100.0f) + "% loss"); } if (m_simulatedMinimumLatency > 0.0f || m_simulatedLatencyVariance > 0.0f) { LogWrite("Simulating " + ((int)(m_simulatedMinimumLatency * 1000.0f)) + " - " + NetTime.ToMillis(m_simulatedMinimumLatency + m_simulatedLatencyVariance) + " ms roundtrip latency"); } if (m_simulatedDuplicateChance > 0.0f) { LogWrite("Simulating " + (m_simulatedDuplicateChance * 100.0f) + "% chance of packet duplication"); } if (m_config.m_throttleBytesPerSecond > 0) { LogWrite("Throttling to " + m_config.m_throttleBytesPerSecond + " bytes per second"); } m_isBound = true; m_shutdownComplete = false; m_statistics.Reset(); }
public override string ToString() { return("roundtrip " + NetTime.ToMillis(roundtripTime) + " ms, offset " + timeOffset + " s"); }