/// <summary> Detects acknowledged and lost messages. </summary> private void AckDelivered(NetHeader header) { for (int i = 0; i < sendWindow.Count; i++) { int seqDist = NetMath.SeqDistance(sendWindow[i], header.AckSequence); // This AckSequence is older than the sendWindow's, not useful: if (seqDist > 0) { break; } // AckHistory has rolled over without acking this message; Ordered reliable is broken: if (seqDist <= -64) { Connection.Disconnect(); } // If the seqDistance corresponds to a true bit in the AckHistory, message delivered/acked: else if (IsAcked(header.AckHistory, seqDist)) { MessageDelivered(i, header); i--; // Since the sendWindow count will decrease, the index needs to be adjusted. } // The seqDist is still within the send window, but if too much time has passed, assume lost: else if (NetTime.Milliseconds() - sendWindowTime[i] > 333) { MessageLost(i); } } }
/// <summary> Handles connecting status. Tracks attempted connections and (re)sends connection data. </summary> private void Connect(IPEndPoint ep, NetStream approvalData) { if (!connectingEndpoints.Contains(ep)) { // We aren't currently trying to connect to this endpoint, add to lists: connectingEndpoints.Add(ep); connectingTimes.Add(NetTime.Milliseconds()); connectingData.Add(approvalData); connectingRetriesRemaining.Add(4); NetLog.Info("Connecting to: " + ep); } else { // We are already trying to connect, update attempt data: int index = connectingEndpoints.IndexOf(ep); connectingRetriesRemaining[index]--; if (connectingRetriesRemaining[index] <= 0) { // Retried max amount of times, notify failure: RemoveFromConnecting(ep, false); return; } connectingTimes[index] = NetTime.Milliseconds(); NetLog.Info("Retrying connection to: " + ep); } // Send the connection request data to the endpoint: SendStream(ep, approvalData); }
/// <summary> Resends a lost message, updates its sent time, and increments the Resends stat. </summary> private void MessageLost(int index) { Resends++; NetStream strm = reliableWindow[sendWindow[index]]; sendWindowTime[index] = NetTime.Milliseconds(); Connection.Socket.SendStream(Connection, strm); }
/// <summary> Timeouts, disconnects, heartbeats, forced-acks, etc. need to be performed at end of frame. </summary> private void EndFrameTasks() { uint currentTime = NetTime.Milliseconds(); for (int i = Connections.Count - 1; i >= 0; i--) { Connections[i].EndOfFrame(currentTime); } CheckForTimeouts(); }
internal bool FlushStream() { if (sendStream == null) { return(false); } Connection.Socket.SendStream(Connection, sendStream); LastReliableSent = NetTime.Milliseconds(); Connection.LastSendTime = LastReliableSent; sendStream = null; return(true); }
internal NetConnection(bool isServer, bool isPeer, NetSocket socket, IPEndPoint endpoint, uint id = 0) { IsServer = isServer; IsPeer = isPeer; Socket = socket; Endpoint = endpoint; Id = id; Unreliable = new NetChannelUnreliable(this); Reliable = new NetChannelReliable(this); LastReceiveTime = LastSendTime = Created = NetTime.Milliseconds(); AddToGroup(0); }
/// <summary> Iterates through pending connections and retries any timeouts. </summary> private void CheckForTimeouts() { if (connectingEndpoints.Count == 0) { return; } for (int i = connectingEndpoints.Count - 1; i >= 0; i--) { if (NetTime.Milliseconds() - connectingTimes[i] > 2000) { Connect(connectingEndpoints[i], connectingData[i]); } } }
/// <summary> Removes acked messages from the send window, releases the stream, updates connection ping, and /// increments the Delivered stat. </summary> private void MessageDelivered(int index, NetHeader header) { Delivered++; NetStream strm = reliableWindow[sendWindow[index]]; if (header.AckTime > 0) { Connection.UpdatePing(NetTime.Milliseconds(), sendWindowTime[index], header.AckTime); } reliableWindow.Remove(sendWindow[index]); sendWindow.RemoveAt(index); sendWindowTime.RemoveAt(index); strm.Release(); }
/// <summary> Prepares the outgoing reliable stream: Writes the reliable bit & reliable header, /// sets stream parameters, and updates send stats. </summary> private NetHeader WriteHeader() { sendStream.Connection = Connection; sendStream.Socket = Connection.Socket; sendStream.WriteBool(true); var header = NetHeader.Create(this, NetTime.Milliseconds()); header.ToStream(sendStream); ReceivedSinceLastSend = 0; Sent++; return(header); }
/// <summary> Updates receive time and forwards stream to proper channel for deserialization. </summary> internal void ReceiveStream(NetStream stream) { stream.Connection = this; LastReceiveTime = NetTime.Milliseconds(); bool reliable = stream.ReadBool(); if (!reliable) { Unreliable.DeserializeStream(stream); } else { Reliable.RouteIncomingStream(stream); } }
/// <summary> /// Starts the socket using the supplied endpoint. /// If the port is taken, the given port will be incremented to a free port. /// </summary> public void StartSocket(IPEndPoint endpoint) { Self = new NetConnection(false, false, this, endpoint); socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); try { const uint IOC_IN = 0x80000000; const uint IOC_VENDOR = 0x18000000; uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; socket.IOControl((int)SIO_UDP_CONNRESET, new[] { Convert.ToByte(false) }, null); } catch { NetLog.Warning("Failed to set control code for ignoring ICMP port unreachable."); } socket.ReceiveBufferSize = 4194304; if (socket.ReceiveBufferSize != 4194304) { NetLog.Warning("ReceiveBufferSize restricted by OS."); } socket.SendBufferSize = 1048576; socket.Blocking = false; try { socket.Bind(endpoint); } catch (SocketException e) { if (e.ErrorCode == 10048) { var newEp = new IPEndPoint(endpoint.Address, endpoint.Port + 1); NetLog.Warning("Port in use. Incrementing and retrying..."); StartSocket(newEp); } else { NetLog.Error(e.Message); } return; } NetLog.Info(NetTime.StartDateTime() + " | Socket Started. Bound to: " + endpoint); NetTime.Milliseconds(); if (ProtocolAuthority) { Rpc.AssignLocalRpcs(); } Events.SocketStart(); }
/// <summary> Checks all sent and unacked messages for timeout. Messages exceeding timeout are considered lost. </summary> internal void CheckTimeouts(uint currentTime) { if (currentTime - lastCheckTime < 333) { return; } lastCheckTime = currentTime; for (int i = 0; i < sendWindow.Count; i++) { if (NetTime.Milliseconds() - sendWindowTime[i] > 333) { MessageLost(i); } } }
/// <summary> Acknowledges and updates the remote sequence. </summary> private void AckReceived(NetHeader header) { int newestDist = NetMath.SeqDistance(header.ObjSequence, NewestRemoteSequence); // If the sequence is newest, shift the buffer and apply ack bit: if (newestDist > 0) { AckHistory = (AckHistory << newestDist) | 1UL; NewestRemoteSequence = header.ObjSequence; } // Else, shift the ack bit and apply to buffer: else { AckHistory |= 1UL << -newestDist; } LastReceiveTime = NetTime.Milliseconds(); ReceivedSinceLastSend++; Received++; }