public QosJob(IList <QosServer> qosServers, string title) : this() { // Copy the QoS Servers into the job, converting all the IP/Port to NetworkEndPoint and DateTime to ticks. m_QosServers = new NativeArray <InternalQosServer>(qosServers?.Count ?? 0, Allocator.Persistent); if (qosServers != null) { for (int i = 0; i < m_QosServers.Length; ++i) { // Indexing NativeArray returns temporary values, so can't edit in place. m_QosServers[i] = new InternalQosServer(qosServers[i].ipv4, qosServers[i].port, qosServers[i].BackoffUntilUtc); } } m_VisitedQosServers = new NativeArray <ulong>(qosServers?.Count ?? 0, Allocator.Persistent); // Indexes into QosResults correspond to indexes into qosServers/m_QosServers QosResults = new NativeArray <QosResult>(m_QosServers.Length, Allocator.Persistent); // Convert the title to a NativeArray of bytes (since everything in the job has to be a value-type) byte[] utf8Title = Encoding.UTF8.GetBytes(title); m_TitleBytesUtf8 = new NativeArray <byte>(utf8Title.Length, Allocator.Persistent); m_TitleBytesUtf8.CopyFrom(utf8Title); }
public void Execute() { if (m_QosServers.Length == 0) { return; // Nothing to do. } m_JobExpireTimeUtc = DateTime.UtcNow.AddMilliseconds(TimeoutMs); // Create the local socket int errorcode = 0; (m_Socket, errorcode) = CreateAndBindSocket(); if (m_Socket == -1 || errorcode != 0) { // Can't run the job Debug.LogError("Failed to create and bind the local socket for QoS Check"); } else { m_Identifier = (ushort)new Random().Next(ushort.MinValue, ushort.MaxValue); for (int i = 0; i < m_QosServers.Length; ++i) { QosResult result = QosResults[i]; InternalQosServer server = m_QosServers[i]; if (QosHelper.ExpiredUtc(m_JobExpireTimeUtc)) { Debug.LogWarning($"Ran out of time to finish remaining QoS Check for endpoint {i}."); break; } // If we've already visited this server, just copy those results here. if (QosServerVisited(server.Id)) { if (TryCopyResult(server.Id, ref result) == false) { Debug.LogError($"Visited server must have a previous result available"); break; } } else if (DateTime.UtcNow > server.BackoffUntilUtc) // Only contact this server if we are allowed { // For each iteration of the loop, give the remaining endpoints an equal fraction of the remaining // overall job time. For example if there are five endpoints that each get 1000ms (5000ms total), // and the first endpoint finishes in 200ms, the remaining endpoints will get 1200ms to complete // (4800ms remaining / 4 endpoints = 1200ms/endpoint). double allottedTimeMs = QosHelper.RemainingUtc(m_JobExpireTimeUtc).TotalMilliseconds / (m_QosServers.Length - i); DateTime startTimeUtc = DateTime.UtcNow; DateTime expireTimeUtc = DateTime.UtcNow.AddMilliseconds(allottedTimeMs); #if UNITY_EDITOR || DEVELOPMENT_BUILD Debug.Log($"QoS Check {i} gets {(expireTimeUtc - DateTime.UtcNow).TotalMilliseconds:F0}ms to complete."); #endif ++m_Identifier; int err = SendQosRequests(server.RemoteEndpoint, m_Identifier, expireTimeUtc, ref result); if (err != 0) { Debug.LogError($"Error {err} sending QoS requests. Will attempt to receive responses anyway."); } err = RecvQosResponses(server.RemoteEndpoint, m_Identifier, expireTimeUtc, ref result); if (err != 0) { Debug.LogError($"Error {err} receiving QoS responses. Will attempt to continue anyway."); } Debug.Log($"Received {result.ResponsesReceived}/{result.RequestsSent} responses from endpoint {i} in {(DateTime.UtcNow - startTimeUtc).TotalMilliseconds:F0}ms"); // Mark this server as visited SetQosServerVisited(server.Id); } else { Debug.LogWarning($"Did not contact endpoint {i} due to backoff restrictions."); } // Save the result (even if we didn't contact the server) QosResults[i] = result; } } NativeBindings.network_close(ref m_Socket, ref errorcode); }