/// <summary> /// Default constructor /// </summary> /// <param name="agent">Reference to the client this packet is destined for</param> /// <param name="buffer">Serialized packet data. If the flags or sequence number /// need to be updated, they will be injected directly into this binary buffer</param> /// <param name="category">Throttling category for this packet</param> /// <param name="type">Packet type</param> public OutgoingPacket(LLAgent agent, UDPPacketBuffer buffer, ThrottleCategory category, PacketType type) { Agent = agent; Buffer = buffer; Category = category; Type = type; }
private void AsyncEndReceive(IAsyncResult iar) { // Asynchronous receive operations will complete here through the call // to AsyncBeginReceive if (!m_shutdownFlag) { // Asynchronous mode will start another receive before the // callback for this packet is even fired. Very parallel :-) if (m_asyncPacketHandling) { AsyncBeginReceive(); } // get the buffer that was created in AsyncBeginReceive // this is the received data //WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState; //UDPPacketBuffer buffer = wrappedBuffer.Instance; UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; try { // get the length of data actually read from the socket, store it with the // buffer buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); // call the abstract method PacketReceived(), passing the buffer that // has just been filled from the socket read. PacketReceived(buffer); } catch (SocketException) { } catch (ObjectDisposedException) { } finally { //wrappedBuffer.Dispose(); // Synchronous mode waits until the packet callback completes // before starting the receive to fetch another packet if (!m_asyncPacketHandling) { AsyncBeginReceive(); } } } }
private void SendAckImmediate(IPEndPoint remoteEndpoint, uint sequenceNumber) { PacketAckPacket ack = new PacketAckPacket(); ack.Header.Reliable = false; ack.Packets = new PacketAckPacket.PacketsBlock[1]; ack.Packets[0] = new PacketAckPacket.PacketsBlock(); ack.Packets[0].ID = sequenceNumber; byte[] packetData = ack.ToBytes(); int length = packetData.Length; UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndpoint, length); buffer.DataLength = length; Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length); Send(buffer); }
protected override void PacketReceived(UDPPacketBuffer buffer) { // Debugging/Profiling //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } //catch (Exception) { } Packet packet = null; int packetEnd = buffer.DataLength - 1; IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; int now = Util.TickCount(); #region Decoding try { packet = Packet.BuildPacket(buffer.Data, ref packetEnd, // Only allocate a buffer for zerodecoding if the packet is zerocoded ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); } catch (MalformedDataException) { m_log.ErrorFormat("Malformed data, cannot parse packet from {0}:\n{1}", buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); } // Fail-safe check if (packet == null) { m_log.Warn("Couldn't build a message from incoming data " + buffer.DataLength + " bytes long from " + buffer.RemoteEndPoint); return; } #endregion Decoding #region Packet to Client Mapping // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { m_log.Debug("Handling UseCircuitCode packet from " + buffer.RemoteEndPoint); HandleUseCircuitCode(buffer, (UseCircuitCodePacket)packet, now); return; } // Determine which agent this packet came from LLAgent agent; if (!m_clients.TryGetValue(address, out agent)) { m_log.Debug("Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + Scene.Name); return; } if (!agent.IsConnected) return; #endregion Packet to Client Mapping #region Stats Tracking Interlocked.Increment(ref m_packetsReceived); Interlocked.Increment(ref agent.PacketsReceived); agent.TickLastPacketReceived = now; if (LoggingEnabled) m_log.Debug("--> (" + buffer.RemoteEndPoint + ") " + packet.Type); #endregion Stats Tracking #region ACK Receiving // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { for (int i = 0; i < packet.Header.AckList.Length; i++) agent.NeedAcks.Remove(packet.Header.AckList[i], now, packet.Header.Resent); } // Handle PacketAck packets if (packet.Type == PacketType.PacketAck) { PacketAckPacket ackPacket = (PacketAckPacket)packet; for (int i = 0; i < ackPacket.Packets.Length; i++) agent.NeedAcks.Remove(ackPacket.Packets[i].ID, now, packet.Header.Resent); // We don't need to do anything else with PacketAck packets return; } #endregion ACK Receiving #region Incoming Packet Accounting // Check the archive of received reliable packet IDs to see whether we already received this packet if (packet.Header.Reliable && !agent.PacketArchive.TryEnqueue(packet.Header.Sequence)) { if (packet.Header.Resent) m_log.Debug("Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); else m_log.Warn("Received a duplicate (not marked as resend) of packet #" + packet.Header.Sequence + ", type: " + packet.Type); // ACK this packet immediately to avoid further resends of this same packet SendAckImmediate((IPEndPoint)buffer.RemoteEndPoint, packet.Header.Sequence); // Avoid firing a callback twice for the same packet return; } #endregion Incoming Packet Accounting #region ACK Sending if (packet.Header.Reliable) { agent.PendingAcks.Enqueue(packet.Header.Sequence); } #endregion ACK Sending #region Ping Check Handling if (packet.Type == PacketType.StartPingCheck) { // We don't need to do anything else with ping checks StartPingCheckPacket startPing = (StartPingCheckPacket)packet; CompletePing(agent, startPing.PingID.PingID); return; } else if (packet.Type == PacketType.CompletePingCheck) { // We don't currently track client ping times return; } #endregion Ping Check Handling // Inbox insertion m_packetInbox.Enqueue(new IncomingPacket(agent, packet, now)); }
public void SendPacketData(LLAgent agent, byte[] data, PacketType type, ThrottleCategory category) { int dataLength = data.Length; bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; bool doCopy = true; // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here // to accomodate for both common scenarios and provide ample room for ACK appending in both int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; UDPPacketBuffer buffer = new UDPPacketBuffer(agent.RemoteEndPoint, bufferSize); // Zerocode if needed if (doZerocode) { try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); doCopy = false; } catch (IndexOutOfRangeException) { // The packet grew larger than the bufferSize while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead m_log.Debug("Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength + " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag"); data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); } } // If the packet data wasn't already copied during zerocoding, copy it now if (doCopy) { if (dataLength > buffer.Data.Length) { m_log.Error("Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length); buffer.Data = new byte[dataLength]; } Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } buffer.DataLength = dataLength; #region Queue or Send OutgoingPacket outgoingPacket = new OutgoingPacket(agent, buffer, category, type); if (!agent.EnqueueOutgoing(outgoingPacket)) SendPacketFinal(outgoingPacket); #endregion Queue or Send }
private void HandleUseCircuitCode(UDPPacketBuffer buffer, UseCircuitCodePacket packet, int now) { IPEndPoint remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; LLAgent agent; if (m_clients.TryGetValue(packet.CircuitCode.ID, out agent)) { // Update the remoteEndPoint for this agent m_clients.UpdateKey2(agent.ID, agent.RemoteEndPoint, remoteEndPoint); agent.RemoteEndPoint = remoteEndPoint; // Acknowledge the UseCircuitCode packet SendAckImmediate(remoteEndPoint, packet.Header.Sequence); // Fire any callbacks registered for this packet IncomingPacket incomingPacket = new IncomingPacket(agent, packet, now); incomingPacket.StartedHandling = now; PacketEvents.BeginRaiseEvent(incomingPacket); } else { m_log.Error("Failed to add new client " + packet.CircuitCode.ID + " connecting from " + remoteEndPoint); } }
public void Send(UDPPacketBuffer buf) { if (!m_shutdownFlag) { try { m_udpSocket.SendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint); //m_udpSocket.BeginSendTo( // buf.Data, // 0, // buf.DataLength, // SocketFlags.None, // buf.RemoteEndPoint, // AsyncEndSend, // buf); } catch (SocketException) { } catch (ObjectDisposedException) { } } }
private void AsyncBeginReceive() { // allocate a packet buffer //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut(); UDPPacketBuffer buf = new UDPPacketBuffer(); if (!m_shutdownFlag) { try { // kick off an async read m_udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, ref buf.RemoteEndPoint, AsyncEndReceive, //wrappedBuffer); buf); } catch (SocketException e) { if (e.SocketErrorCode == SocketError.ConnectionReset) { m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); bool salvaged = false; while (!salvaged) { try { m_udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, ref buf.RemoteEndPoint, AsyncEndReceive, //wrappedBuffer); buf); salvaged = true; } catch (SocketException) { } catch (ObjectDisposedException) { return; } } m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); } } catch (ObjectDisposedException) { } } }
/// <summary> /// This method is called when an incoming packet is received /// </summary> /// <param name="buffer">Incoming packet buffer</param> protected abstract void PacketReceived(UDPPacketBuffer buffer);
protected override void PacketReceived(UDPPacketBuffer buffer) { // Debugging/Profiling //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } //catch (Exception) { } Packet packet = null; int packetEnd = buffer.DataLength - 1; IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; int now = Util.TickCount(); #region Decoding try { packet = Packet.BuildPacket(buffer.Data, ref packetEnd, // Only allocate a buffer for zerodecoding if the packet is zerocoded ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); } catch (MalformedDataException) { m_log.ErrorFormat("Malformed data, cannot parse packet from {0}:\n{1}", buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); } // Fail-safe check if (packet == null) { m_log.Warn("Couldn't build a message from incoming data " + buffer.DataLength + " bytes long from " + buffer.RemoteEndPoint); return; } #endregion Decoding #region Packet to Client Mapping // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { m_log.Debug("Handling UseCircuitCode packet from " + buffer.RemoteEndPoint); HandleUseCircuitCode(buffer, (UseCircuitCodePacket)packet, now); return; } // Determine which agent this packet came from LLAgent agent; if (!m_clients.TryGetValue(address, out agent)) { m_log.Debug("Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + Scene.Name); return; } if (!agent.IsConnected) { return; } #endregion Packet to Client Mapping #region Stats Tracking Interlocked.Increment(ref m_packetsReceived); Interlocked.Increment(ref agent.PacketsReceived); agent.TickLastPacketReceived = now; if (LoggingEnabled) { m_log.Debug("--> (" + buffer.RemoteEndPoint + ") " + packet.Type); } #endregion Stats Tracking #region ACK Receiving // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { for (int i = 0; i < packet.Header.AckList.Length; i++) { agent.NeedAcks.Remove(packet.Header.AckList[i], now, packet.Header.Resent); } } // Handle PacketAck packets if (packet.Type == PacketType.PacketAck) { PacketAckPacket ackPacket = (PacketAckPacket)packet; for (int i = 0; i < ackPacket.Packets.Length; i++) { agent.NeedAcks.Remove(ackPacket.Packets[i].ID, now, packet.Header.Resent); } // We don't need to do anything else with PacketAck packets return; } #endregion ACK Receiving #region Incoming Packet Accounting // Check the archive of received reliable packet IDs to see whether we already received this packet if (packet.Header.Reliable && !agent.PacketArchive.TryEnqueue(packet.Header.Sequence)) { if (packet.Header.Resent) { m_log.Debug("Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); } else { m_log.Warn("Received a duplicate (not marked as resend) of packet #" + packet.Header.Sequence + ", type: " + packet.Type); } // ACK this packet immediately to avoid further resends of this same packet SendAckImmediate((IPEndPoint)buffer.RemoteEndPoint, packet.Header.Sequence); // Avoid firing a callback twice for the same packet return; } #endregion Incoming Packet Accounting #region ACK Sending if (packet.Header.Reliable) { agent.PendingAcks.Enqueue(packet.Header.Sequence); } #endregion ACK Sending #region Ping Check Handling if (packet.Type == PacketType.StartPingCheck) { // We don't need to do anything else with ping checks StartPingCheckPacket startPing = (StartPingCheckPacket)packet; CompletePing(agent, startPing.PingID.PingID); return; } else if (packet.Type == PacketType.CompletePingCheck) { // We don't currently track client ping times return; } #endregion Ping Check Handling // Inbox insertion m_packetInbox.Enqueue(new IncomingPacket(agent, packet, now)); }
internal void SendPacketFinal(OutgoingPacket outgoingPacket) { const int MAX_APPENDED_ACKS = 250; UDPPacketBuffer buffer = outgoingPacket.Buffer; byte flags = buffer.Data[0]; bool isResend = (flags & Helpers.MSG_RESENT) != 0; bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; bool isZerocoded = (flags & Helpers.MSG_ZEROCODED) != 0; LLAgent agent = outgoingPacket.Agent; if (!agent.IsConnected || buffer.RemoteEndPoint == null) { m_log.Debug("Dropping " + buffer.DataLength + " byte packet to client we have no route to"); return; } int dataLength = buffer.DataLength; #region ACK Appending // NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here if (!isZerocoded && outgoingPacket.Type != PacketType.PacketAck) { // Keep appending ACKs until there is no room left in the buffer or there are // no more ACKs to append byte ackCount = 0; uint ack; while (dataLength + 5 < buffer.Data.Length && ackCount < MAX_APPENDED_ACKS && agent.PendingAcks.TryDequeue(out ack)) { Utils.UIntToBytesBig(ack, buffer.Data, dataLength); dataLength += 4; ++ackCount; } if (ackCount > 0) { // Set the last byte of the packet equal to the number of appended ACKs buffer.Data[dataLength++] = ackCount; // Set the appended ACKs flag on this packet buffer.Data[0] |= Helpers.MSG_APPENDED_ACKS; } } #endregion ACK Appending buffer.DataLength = dataLength; #region Sequence Number Assignment if (!isResend) { // Not a resend, assign a new sequence number uint sequenceNumber = (uint)Interlocked.Increment(ref agent.CurrentSequence); Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); outgoingPacket.SequenceNumber = sequenceNumber; if (isReliable) { // Add this packet to the list of ACK responses we are waiting on from the server agent.NeedAcks.Add(outgoingPacket); } } #endregion Sequence Number Assignment // Stats tracking Interlocked.Increment(ref agent.PacketsSent); Interlocked.Increment(ref m_packetsSent); if (isReliable) { Interlocked.Add(ref agent.UnackedBytes, outgoingPacket.Buffer.DataLength); } if (LoggingEnabled) { m_log.Debug("<-- (" + buffer.RemoteEndPoint + ") " + outgoingPacket.Type); } // Put the UDP payload on the wire Send(buffer); // Keep track of when this packet was sent out (right now) outgoingPacket.TickCount = Util.TickCount(); //m_log.Debug("Sent " + outgoingPacket.Buffer.DataLength + " byte " + outgoingPacket.Category + " packet"); }
public void SendPacketData(LLAgent agent, byte[] data, PacketType type, ThrottleCategory category) { int dataLength = data.Length; bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; bool doCopy = true; // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here // to accomodate for both common scenarios and provide ample room for ACK appending in both int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; UDPPacketBuffer buffer = new UDPPacketBuffer(agent.RemoteEndPoint, bufferSize); // Zerocode if needed if (doZerocode) { try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); doCopy = false; } catch (IndexOutOfRangeException) { // The packet grew larger than the bufferSize while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead m_log.Debug("Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength + " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag"); data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); } } // If the packet data wasn't already copied during zerocoding, copy it now if (doCopy) { if (dataLength > buffer.Data.Length) { m_log.Error("Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length); buffer.Data = new byte[dataLength]; } Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } buffer.DataLength = dataLength; #region Queue or Send OutgoingPacket outgoingPacket = new OutgoingPacket(agent, buffer, category, type); if (!agent.EnqueueOutgoing(outgoingPacket)) { SendPacketFinal(outgoingPacket); } #endregion Queue or Send }