public override bool Decrypt(NetIncomingMessage msg) { int unEncLenBits = (int)msg.ReadUInt32(); var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4); var cs = GetDecryptStream(ms); var result = m_peer.GetStorage(unEncLenBits); cs.Read(result, 0, NetUtility.BytesToHoldBits(unEncLenBits)); //cs.Close(); cs.Dispose(); // TODO: recycle existing msg msg.m_data = result; msg.m_bitLength = unEncLenBits; return(true); }
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // seqNr is the actual nr received public override NetSocketResult ReceiveAcknowledge(TimeSpan now, int seqNr) { if (!_doFlowControl) { // we have no use for acks on this channel since we don't respect the window anyway _connection.Peer.LogWarning(new NetLogMessage(NetLogCode.SuppressedUnreliableAck, endPoint: _connection)); return(new NetSocketResult(true, false)); } // late (dupe), on time or early ack? int relate = NetUtility.RelativeSequenceNumber(seqNr, _windowStart); if (relate < 0) { //m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr); return(new NetSocketResult(true, false)); // late/duplicate ack } if (relate == 0) { //m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr); // ack arrived right on time LidgrenException.Assert(seqNr == _windowStart); _receivedAcks[_windowStart] = false; _windowStart = NetUtility.PowOf2Mod(_windowStart + 1, NetConstants.SequenceNumbers); return(new NetSocketResult(true, false)); } // Advance window to this position _receivedAcks[seqNr] = true; while (_windowStart != seqNr) { _receivedAcks[_windowStart] = false; _windowStart = NetUtility.PowOf2Mod(_windowStart + 1, NetConstants.SequenceNumbers); } return(new NetSocketResult(true, false)); }
internal override void ReceiveMessage(NetIncomingMessage message) { int nr = message.m_sequenceNumber; int relate = NetUtility.RelativeSequenceNumber(nr, m_windowStart); // ack no matter what m_connection.QueueAck(message.m_receivedMessageType, nr); if (relate == 0) { // Log("Received message #" + message.SequenceNumber + " right on time"); // // excellent, right on time // AdvanceWindow(); m_peer.ReleaseMessage(message); return; } if (relate < 0) { m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING LATE or DUPE"); return; } // relate > 0 = early message if (relate > m_windowSize) { // too early message! m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart); return; } // ok m_windowStart = (m_windowStart + relate) % NetConstants.NumSequenceNumbers; m_peer.ReleaseMessage(message); return; }
// may be on user thread private NetSenderChannelBase CreateSenderChannel(NetMessageType tp) { NetSenderChannelBase chan; lock (m_sendChannels) { NetDeliveryMethod method = NetUtility.GetDeliveryMethod(tp); int sequenceChannel = (int)tp - (int)method; int channelSlot = (int)method - 1 + sequenceChannel; if (m_sendChannels[channelSlot] != null) { // we were pre-empted by another call to this method chan = m_sendChannels[channelSlot]; } else { switch (method) { case NetDeliveryMethod.Unreliable: case NetDeliveryMethod.UnreliableSequenced: chan = new NetUnreliableSenderChannel(this, NetUtility.GetWindowSize(method), method); break; case NetDeliveryMethod.ReliableOrdered: chan = new NetReliableSenderChannel(this, NetUtility.GetWindowSize(method)); break; case NetDeliveryMethod.Unknown: case NetDeliveryMethod.ReliableSequenced: case NetDeliveryMethod.ReliableUnordered: default: chan = new NetReliableSenderChannel(this, NetUtility.GetWindowSize(method)); break; } m_sendChannels[channelSlot] = chan; } } return(chan); }
private NetReceiverChannelBase CreateReceiverChannel(NetMessageType tp) { m_peer.VerifyNetworkThread(); // create receiver channel NetReceiverChannelBase chan; NetDeliveryMethod method = NetUtility.GetDeliveryMethod(tp); switch (method) { case NetDeliveryMethod.Unreliable: chan = new NetUnreliableUnorderedReceiver(this); break; case NetDeliveryMethod.ReliableOrdered: chan = new NetReliableOrderedReceiver(this, NetConstants.ReliableOrderedWindowSize); break; case NetDeliveryMethod.UnreliableSequenced: chan = new NetUnreliableSequencedReceiver(this); break; case NetDeliveryMethod.ReliableUnordered: chan = new NetReliableUnorderedReceiver(this, NetConstants.ReliableOrderedWindowSize); break; case NetDeliveryMethod.ReliableSequenced: chan = new NetReliableSequencedReceiver(this, NetConstants.ReliableSequencedWindowSize); break; default: throw new NetException("Unhandled NetDeliveryMethod!"); } int channelSlot = (int)tp - 1; NetException.Assert(m_receiveChannels[channelSlot] == null); m_receiveChannels[channelSlot] = chan; return(chan); }
internal void Discover(NetPeer peer) { #if !DOTNETCORE string str = "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "ST:upnp:rootdevice\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:3\r\n\r\n"; m_discoveryResponseDeadline = NetTime.Now + 6.0; // arbitrarily chosen number, router gets 6 seconds to respond m_status = UPnPStatus.Discovering; byte[] arr = System.Text.Encoding.UTF8.GetBytes(str); m_peer.LogDebug("Attempting UPnP discovery"); peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); peer.RawSend(arr, 0, arr.Length, new NetEndPoint(NetUtility.GetBroadcastAddress(), 1900)); peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false); #endif }
/// <summary> /// Send a message to an unconnected host. /// </summary> public void SendUnconnectedMessage(NetOutgoingMessage message, ReadOnlySpan <char> host, int port) { if (message == null) { throw new ArgumentNullException(nameof(message)); } message.AssertNotSent(nameof(message)); AssertValidUnconnectedLength(message); IPAddress?address = NetUtility.Resolve(host); if (address == null) { throw new LidgrenException("Failed to resolve " + host.ToString()); } IPEndPoint recipient = new(address, port); SendUnconnectedMessageCore(message, recipient); }
public override bool Decrypt(NetIncomingMessage msg) { int unEncLenBits = (int)msg.ReadUInt32(); var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4); var cs = new CryptoStream(ms, m_algorithm.CreateDecryptor(), CryptoStreamMode.Read); var byteLen = NetUtility.BytesToHoldBits(unEncLenBits); var result = m_peer.GetStorage(byteLen); cs.Read(result, 0, byteLen); cs.Close(); // TODO: recycle existing msg msg.m_data = result; msg.m_bitLength = unEncLenBits; msg.m_readPosition = 0; return(true); }
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // seqNr is the actual nr received internal override void ReceiveAcknowledge(double now, int seqNr) { if (m_doFlowControl == false) { // we have no use for acks on this channel since we don't respect the window anyway m_connection.m_peer.LogVerbose("SuppressUnreliableUnorderedAcks sender/receiver mismatch!"); return; } // late (dupe), on time or early ack? int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart); if (relate < 0) { //m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr); return; // late/duplicate ack } if (relate == 0) { //m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr); // ack arrived right on time NetException.Assert(seqNr == m_windowStart); m_receivedAcks[m_windowStart] = false; m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; return; } // Advance window to this position m_receivedAcks[seqNr] = true; while (m_windowStart != seqNr) { m_receivedAcks[m_windowStart] = false; m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; } }
/// <summary> /// Add a forwarding rule to the router using UPnP /// </summary> public bool ForwardPort(int port, string description) { if (!CheckAvailability()) { return(false); } IPAddress mask; var client = NetUtility.GetMyAddress(out mask); if (client == null) { return(false); } try { XmlDocument xdoc = SOAPRequest(m_serviceUrl, "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:" + m_serviceName + ":1\">" + "<NewRemoteHost></NewRemoteHost>" + "<NewExternalPort>" + port.ToString() + "</NewExternalPort>" + "<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" + "<NewInternalPort>" + port.ToString() + "</NewInternalPort>" + "<NewInternalClient>" + client.ToString() + "</NewInternalClient>" + "<NewEnabled>1</NewEnabled>" + "<NewPortMappingDescription>" + description + "</NewPortMappingDescription>" + "<NewLeaseDuration>0</NewLeaseDuration>" + "</u:AddPortMapping>", "AddPortMapping"); m_peer.LogDebug("Sent UPnP port forward request"); System.Threading.Thread.Sleep(50); } catch (Exception ex) { m_peer.LogWarning("UPnP port forward failed: " + ex.Message); return(false); } return(true); }
/// <summary> /// Encodes a <see cref="NetBitArray"/> to this buffer. /// </summary> public static void Write(this IBitBuffer buffer, NetBitArray bitArray) { buffer.WriteVar(bitArray.Length); ReadOnlySpan <uint> values = bitArray.GetBuffer().Span; int bitsLeft = NetUtility.PowOf2Mod(bitArray.Length, NetBitArray.BitsPerElement); if (bitsLeft == 0) { for (int i = 0; i < values.Length; i++) { buffer.Write(values[i]); } } else { for (int i = 0; i < values.Length - 1; i++) { buffer.Write(values[i]); } uint last = values[^ 1];
/// <summary> /// Add a forwarding rule to the router using UPnP /// </summary> public bool ForwardPort(int port, string description) { //if (!CheckAvailability()) // return false; IPAddress mask; var client = NetUtility.GetMyAddress(out mask); if (client == null) { client = NetUtility.GetBroadcastAddress(); } //return false; try { SOAPRequest(m_serviceUrl, "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:" + m_serviceName + ":1\">" + "<NewRemoteHost></NewRemoteHost>" + "<NewExternalPort>" + port.ToString() + "</NewExternalPort>" + "<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper(System.Globalization.CultureInfo.InvariantCulture) + "</NewProtocol>" + "<NewInternalPort>" + port.ToString() + "</NewInternalPort>" + "<NewInternalClient>" + client.ToString() + "</NewInternalClient>" + "<NewEnabled>1</NewEnabled>" + "<NewPortMappingDescription>" + description + "</NewPortMappingDescription>" + "<NewLeaseDuration>0</NewLeaseDuration>" + "</u:AddPortMapping>", "AddPortMapping"); m_peer.LogDebug("Sent UPnP port forward request"); NetUtility.Sleep(50); } catch (Exception ex) { m_peer.LogWarning("UPnP port forward failed: " + ex.Message); return(false); } return(true); }
/// <summary> /// Add a forwarding rule to the router using UPnP /// </summary> public bool ForwardPort(int port, string description) { if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis)) { return(false); } IPAddress mask; var client = NetUtility.GetMyAddress(out mask); if (client == null) { return(false); } try { SOAPRequest(m_serviceUrl, "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" + "<NewRemoteHost></NewRemoteHost><NewExternalPort>" + port.ToString() + "</NewExternalPort>" + "<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" + "<NewInternalPort>" + port.ToString() + "</NewInternalPort>" + "<NewInternalClient>" + client.ToString() + "</NewInternalClient>" + "<NewEnabled>1</NewEnabled>" + "<NewPortMappingDescription>" + description + "</NewPortMappingDescription>" + "<NewLeaseDuration>0</NewLeaseDuration>" + "</u:AddPortMapping>", "AddPortMapping"); m_peer.LogDebug("Sent UPnP port forward request"); System.Threading.Thread.Sleep(50); } catch (Exception ex) { m_peer.LogWarning("UPnP port forward failed: " + ex.Message); return(false); } return(true); }
/// <summary> /// Reads all fields with the specified binding of the object in alphabetical order using reflection /// </summary> public void ReadAllProperties(object target, BindingFlags flags) { if (target == null) { throw new ArgumentNullException("target"); } Type tp = target.GetType(); PropertyInfo[] fields = tp.GetProperties(flags); NetUtility.SortMembersList(fields); foreach (PropertyInfo fi in fields) { object value; // find read method MethodInfo readMethod; if (s_readMethods.TryGetValue(fi.PropertyType, out readMethod)) { // read value value = readMethod.Invoke(this, null); // set the value //#if UNITY_WEBPLAYER || UNITY_4_5 // var setMethod = fi.GetSetMethod(); //#else //var setMethod = fi.SetMethod; //#endif var setMethod = fi.GetSetMethod(); if (setMethod != null) { setMethod.Invoke(target, new object[] { value }); } } } }
/// <summary> /// Reads a string /// </summary> public string ReadString() { int byteLen = (int)ReadVariableUInt32(); if (byteLen == 0) { return(String.Empty); } NetUtility.Assert(m_bitLength - m_readPosition >= (byteLen * 8), c_readOverflowError); if ((m_readPosition & 7) == 0) { // read directly string retval = System.Text.Encoding.UTF8.GetString(Data, m_readPosition >> 3, byteLen); m_readPosition += (8 * byteLen); return(retval); } byte[] bytes = ReadBytes(byteLen); return(System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length)); }
private void InitializeNetwork() { lock (m_initializeLock) { m_configuration.Lock(); if (m_status == NetPeerStatus.Running) return; if (m_configuration.m_enableUPnP) m_upnp = new NetUPnP(this); InitializePools(); m_releasedIncomingMessages.Clear(); m_unsentUnconnectedMessages.Clear(); m_handshakes.Clear(); // bind to socket BindSocket(false); m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize]; m_sendBuffer = new byte[m_configuration.SendBufferSize]; m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error); m_readHelperMessage.m_data = m_receiveBuffer; byte[] macBytes = NetUtility.GetMacAddressBytes(); var boundEp = m_socket.LocalEndPoint as NetEndPoint; byte[] epBytes = BitConverter.GetBytes(boundEp.GetHashCode()); byte[] combined = new byte[epBytes.Length + macBytes.Length]; Array.Copy(epBytes, 0, combined, 0, epBytes.Length); Array.Copy(macBytes, 0, combined, epBytes.Length, macBytes.Length); m_uniqueIdentifier = BitConverter.ToInt64(NetUtility.ComputeSHAHash(combined), 0); m_status = NetPeerStatus.Running; } }
/// <summary> /// Reads all fields with the specified binding of the object in alphabetical order using reflection /// </summary> public void ReadAllProperties(object target, BindingFlags flags) { if (target == null) { throw new ArgumentNullException("target"); } Type tp = target.GetType(); PropertyInfo[] fields = tp.GetProperties(flags); NetUtility.SortMembersList(fields); foreach (PropertyInfo fi in fields) { object value; // find read method MethodInfo readMethod; if (s_readMethods.TryGetValue(fi.PropertyType, out readMethod)) { // read value value = readMethod.Invoke(this, null); // set the value #if UNITY_WEBPLAYER || UNITY_4_5 var setMethod = fi.GetSetMethod(); #else //This must be changed to the above as this cannot be done in .Net 3.5 //Reflection is not too fancy in 3.5 //Previous: var setMethod = fi.SetMethod; var setMethod = fi.GetSetMethod(); #endif if (setMethod != null) { setMethod.Invoke(target, new object[] { value }); } } } }
/// <summary> /// Binds to socket and spawns the networking thread /// </summary> public void Start() { if (m_status != NetPeerStatus.NotRunning) { // already running! Just ignore... LogVerbose("Start() called on already running NetPeer - ignoring."); return; } m_status = NetPeerStatus.Starting; // fix network thread name if (m_configuration.NetworkThreadName == "Lidgren network thread") { int pc = Interlocked.Increment(ref s_initializedPeersCount); m_configuration.NetworkThreadName = "Lidgren network thread " + pc.ToString(); } InitializeNetwork(); // start network thread m_networkThread = new Thread(new ThreadStart(NetworkLoop)); m_networkThread.Name = m_configuration.NetworkThreadName; m_networkThread.IsBackground = true; m_networkThread.Start(); #if ENABLE_UPNP // send upnp discovery if (m_upnp != null) { m_upnp.Discover(this); } #endif // allow some time for network thread to start up in case they call Connect() or UPnP calls immediately NetUtility.Sleep(50); }
/// <summary> /// Writes all properties with specified binding in alphabetical order using reflection /// </summary> public void WriteAllProperties(object ob, BindingFlags flags) { if (ob == null) { return; } Type tp = ob.GetType(); PropertyInfo[] fields = tp.GetProperties(flags); NetUtility.SortMembersList(fields); foreach (PropertyInfo fi in fields) { MethodInfo getMethod = fi.GetGetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic); object value = getMethod.Invoke(ob, null); // find the appropriate Write method MethodInfo writeMethod; if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod)) { writeMethod.Invoke(this, new object[] { value }); } } }
/// <summary> /// Emit a discovery signal to all hosts on your subnet /// </summary> public void DiscoverLocalPeers(int serverPort) { NetOutgoingMessage um = CreateMessage(0); um.m_messageType = NetMessageType.Discovery; Interlocked.Increment(ref um.m_recyclingCount); m_unsentUnconnectedMessages.Enqueue(new NetTuple <NetEndPoint, long, NetOutgoingMessage>(new NetEndPoint(NetUtility.GetBroadcastAddress(), serverPort), 0, um)); }
/// <summary> /// Create a connection to a remote endpoint /// </summary> public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage) { return(Connect(new IPEndPoint(NetUtility.Resolve(host, NetGameConfiguration.NetAddressFamily), port), hailMessage)); }
/// <summary> /// Create a connection to a remote endpoint /// </summary> public NetConnection Connect(string host, int port) { return(Connect(new IPEndPoint(NetUtility.Resolve(host, NetGameConfiguration.NetAddressFamily), port), null)); }
internal override void ReceiveMessage(NetIncomingMessage message) { int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart); // ack no matter what m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber); if (relate == 0) { // Log("Received message #" + message.SequenceNumber + " right on time"); // // excellent, right on time // //m_peer.LogVerbose("Received RIGHT-ON-TIME " + message); AdvanceWindow(); m_peer.ReleaseMessage(message); // release withheld messages int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers; while (m_earlyReceived[nextSeqNr % m_windowSize]) { message = m_withheldMessages[nextSeqNr % m_windowSize]; NetException.Assert(message != null); // remove it from withheld messages m_withheldMessages[nextSeqNr % m_windowSize] = null; m_peer.LogVerbose("Releasing withheld message #" + message); m_peer.ReleaseMessage(message); AdvanceWindow(); nextSeqNr++; } return; } if (relate < 0) { // duplicate m_connection.m_statistics.MessageDropped(); m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE"); return; } // relate > 0 = early message if (relate > m_windowSize) { // too early message! m_connection.m_statistics.MessageDropped(); m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart); return; } m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true); m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart); m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message; }
private void InitializeNetwork() { lock (m_initializeLock) { m_configuration.Lock(); if (m_status == NetPeerStatus.Running) { return; } if (m_configuration.m_enableUPnP) { m_upnp = new NetUPnP(this); } InitializePools(); m_releasedIncomingMessages.Clear(); m_unsentUnconnectedMessages.Clear(); m_handshakes.Clear(); // bind to socket IPEndPoint iep = null; iep = new IPEndPoint(m_configuration.LocalAddress, m_configuration.Port); EndPoint ep = (EndPoint)iep; m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); m_socket.ReceiveBufferSize = m_configuration.ReceiveBufferSize; m_socket.SendBufferSize = m_configuration.SendBufferSize; m_socket.Blocking = false; m_socket.Bind(ep); try { const uint IOC_IN = 0x80000000; const uint IOC_VENDOR = 0x18000000; uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; m_socket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null); } catch { // ignore; SIO_UDP_CONNRESET not supported on this platform } IPEndPoint boundEp = m_socket.LocalEndPoint as IPEndPoint; LogDebug("Socket bound to " + boundEp + ": " + m_socket.IsBound); m_listenPort = boundEp.Port; m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize]; m_sendBuffer = new byte[m_configuration.SendBufferSize]; m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error); m_readHelperMessage.m_data = m_receiveBuffer; byte[] macBytes = new byte[8]; NetRandom.Instance.NextBytes(macBytes); #if IS_MAC_AVAILABLE try { System.Net.NetworkInformation.PhysicalAddress pa = NetUtility.GetMacAddress(); if (pa != null) { macBytes = pa.GetAddressBytes(); LogVerbose("Mac address is " + NetUtility.ToHexString(macBytes)); } else { LogWarning("Failed to get Mac address"); } } catch (NotSupportedException) { // not supported; lets just keep the random bytes set above } #endif byte[] epBytes = BitConverter.GetBytes(boundEp.GetHashCode()); byte[] combined = new byte[epBytes.Length + macBytes.Length]; Array.Copy(epBytes, 0, combined, 0, epBytes.Length); Array.Copy(macBytes, 0, combined, epBytes.Length, macBytes.Length); m_uniqueIdentifier = BitConverter.ToInt64(SHA1.Create().ComputeHash(combined), 0); m_status = NetPeerStatus.Running; } }
private void Heartbeat() { VerifyNetworkThread(); double dnow = NetTime.Now; float now = (float)dnow; double delta = dnow - m_lastHeartbeat; int maxCHBpS = 1250 - m_connections.Count; if (maxCHBpS < 250) { maxCHBpS = 250; } if (delta > (1.0 / (double)maxCHBpS)) // max connection heartbeats/second max { m_frameCounter++; m_lastHeartbeat = dnow; // do handshake heartbeats if ((m_frameCounter % 3) == 0) { foreach (var kvp in m_handshakes) { NetConnection conn = kvp.Value as NetConnection; #if DEBUG // sanity check if (kvp.Key != kvp.Key) { LogWarning("Sanity fail! Connection in handshake list under wrong key!"); } #endif conn.UnconnectedHeartbeat(now); if (conn.m_status == NetConnectionStatus.Connected || conn.m_status == NetConnectionStatus.Disconnected) { #if DEBUG // sanity check if (conn.m_status == NetConnectionStatus.Disconnected && m_handshakes.ContainsKey(conn.RemoteEndpoint)) { LogWarning("Sanity fail! Handshakes list contained disconnected connection!"); m_handshakes.Remove(conn.RemoteEndpoint); } #endif break; // collection has been modified } } } #if DEBUG SendDelayedPackets(); #endif // update m_executeFlushSendQueue if (m_configuration.m_autoFlushSendQueue) { m_executeFlushSendQueue = true; } // do connection heartbeats lock (m_connections) { foreach (NetConnection conn in m_connections) { conn.Heartbeat(now, m_frameCounter); if (conn.m_status == NetConnectionStatus.Disconnected) { // // remove connection // m_connections.Remove(conn); m_connectionLookup.Remove(conn.RemoteEndpoint); break; // can't continue iteration here } } } m_executeFlushSendQueue = false; // send unsent unconnected messages NetTuple <IPEndPoint, NetOutgoingMessage> unsent; while (m_unsentUnconnectedMessages.TryDequeue(out unsent)) { NetOutgoingMessage om = unsent.Item2; bool connReset; int len = om.Encode(m_sendBuffer, 0, 0); SendPacket(len, unsent.Item1, 1, out connReset); Interlocked.Decrement(ref om.m_recyclingCount); if (om.m_recyclingCount <= 0) { Recycle(om); } } } // // read from socket // if (m_socket == null) { return; } if (!m_socket.Poll(1000, SelectMode.SelectRead)) // wait up to 1 ms for data to arrive { return; } //if (m_socket == null || m_socket.Available < 1) // return; do { int bytesReceived = 0; try { bytesReceived = m_socket.ReceiveFrom(m_receiveBuffer, 0, m_receiveBuffer.Length, SocketFlags.None, ref m_senderRemote); } catch (SocketException sx) { if (sx.SocketErrorCode == SocketError.ConnectionReset) { // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable" // we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?! // So, what to do? LogWarning("ConnectionReset"); return; } LogWarning(sx.ToString()); return; } if (bytesReceived < NetConstants.HeaderByteSize) { return; } //LogVerbose("Received " + bytesReceived + " bytes"); IPEndPoint ipsender = (IPEndPoint)m_senderRemote; if (ipsender.Port == 1900) { // UPnP response try { string resp = System.Text.Encoding.ASCII.GetString(m_receiveBuffer, 0, bytesReceived); if (resp.Contains("upnp:rootdevice")) { resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9); resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); m_upnp.ExtractServiceUrl(resp); return; } } catch { } } NetConnection sender = null; m_connectionLookup.TryGetValue(ipsender, out sender); double receiveTime = NetTime.Now; // // parse packet into messages // int numMessages = 0; int ptr = 0; while ((bytesReceived - ptr) >= NetConstants.HeaderByteSize) { // decode header // 8 bits - NetMessageType // 1 bit - Fragment? // 15 bits - Sequence number // 16 bits - Payload length in bits numMessages++; NetMessageType tp = (NetMessageType)m_receiveBuffer[ptr++]; byte low = m_receiveBuffer[ptr++]; byte high = m_receiveBuffer[ptr++]; bool isFragment = ((low & 1) == 1); ushort sequenceNumber = (ushort)((low >> 1) | (((int)high) << 7)); ushort payloadBitLength = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8)); int payloadByteLength = NetUtility.BytesToHoldBits(payloadBitLength); if (bytesReceived - ptr < payloadByteLength) { LogWarning("Malformed packet; stated payload length " + payloadByteLength + ", remaining bytes " + (bytesReceived - ptr)); return; } try { NetException.Assert(tp <NetMessageType.Unused1 || tp> NetMessageType.Unused29); if (tp >= NetMessageType.LibraryError) { if (sender != null) { sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength); } else { ReceivedUnconnectedLibraryMessage(receiveTime, ipsender, tp, ptr, payloadByteLength); } } else { if (sender == null && !m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData)) { return; // dropping unconnected message since it's not enabled } NetIncomingMessage msg = CreateIncomingMessage(NetIncomingMessageType.Data, payloadByteLength); msg.m_isFragment = isFragment; msg.m_receiveTime = receiveTime; msg.m_sequenceNumber = sequenceNumber; msg.m_receivedMessageType = tp; msg.m_senderConnection = sender; msg.m_senderEndpoint = ipsender; msg.m_bitLength = payloadBitLength; Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength); if (sender != null) { if (tp == NetMessageType.Unconnected) { // We're connected; but we can still send unconnected messages to this peer msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData; ReleaseMessage(msg); } else { // connected application (non-library) message sender.ReceivedMessage(msg); } } else { // at this point we know the message type is enabled // unconnected application (non-library) message msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData; ReleaseMessage(msg); } } } catch (Exception ex) { LogError("Packet parsing error: " + ex.Message + " from " + ipsender); } ptr += payloadByteLength; } m_statistics.PacketReceived(bytesReceived, numMessages); if (sender != null) { sender.m_statistics.PacketReceived(bytesReceived, numMessages); } } while (m_socket.Available > 0); }
private void ExecutePeerShutdown() { VerifyNetworkThread(); LogDebug("Shutting down..."); // disconnect and make one final heartbeat var list = new List <NetConnection>(m_handshakes.Count + m_connections.Count); lock (m_connections) { foreach (var conn in m_connections) { if (conn != null) { list.Add(conn); } } } lock (m_handshakes) { foreach (var hs in m_handshakes.Values) { if (hs != null && list.Contains(hs) == false) { list.Add(hs); } } } // shut down connections foreach (NetConnection conn in list) { conn.Shutdown(m_shutdownReason); } FlushDelayedPackets(); // one final heartbeat, will send stuff and do disconnect Heartbeat(); NetUtility.Sleep(10); lock (m_initializeLock) { try { if (m_socket != null) { try { m_socket.Shutdown(SocketShutdown.Receive); } catch (Exception ex) { LogDebug("Socket.Shutdown exception: " + ex.ToString()); } try { m_socket.Close(2); // 2 seconds timeout } catch (Exception ex) { LogDebug("Socket.Close exception: " + ex.ToString()); } } } finally { m_socket = null; m_status = NetPeerStatus.NotRunning; LogDebug("Shutdown complete"); // wake up any threads waiting for server shutdown if (m_messageReceivedEvent != null) { m_messageReceivedEvent.Set(); } } m_lastSocketBind = float.MinValue; m_receiveBuffer = null; m_sendBuffer = null; m_unsentUnconnectedMessages.Clear(); m_connections.Clear(); m_connectionLookup.Clear(); m_handshakes.Clear(); } return; }
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // seqNr is the actual nr received internal override void ReceiveAcknowledge(float now, int seqNr) { // late (dupe), on time or early ack? int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart); if (relate < 0) { //m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr); return; // late/duplicate ack } if (relate == 0) { //m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr); // ack arrived right on time NetException.Assert(seqNr == m_windowStart); m_receivedAcks[m_windowStart] = false; DestoreMessage(m_windowStart % m_windowSize); m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; // advance window if we already have early acks while (m_receivedAcks.Get(m_windowStart)) { //m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "..."); m_receivedAcks[m_windowStart] = false; DestoreMessage(m_windowStart % m_windowSize); NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; //m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart); } return; } // // early ack... (if it has been sent!) // // If it has been sent either the m_windowStart message was lost // ... or the ack for that message was lost // //m_connection.m_peer.LogDebug("Received early ack for #" + seqNr); int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart); if (sendRelate <= 0) { // yes, we've sent this message - it's an early (but valid) ack if (m_receivedAcks[seqNr]) { // we've already destored/been acked for this message } else { m_receivedAcks[seqNr] = true; } } else if (sendRelate > 0) { // This is a bug in Lidgren (that I don't have time to mess around with) (-AR) // Disconnecting causes the channel to Reset() (resets pending sequence number), but somehow we still receive acks. // (Status gets set to Disconnected after the Reset, but seems to all be on the network thread, so not a race condition) if (m_connection == null || m_connection.m_status == NetConnectionStatus.Disconnected) { return; // Let's not call NetException.Assert! } // uh... we haven't sent this message yet? Weird, dupe or error... NetException.Assert(false, "Got ack for message not yet sent?"); return; } // Ok, lets resend all missing acks int rnr = seqNr; do { rnr--; if (rnr < 0) { rnr = NetConstants.NumSequenceNumbers - 1; } if (m_receivedAcks[rnr]) { // m_connection.m_peer.LogDebug("Not resending #" + rnr + " (since we got ack)"); } else { int slot = rnr % m_windowSize; NetException.Assert(m_storedMessages[slot].Message != null); if (m_storedMessages[slot].NumSent == 1) { // just sent once; resend immediately since we found gap in ack sequence NetOutgoingMessage rmsg = m_storedMessages[slot].Message; //m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")"); if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35f)) { // already resent recently } else { m_storedMessages[slot].LastSent = now; m_storedMessages[slot].NumSent++; m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence); m_connection.QueueSendMessage(rmsg, rnr); } } } } while (rnr != m_windowStart); }
/// <summary> /// Create a connection to a remote endpoint /// </summary> public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage) { return(Connect(new NetEndPoint(NetUtility.Resolve(host), port), hailMessage)); }
/// <summary> /// Create a connection to a remote endpoint /// </summary> public NetConnection Connect(string host, int port) { return(Connect(new NetEndPoint(NetUtility.Resolve(host), port), null)); }
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // seqNr is the actual nr received internal override void ReceiveAcknowledge(double now, int seqNr) { // late (dupe), on time or early ack? int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart); if (relate < 0) { //m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr); return; // late/duplicate ack } if (relate == 0) { //m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr); // ack arrived right on time NetException.Assert(seqNr == m_windowStart); bool resetTimeout; m_receivedAcks[m_windowStart] = false; DestoreMessage(now, m_windowStart % m_windowSize, out resetTimeout); m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; // advance window if we already have early acks while (m_receivedAcks.Get(m_windowStart)) { //m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "..."); m_receivedAcks[m_windowStart] = false; bool rt; DestoreMessage(now, m_windowStart % m_windowSize, out rt); resetTimeout |= rt; NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; //m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart); } if (resetTimeout) { m_connection.ResetTimeout(now); } return; } // // early ack... (if it has been sent!) // // If it has been sent either the m_windowStart message was lost // ... or the ack for that message was lost // //m_connection.m_peer.LogDebug("Received early ack for #" + seqNr); int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart); if (sendRelate <= 0) { // yes, we've sent this message - it's an early (but valid) ack if (m_receivedAcks[seqNr]) { // we've already destored/been acked for this message } else { m_receivedAcks[seqNr] = true; } } else if (sendRelate > 0) { // uh... we haven't sent this message yet? Weird, dupe or error... NetException.Assert(false, "Got ack for message not yet sent?"); return; } // Ok, lets resend all missing acks int rnr = seqNr; do { rnr--; if (rnr < 0) { rnr = NetConstants.NumSequenceNumbers - 1; } if (m_receivedAcks[rnr]) { // m_connection.m_peer.LogDebug("Not resending #" + rnr + " (since we got ack)"); } else { int slot = rnr % m_windowSize; NetException.Assert(m_storedMessages[slot].Message != null); if (m_storedMessages[slot].NumSent == 1) { // just sent once; resend immediately since we found gap in ack sequence NetOutgoingMessage rmsg = m_storedMessages[slot].Message; //m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")"); if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35)) { // already resent recently } else { m_storedMessages[slot].LastSent = now; m_storedMessages[slot].NumSent++; m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence); Interlocked.Increment(ref rmsg.m_recyclingCount); // increment this since it's being decremented in QueueSendMessage m_connection.QueueSendMessage(rmsg, rnr); } } } } while (rnr != m_windowStart); }