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 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; }
/// <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 { XmlDocument xdoc = 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> /// 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> /// Create a connection to a remote endpoint /// </summary> public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage) { return(Connect(new IPEndPoint(NetUtility.Resolve(host), 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), port), null)); }
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 PlatformSocket(); m_socket.ReceiveBufferSize = m_configuration.ReceiveBufferSize; m_socket.SendBufferSize = m_configuration.SendBufferSize; m_socket.Blocking = false; m_socket.Bind(ep); m_socket.Setup(); 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_FULL_NET_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); #if WINDOWS_PHONE SHA1 s = new SHA1Managed(); m_uniqueIdentifier = BitConverter.ToInt64(s.ComputeHash(combined), 0); #else m_uniqueIdentifier = BitConverter.ToInt64(SHA1.Create().ComputeHash(combined), 0); #endif 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 // 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 } } } // send unsent unconnected messages NetTuple <IPEndPoint, NetOutgoingMessage> unsent; while (m_unsentUnconnectedMessages.TryDequeue(out unsent)) { NetOutgoingMessage om = unsent.Item2; #if DEBUG if (om.m_messageType == NetMessageType.NatPunchMessage) { LogDebug("Sending Nat Punch Message to " + unsent.Item1.ToString()); } #endif 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)) // 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, 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? 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.UTF8.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 HandleReleasedFragment(NetIncomingMessage im) { // // read fragmentation header and combine fragments // int group; int totalBits; int chunkByteSize; int chunkNumber; int ptr = NetFragmentationHelper.ReadHeader( im.m_data, 0, out group, out totalBits, out chunkByteSize, out chunkNumber ); NetException.Assert(im.LengthBytes > ptr); NetException.Assert(group > 0); NetException.Assert(totalBits > 0); NetException.Assert(chunkByteSize > 0); int totalBytes = NetUtility.BytesToHoldBits((int)totalBits); int totalNumChunks = totalBytes / chunkByteSize; if (totalNumChunks * chunkByteSize < totalBytes) { totalNumChunks++; } NetException.Assert(chunkNumber < totalNumChunks); if (chunkNumber >= totalNumChunks) { LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")"); return; } Dictionary <int, ReceivedFragmentGroup> groups; if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups)) { groups = new Dictionary <int, ReceivedFragmentGroup>(); m_receivedFragmentGroups[im.SenderConnection] = groups; } ReceivedFragmentGroup info; if (!groups.TryGetValue(group, out info)) { info = new ReceivedFragmentGroup(); info.Data = new byte[totalBytes]; info.ReceivedChunks = new NetBitVector(totalNumChunks); groups[group] = info; } info.ReceivedChunks[chunkNumber] = true; info.LastReceived = (float)NetTime.Now; // copy to data int offset = (chunkNumber * chunkByteSize); Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr); int cnt = info.ReceivedChunks.Count(); //LogVerbose("Found fragment #" + chunkNumber + " in group " + group + " offset " + offset + " of total bits " + totalBits + " (total chunks done " + cnt + ")"); LogVerbose("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)"); if (info.ReceivedChunks.Count() == totalNumChunks) { // Done! Transform this incoming message im.m_data = info.Data; im.m_bitLength = (int)totalBits; im.m_isFragment = false; LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)"); ReleaseMessage(im); } else { // data has been copied; recycle this incoming message Recycle(im); } return; }
internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payloadLength) { m_peer.VerifyNetworkThread(); byte[] hail; switch (tp) { case NetMessageType.Connect: if (m_status == NetConnectionStatus.None) { // Whee! Server full has already been checked bool ok = ValidateHandshakeData(ptr, payloadLength, out hail); if (ok) { if (hail != null) { m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail); m_remoteHailMessage.LengthBits = (hail.Length * 8); } else { m_remoteHailMessage = null; } if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval)) { // ok, let's not add connection just yet NetIncomingMessage appMsg = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval, (m_remoteHailMessage == null ? 0 : m_remoteHailMessage.LengthBytes)); appMsg.m_receiveTime = now; appMsg.m_senderConnection = this; appMsg.m_senderEndpoint = this.m_remoteEndpoint; if (m_remoteHailMessage != null) { appMsg.Write(m_remoteHailMessage.m_data, 0, m_remoteHailMessage.LengthBytes); } m_peer.ReleaseMessage(appMsg); return; } SendConnectResponse((float)now, true); } return; } if (m_status == NetConnectionStatus.RespondedConnect) { // our ConnectResponse must have been lost SendConnectResponse((float)now, true); return; } m_peer.LogDebug("Unhandled Connect: " + tp + ", status is " + m_status + " length: " + payloadLength); break; case NetMessageType.ConnectResponse: switch (m_status) { case NetConnectionStatus.InitiatedConnect: // awesome bool ok = ValidateHandshakeData(ptr, payloadLength, out hail); if (ok) { if (hail != null) { m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail); m_remoteHailMessage.LengthBits = (hail.Length * 8); } else { m_remoteHailMessage = null; } m_peer.AcceptConnection(this); SendConnectionEstablished(); return; } break; case NetConnectionStatus.RespondedConnect: // hello, wtf? break; case NetConnectionStatus.Disconnecting: case NetConnectionStatus.Disconnected: case NetConnectionStatus.None: // wtf? anyway, bye! break; case NetConnectionStatus.Connected: // my ConnectionEstablished must have been lost, send another one SendConnectionEstablished(); return; } break; case NetMessageType.ConnectionEstablished: switch (m_status) { case NetConnectionStatus.Connected: // ok... break; case NetConnectionStatus.Disconnected: case NetConnectionStatus.Disconnecting: case NetConnectionStatus.None: // too bad, almost made it break; case NetConnectionStatus.InitiatedConnect: // weird, should have been ConnectResponse... break; case NetConnectionStatus.RespondedConnect: // awesome NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength); InitializeRemoteTimeOffset(msg.ReadSingle()); m_peer.AcceptConnection(this); InitializePing(); SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier)); return; } break; case NetMessageType.Disconnect: // ouch string reason = "Ouch"; try { NetIncomingMessage inc = m_peer.SetupReadHelperMessage(ptr, payloadLength); reason = inc.ReadString(); } catch { } ExecuteDisconnect(reason, false); break; case NetMessageType.Discovery: m_peer.HandleIncomingDiscoveryRequest(now, m_remoteEndpoint, ptr, payloadLength); return; case NetMessageType.DiscoveryResponse: m_peer.HandleIncomingDiscoveryResponse(now, m_remoteEndpoint, ptr, payloadLength); return; case NetMessageType.Ping: // silently ignore return; default: m_peer.LogDebug("Unhandled type during handshake: " + tp + " length: " + payloadLength); break; } }
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_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE"); return; } // relate > 0 = early message if (relate > m_windowSize) { // too early message! 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; m_peer.ReleaseMessage(message); }
// 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; 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 { DestoreMessage(seqNr % m_windowSize); m_receivedAcks[seqNr] = true; } } else if (sendRelate > 0) { // uh... we haven't sent this message yet? Weird, dupe or error... 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); }