/// <summary> /// Default constructor /// </summary> /// <param name="client">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> public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method) { Client = client; Buffer = buffer; Category = category; UnackedMethod = method; }
private void AsyncBeginReceive() { lock (typeof(Constants)) { if (!Constants.isClosing) { // allocate a packet buffer UDPPacketBuffer buf = new UDPPacketBuffer(); try { // kick off an async read udpSocket.BeginReceiveFrom( buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, ref buf.RemoteEndPoint, new AsyncCallback(AsyncEndReceive), buf); } catch (SocketException se) { txtReceive.Text = se.Message + System.Environment.NewLine + txtReceive.Text; } } } }
/// <summary> /// Default constructor /// </summary> /// <param name="client">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="resendMethod">The delegate to be called if this packet is determined to be unacknowledged</param> /// <param name="finishedMethod">The delegate to be called when this packet is sent</param> public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod resendMethod, UnackedPacketMethod finishedMethod, Packet packet) { Client = client; Buffer = buffer; Category = category; UnackedMethod = resendMethod; FinishedMethod = finishedMethod; Packet = packet; }
private void HandleUseCircuitCode(object o) { object[] array = (object[])o; UDPPacketBuffer buffer = (UDPPacketBuffer)array[0]; UseCircuitCodePacket packet = (UseCircuitCodePacket)array[1]; IPEndPoint remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; // Begin the process of adding the client to the simulator AddNewClient((UseCircuitCodePacket)packet, remoteEndPoint); // Acknowledge the UseCircuitCode packet SendAckImmediate(remoteEndPoint, packet.Header.Sequence); }
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 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) { } catch (Exception ex) { MainConsole.Instance.Error("[UDPBase]: Hit error: " + ex.ToString()); } finally { // Synchronous mode waits until the packet callback completes // before starting the receive to fetch another packet if (!m_asyncPacketHandling) { AsyncBeginReceive(); } } } }
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 (Exception e) { m_log.ErrorFormat("[LLUDP] Exception thrown from UDP EndReceiveFrom: {0}", e); } finally { //wrappedBuffer.Dispose(); // Synchronous mode waits until the packet callback completes // before starting the receive to fetch another packet if (!m_asyncPacketHandling) { AsyncBeginReceive(); } } } }
public void SyncSend(UDPPacketBuffer buf) { if (!m_shutdownFlag) { try { // well not async but blocking m_udpSocket.SendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint); } catch (SocketException) { } catch (ObjectDisposedException) { } catch (Exception) { } } }
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); AsyncBeginSend(buffer); }
private void HandleUseCircuitCode(object o) { DateTime startTime = DateTime.Now; object[] array = (object[])o; UDPPacketBuffer buffer = (UDPPacketBuffer)array[0]; UseCircuitCodePacket packet = (UseCircuitCodePacket)array[1]; IPEndPoint remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; // Begin the process of adding the client to the simulator if (AddNewClient((UseCircuitCodePacket)packet, remoteEndPoint)) { // Acknowledge the UseCircuitCode packet SendAckImmediate(remoteEndPoint, packet.Header.Sequence); m_log.InfoFormat( "[LLUDPSERVER]: Handling UseCircuitCode request from {0} took {1}ms", remoteEndPoint, (DateTime.Now - startTime).Milliseconds); } }
private void AsyncBeginReceive() { // allocate a packet buffer //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut(); UDPPacketBuffer buf = _recvBufferPool.LeaseObject(); if (!m_shutdownFlag) { bool recvFromRunning = false; while (!recvFromRunning) { try { // kick off an async read m_udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, ref buf.RemoteEndPoint, AsyncEndReceive, buf); recvFromRunning = true; } catch (SocketException e) { m_log.ErrorFormat("[LLUDP] SocketException thrown from UDP BeginReceiveFrom, retrying: {0}", e); } catch (Exception e) { m_log.ErrorFormat("[LLUDP] Exception thrown from UDP BeginReceiveFrom: {0}", e); //UDP is toast and will not recover. The sim is for all intents and purposes, dead. throw; } } } }
void SendPacket(UDPClient client, Packet packet, PacketCategory category, bool setSequence) { byte[] buffer; int bytes; // Set sequence implies that this is not a resent packet if (setSequence) { // Reset to zero if we've hit the upper sequence number limit Interlocked.CompareExchange(ref client.CurrentSequence, 0, 0xFFFFFF); // Increment and fetch the current sequence number uint sequence = (uint)Interlocked.Increment(ref client.CurrentSequence); packet.Header.Sequence = sequence; if (packet.Header.Reliable) { OutgoingPacket outgoing; if (packet.Header.Resent && client.NeedAcks.TryGetValue(packet.Header.Sequence, out outgoing)) { // This packet has already been sent out once, strip any appended ACKs // off it and reinsert them into the outgoing ACK queue under the // assumption that this packet will continually be rejected from the // client or that the appended ACKs are possibly making the delivery fail if (packet.Header.AckList.Length > 0) { Logger.DebugLog(String.Format("Purging ACKs from packet #{0} ({1}) which will be resent.", packet.Header.Sequence, packet.GetType())); lock (client.PendingAcks) { foreach (uint ack in packet.Header.AckList) { if (!client.PendingAcks.ContainsKey(ack)) { client.PendingAcks[ack] = ack; } } } packet.Header.AppendedAcks = false; packet.Header.AckList = new uint[0]; } } else { // Wrap this packet in a struct to track timeouts and resends outgoing = new OutgoingPacket(packet); // Add this packet to the list of ACK responses we are waiting on from this client lock (client.NeedAcks) client.NeedAcks[sequence] = outgoing; // This packet is not a resend, check if the conditions are favorable // to ACK appending if (packet.Type != PacketType.PacketAck) { lock (client.PendingAcks) { int count = client.PendingAcks.Count; if (count > 0 && count < 10) { // Append all of the queued up outgoing ACKs to this packet packet.Header.AckList = new uint[count]; for (int i = 0; i < count; i++) { packet.Header.AckList[i] = client.PendingAcks.Values[i]; } client.PendingAcks.Clear(); packet.Header.AppendedAcks = true; } } } } // Update the sent time for this packet outgoing.TickCount = Environment.TickCount; } else if (packet.Header.AckList.Length > 0) { // Sanity check for ACKS appended on an unreliable packet, this is bad form Logger.Log("Sending appended ACKs on an unreliable packet", Helpers.LogLevel.Warning); } } // Serialize the packet buffer = packet.ToBytes(); bytes = buffer.Length; //Stats.SentBytes += (ulong)bytes; //++Stats.SentPackets; UDPPacketBuffer buf = new UDPPacketBuffer(client.Address); // Zerocode if needed if (packet.Header.Zerocoded) { bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data); } else { Buffer.BlockCopy(buffer, 0, buf.Data, 0, bytes); } buf.DataLength = bytes; AsyncBeginSend(buf); }
protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) { }
protected override void PacketReceived(UDPPacketBuffer buffer) { Packet packet = null; // Check if this packet came from the server we expected it to come from if (!ipEndPoint.Address.Equals(((IPEndPoint)buffer.RemoteEndPoint).Address)) { Logger.Log("Received " + buffer.DataLength + " bytes of data from unrecognized source " + ((IPEndPoint)buffer.RemoteEndPoint).ToString(), Helpers.LogLevel.Warning, Client); return; } // Update the disconnect flag so this sim doesn't time out DisconnectCandidate = false; #region Packet Decoding int packetEnd = buffer.DataLength - 1; try { packet = Packet.BuildPacket(buffer.Data, ref packetEnd, buffer.ZeroData); } catch (MalformedDataException) { Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}", Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error); } // Fail-safe check if (packet == null) { Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning, Client); return; } Stats.RecvBytes += (ulong)buffer.DataLength; ++Stats.RecvPackets; #endregion Packet Decoding #region Reliable Handling if (packet.Header.Reliable) { // Add this packet to the list of ACKs that need to be sent out lock (PendingAcks) { uint sequence = (uint)packet.Header.Sequence; if (!PendingAcks.ContainsKey(sequence)) PendingAcks[sequence] = sequence; } // Send out ACKs if we have a lot of them if (PendingAcks.Count >= Client.Settings.MAX_PENDING_ACKS) SendAcks(); if (packet.Header.Resent) ++Stats.ReceivedResends; } #endregion Reliable Handling #region Inbox Insertion NetworkManager.IncomingPacket incomingPacket; incomingPacket.Simulator = this; incomingPacket.Packet = packet; // TODO: Prioritize the queue Network.PacketInbox.Enqueue(incomingPacket); #endregion Inbox Insertion }
/// <summary> /// Sends a packet directly to the simulator without queuing /// </summary> /// <param name="packet">Packet to be sent</param> /// <param name="setSequence">True to set the sequence number, false to /// leave it as is</param> public void SendPacketUnqueued(Packet packet, bool setSequence) { byte[] buffer; int bytes; // Set sequence implies that this is not a resent packet if (setSequence) { // Reset to zero if we've hit the upper sequence number limit Interlocked.CompareExchange(ref Sequence, 0, Settings.MAX_SEQUENCE); // Increment and fetch the current sequence number packet.Header.Sequence = (uint)Interlocked.Increment(ref Sequence); if (packet.Header.Reliable) { // Wrap this packet in a struct to track timeouts and resends NetworkManager.OutgoingPacket outgoing = new NetworkManager.OutgoingPacket(this, packet, true); // Keep track of when this packet was first sent out (right now) outgoing.TickCount = Environment.TickCount; // Add this packet to the list of ACK responses we are waiting on from the server lock (NeedAck) { NeedAck[packet.Header.Sequence] = outgoing; } if (packet.Header.Resent) { // This packet has already been sent out once, strip any appended ACKs // off it and reinsert them into the outgoing ACK queue under the // assumption that this packet will continually be rejected from the // server or that the appended ACKs are possibly making the delivery fail if (packet.Header.AckList.Length > 0) { Logger.DebugLog(String.Format("Purging ACKs from packet #{0} ({1}) which will be resent.", packet.Header.Sequence, packet.GetType())); lock (PendingAcks) { foreach (uint sequence in packet.Header.AckList) { if (!PendingAcks.ContainsKey(sequence)) PendingAcks[sequence] = sequence; } } packet.Header.AppendedAcks = false; packet.Header.AckList = new uint[0]; } // Update the sent time for this packet SetResentTime(packet.Header.Sequence); } else { // This packet is not a resend, check if the conditions are favorable // to ACK appending if (packet.Type != PacketType.PacketAck && packet.Type != PacketType.LogoutRequest) { lock (PendingAcks) { if (PendingAcks.Count > 0 && PendingAcks.Count < Client.Settings.MAX_APPENDED_ACKS) { // Append all of the queued up outgoing ACKs to this packet packet.Header.AckList = new uint[PendingAcks.Count]; for (int i = 0; i < PendingAcks.Count; i++) packet.Header.AckList[i] = PendingAcks.Values[i]; PendingAcks.Clear(); packet.Header.AppendedAcks = true; } } } } } else if (packet.Header.AckList.Length > 0) { // Sanity check for ACKS appended on an unreliable packet, this is bad form Logger.Log("Sending appended ACKs on an unreliable packet", Helpers.LogLevel.Warning); } } // Serialize the packet buffer = packet.ToBytes(); bytes = buffer.Length; Stats.SentBytes += (ulong)bytes; ++Stats.SentPackets; UDPPacketBuffer buf = new UDPPacketBuffer(ipEndPoint); // Zerocode if needed if (packet.Header.Zerocoded) bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data); else Buffer.BlockCopy(buffer, 0, buf.Data, 0, bytes); buf.DataLength = bytes; AsyncBeginSend(buf); }
/// <summary> /// Used by tests that aren't testing this stage. /// </summary> private ScenePresence AddClient() { UUID myAgentUuid = TestHelpers.ParseTail(0x1); UUID mySessionUuid = TestHelpers.ParseTail(0x2); uint myCircuitCode = 123456; IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999); UseCircuitCodePacket uccp = new UseCircuitCodePacket(); UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock = new UseCircuitCodePacket.CircuitCodeBlock(); uccpCcBlock.Code = myCircuitCode; uccpCcBlock.ID = myAgentUuid; uccpCcBlock.SessionID = mySessionUuid; uccp.CircuitCode = uccpCcBlock; byte[] uccpBytes = uccp.ToBytes(); UDPPacketBuffer upb = new UDPPacketBuffer(testEp, uccpBytes.Length); upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor. Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length); AgentCircuitData acd = new AgentCircuitData(); acd.AgentID = myAgentUuid; acd.SessionID = mySessionUuid; m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd); m_udpServer.PacketReceived(upb); return m_scene.GetScenePresence(myAgentUuid); }
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) { } } }
public void SendPacketData(byte[] data, int dataLength, PacketType type, bool doZerocode) { UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndPoint, Packet.MTU); // Zerocode if needed if (doZerocode) { try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); } catch (IndexOutOfRangeException) { // The packet grew larger than Packet.MTU bytes while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } } else { Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } buffer.DataLength = dataLength; #region Queue or Send NetworkManager.OutgoingPacket outgoingPacket = new NetworkManager.OutgoingPacket(this, buffer); // Send ACK and logout packets directly, everything else goes through the queue if (Network.ThrottleOutgoingPackets == false || type == PacketType.PacketAck || type == PacketType.LogoutRequest) { SendPacketFinal(outgoingPacket); } else { Network.PacketOutbox.Enqueue(outgoingPacket); } #endregion Queue or Send }
/// <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) { UDPClient client = null; Packet packet = null; int packetEnd = buffer.DataLength - 1; IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; // Decoding try { packet = Packet.BuildPacket(buffer.Data, ref packetEnd, buffer.ZeroData); } catch (MalformedDataException) { Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}", Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error); } // Fail-safe check if (packet == null) { Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning); return; } //Stats.RecvBytes += (ulong)buffer.DataLength; //++Stats.RecvPackets; if (packet.Type == PacketType.UseCircuitCode) { UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet; Agent agent; if (CompleteAgentConnection(useCircuitCode.CircuitCode.Code, out agent)) { AddClient(agent, address); if (clients.TryGetValue(agent.AgentID, out client)) { Logger.Log("Activated UDP circuit " + useCircuitCode.CircuitCode.Code, Helpers.LogLevel.Info); } else { Logger.Log("Failed to locate newly created UDPClient", Helpers.LogLevel.Error); return; } } else { Logger.Log("Received a UseCircuitCode packet for an unrecognized circuit: " + useCircuitCode.CircuitCode.Code.ToString(), Helpers.LogLevel.Warning); return; } } else { // Determine which agent this packet came from if (!clients.TryGetValue(address, out client)) { Logger.Log("Received UDP packet from an unrecognized source: " + address.ToString(), Helpers.LogLevel.Warning); return; } } client.Agent.TickLastPacketReceived = Environment.TickCount; // Reliable handling if (packet.Header.Reliable) { // Queue up this sequence number for acknowledgement QueueAck(client, (uint)packet.Header.Sequence); //if (packet.Header.Resent) ++Stats.ReceivedResends; } // Inbox insertion IncomingPacket incomingPacket; incomingPacket.Client = client; incomingPacket.Packet = packet; // TODO: Prioritize the queue packetInbox.Enqueue(incomingPacket); }
/// <summary> /// Called when the packet is built and this buffer is no longer in use /// </summary> /// <param name="buffer"></param> protected void PacketBuildingFinished(UDPPacketBuffer buffer) { buffer.ResetEndpoint(); _recvBufferPool.ReturnObject(buffer); }
protected override void PacketReceived(UDPPacketBuffer buffer) { UIForm._NetRecvData(buffer); }
public void SendPacketData(byte[] data, int dataLength, PacketType type, bool doZerocode) { UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndPoint, Packet.MTU); // Zerocode if needed if (doZerocode) { try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); } catch (IndexOutOfRangeException) { // The packet grew larger than Packet.MTU bytes while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } } else { Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } buffer.DataLength = dataLength; #region Queue or Send NetworkManager.OutgoingPacket outgoingPacket = new NetworkManager.OutgoingPacket(this, buffer); // Send ACK and logout packets directly, everything else goes through the queue if (Client.Settings.THROTTLE_OUTGOING_PACKETS == false || type == PacketType.PacketAck || type == PacketType.LogoutRequest) { SendPacketFinal(outgoingPacket); } else { Network.PacketOutbox.Enqueue(outgoingPacket); } #endregion Queue or Send #region Stats Tracking if (Client.Settings.TRACK_UTILIZATION) { Client.Stats.Update(type.ToString(), OpenMetaverse.Stats.Type.Packet, dataLength, 0); } #endregion }
public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType 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(udpClient.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("[LLUDPSERVER]: 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) { Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } else { bufferSize = dataLength; buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); // m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + // type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length + ". Dropping packet"); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } } buffer.DataLength = dataLength; #region Queue or Send OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) { SendPacketFinal(outgoingPacket); } #endregion Queue or Send }
/// <summary> /// Default constructor /// </summary> /// <param name="client">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> public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category) { Client = client; Buffer = buffer; Category = category; }
/// <summary> /// Actually send a packet to a client. /// </summary> /// <param name="outgoingPacket"></param> internal void SendPacketFinal(OutgoingPacket outgoingPacket) { 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; LLUDPClient udpClient = outgoingPacket.Client; if (!udpClient.IsConnected) { return; } #region ACK Appending int dataLength = buffer.DataLength; // NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here if (!isZerocoded) { // Keep appending ACKs until there is no room left in the buffer or there are // no more ACKs to append uint ackCount = 0; uint ack; while (dataLength + 5 < buffer.Data.Length && udpClient.PendingAcks.Dequeue(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++] = (byte)ackCount; // Set the appended ACKs flag on this packet buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS); } } buffer.DataLength = dataLength; #endregion ACK Appending #region Sequence Number Assignment if (!isResend) { // Not a resend, assign a new sequence number uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.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 udpClient.NeedAcks.Add(outgoingPacket); } } #endregion Sequence Number Assignment // Stats tracking Interlocked.Increment(ref udpClient.PacketsSent); if (isReliable) { Interlocked.Add(ref udpClient.UnackedBytes, outgoingPacket.Buffer.DataLength); } // Put the UDP payload on the wire AsyncBeginSend(buffer); // Keep track of when this packet was sent out (right now) outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; }
protected override void PacketReceived(UDPPacketBuffer buffer) { // Debugging/Profiling //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } //catch (Exception) { } LLUDPClient udpClient = null; Packet packet = null; int packetEnd = buffer.DataLength - 1; IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; #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) { } // Fail-safe check if (packet == null) { m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse {0} byte packet from {1}:", buffer.DataLength, buffer.RemoteEndPoint); m_log.Error(Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); return; } #endregion Decoding #region Packet to Client Mapping // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { object[] array = new object[] { buffer, packet }; if (m_asyncPacketHandling) { Util.FireAndForget(HandleUseCircuitCode, array); } else { HandleUseCircuitCode(array); } return; } // Determine which agent this packet came from IClientAPI client; if (!m_scene.TryGetClient(address, out client) || !(client is LLClientView)) { //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); return; } udpClient = ((LLClientView)client).UDPClient; if (!udpClient.IsConnected) { return; } #endregion Packet to Client Mapping // Stats tracking Interlocked.Increment(ref udpClient.PacketsReceived); int now = Environment.TickCount & Int32.MaxValue; udpClient.TickLastPacketReceived = now; #region ACK Receiving // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { for (int i = 0; i < packet.Header.AckList.Length; i++) { udpClient.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++) { udpClient.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 ACK Sending if (packet.Header.Reliable) { udpClient.PendingAcks.Enqueue(packet.Header.Sequence); // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove // 2*MTU bytes from the value and send ACKs, and finally add the local value back to // client.BytesSinceLastACK. Lockless thread safety int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); bytesSinceLastACK += buffer.DataLength; if (bytesSinceLastACK > LLUDPServer.MTU * 2) { bytesSinceLastACK -= LLUDPServer.MTU * 2; SendAcks(udpClient); } Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); } #endregion ACK Sending #region Incoming Packet Accounting // Check the archive of received reliable packet IDs to see whether we already received this packet if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) { if (packet.Header.Resent) { m_log.DebugFormat( "[LLUDPSERVER]: Received a resend of already processed packet #{0}, type {1} from {2}", packet.Header.Sequence, packet.Type, client.Name); } else { m_log.WarnFormat( "[LLUDPSERVER]: Received a duplicate (not marked as resend) of packet #{0}, type {1} from {2}", packet.Header.Sequence, packet.Type, client.Name); } // Avoid firing a callback twice for the same packet return; } #endregion Incoming Packet Accounting #region BinaryStats LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); #endregion BinaryStats #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(udpClient, startPing.PingID.PingID); if ((Environment.TickCount - m_elapsedMSSinceLastStatReport) >= 3000) { udpClient.SendPacketStats(); m_elapsedMSSinceLastStatReport = Environment.TickCount; } return; } else if (packet.Type == PacketType.CompletePingCheck) { // We don't currently track client ping times return; } #endregion Ping Check Handling // Inbox insertion packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); }
public void SyncSend(UDPPacketBuffer buf) { if (!m_shutdownFlag) { try { // well not async but blocking m_udpSocket.SendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint); } catch (SocketException) { } catch (ObjectDisposedException) { } } }
public void SendPacketData(LLUDPClient udpClient, byte[] data, Packet packet, ThrottleOutPacketType category, UnackedPacketMethod resendMethod, UnackedPacketMethod finishedMethod) { 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 * 2; UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.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.Info("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + packet.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) { Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } else { bufferSize = dataLength; buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); // m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + // type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length + ". Dropping packet"); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } } buffer.DataLength = dataLength; #region Queue or Send OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, resendMethod, finishedMethod, packet); if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) SendPacketFinal(outgoingPacket); #endregion Queue or Send }
public void TestAddClient() { TestHelpers.InMethod(); // TestHelpers.EnableLogging(); AddUdpServer(); UUID myAgentUuid = TestHelpers.ParseTail(0x1); UUID mySessionUuid = TestHelpers.ParseTail(0x2); uint myCircuitCode = 123456; IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999); UseCircuitCodePacket uccp = new UseCircuitCodePacket(); UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock = new UseCircuitCodePacket.CircuitCodeBlock(); uccpCcBlock.Code = myCircuitCode; uccpCcBlock.ID = myAgentUuid; uccpCcBlock.SessionID = mySessionUuid; uccp.CircuitCode = uccpCcBlock; byte[] uccpBytes = uccp.ToBytes(); UDPPacketBuffer upb = new UDPPacketBuffer(testEp, uccpBytes.Length); upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor. Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length); m_udpServer.PacketReceived(upb); // Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet Assert.That(m_scene.GetScenePresence(myAgentUuid), Is.Null); AgentCircuitData acd = new AgentCircuitData(); acd.AgentID = myAgentUuid; acd.SessionID = mySessionUuid; m_scene.AuthenticateHandler.AddNewCircuit(myCircuitCode, acd); m_udpServer.PacketReceived(upb); // Should succeed now ScenePresence sp = m_scene.GetScenePresence(myAgentUuid); Assert.That(sp.UUID, Is.EqualTo(myAgentUuid)); Assert.That(m_udpServer.PacketsSent.Count, Is.EqualTo(1)); Packet packet = m_udpServer.PacketsSent[0]; Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket))); PacketAckPacket ackPacket = packet as PacketAckPacket; Assert.That(ackPacket.Packets.Length, Is.EqualTo(1)); Assert.That(ackPacket.Packets[0].ID, Is.EqualTo(0)); }
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); // AsyncBeginSend(buffer); SyncSend(buffer); }
/// <summary> /// Send a raw byte array payload as a packet /// </summary> /// <param name="payload">The packet payload</param> /// <param name="setSequence">Whether the second, third, and fourth bytes /// should be modified to the current stream sequence number</param> public void SendPacketUnqueued(byte[] payload, bool setSequence) { try { if (setSequence && payload.Length > 3) { uint sequence = (uint)Interlocked.Increment(ref Sequence); payload[1] = (byte)(sequence >> 16); payload[2] = (byte)(sequence >> 8); payload[3] = (byte)(sequence % 256); } Stats.SentBytes += (ulong)payload.Length; ++Stats.SentPackets; UDPPacketBuffer buf = new UDPPacketBuffer(ipEndPoint); Buffer.BlockCopy(payload, 0, buf.Data, 0, payload.Length); buf.DataLength = payload.Length; AsyncBeginSend(buf); } catch (SocketException) { Logger.Log("Tried to send a " + payload.Length + " byte payload on a closed socket, shutting down " + this.ToString(), Helpers.LogLevel.Info, Client); Network.DisconnectSim(this, false); return; } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } }
public virtual void SendAckImmediate(IPEndPoint remoteEndpoint, PacketAckPacket ack) { 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); AsyncBeginSend(buffer); }
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) { MainConsole.Instance.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; } } MainConsole.Instance.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); } } catch (ObjectDisposedException) { } } }
protected override void PacketReceived(UDPPacketBuffer buffer) { // Debugging/Profiling //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } //catch (Exception) { } LLUDPClient udpClient = null; Packet packet = null; int packetEnd = buffer.DataLength - 1; IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; #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) { } // Fail-safe check if (packet == null) { m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse {0} byte packet from {1}:", buffer.DataLength, buffer.RemoteEndPoint); m_log.Error(Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); return; } #endregion Decoding #region Packet to Client Mapping // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { object[] array = new object[] { buffer, packet }; if (m_asyncPacketHandling) Util.FireAndForget(HandleUseCircuitCode, array); else HandleUseCircuitCode(array); return; } // Determine which agent this packet came from IClientAPI client; if (!m_scene.ClientManager.TryGetValue (address, out client) || !(client is LLClientView)) { if(client != null) m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); return; } udpClient = ((LLClientView)client).UDPClient; if (!udpClient.IsConnected) return; #endregion Packet to Client Mapping // Stats tracking Interlocked.Increment(ref udpClient.PacketsReceived); int now = Environment.TickCount & Int32.MaxValue; udpClient.TickLastPacketReceived = now; #region ACK Receiving // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { for (int i = 0; i < packet.Header.AckList.Length; i++) udpClient.NeedAcks.Acknowledge(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++) udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent); // We don't need to do anything else with PacketAck packets return; } #endregion ACK Receiving #region ACK Sending if (packet.Header.Reliable) { udpClient.PendingAcks.Enqueue(packet.Header.Sequence); // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove // 2*MTU bytes from the value and send ACKs, and finally add the local value back to // client.BytesSinceLastACK. Lockless thread safety int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); bytesSinceLastACK += buffer.DataLength; if (bytesSinceLastACK > LLUDPServer.MTU * 2) { bytesSinceLastACK -= LLUDPServer.MTU * 2; SendAcks(udpClient); } Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); } #endregion ACK Sending #region Incoming Packet Accounting // Check the archive of received reliable packet IDs to see whether we already received this packet if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) { //if (packet.Header.Resent) // m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); //else // m_log.Warn("[LLUDPSERVER]: Received a duplicate (not marked as resend) of packet #" + packet.Header.Sequence + ", type: " + packet.Type); // Avoid firing a callback twice for the same packet return; } #endregion Incoming Packet Accounting #region BinaryStats LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); #endregion BinaryStats #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(udpClient, startPing.PingID.PingID); if ((Environment.TickCount - m_elapsedMSSinceLastStatReport) >= 3000) { udpClient.SendPacketStats(); m_elapsedMSSinceLastStatReport = Environment.TickCount; } return; } else if (packet.Type == PacketType.CompletePingCheck) { // We don't currently track client ping times return; } #endregion Ping Check Handling // Inbox insertion packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); }
public void AsyncBeginSend(UDPPacketBuffer buf) { rwLock.AcquireReaderLock(-1); if (!shutdownFlag) { try { Interlocked.Increment(ref rwOperationCount); udpSocket.BeginSendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint, new AsyncCallback(AsyncEndSend), buf); } catch (SocketException) { //Logger.Log( // "A SocketException occurred in UDPServer.AsyncBeginSend()", // Helpers.LogLevel.Error, se); } } rwLock.ReleaseReaderLock(); }
public override void PacketReceived(UDPPacketBuffer buffer) { // Debugging/Profiling //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } //catch (Exception) { } // m_log.DebugFormat( // "[LLUDPSERVER]: Packet received from {0} in {1}", buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); LLUDPClient udpClient = null; Packet packet = null; int packetEnd = buffer.DataLength - 1; IPEndPoint endPoint = (IPEndPoint)buffer.RemoteEndPoint; #region Decoding if (buffer.DataLength < 7) { // m_log.WarnFormat( // "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}", // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); RecordMalformedInboundPacket(endPoint); return; // Drop undersized packet } int headerLen = 7; if (buffer.Data[6] == 0xFF) { if (buffer.Data[7] == 0xFF) headerLen = 10; else headerLen = 8; } if (buffer.DataLength < headerLen) { // m_log.WarnFormat( // "[LLUDPSERVER]: Dropping packet with malformed header received from {0} in {1}", // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); RecordMalformedInboundPacket(endPoint); return; // Malformed header } 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); // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all // bytes are copied out). packet = PacketPool.Instance.GetPacket(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 (Exception e) { if (IncomingMalformedPacketCount < 100) m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); } // Fail-safe check if (packet == null) { if (IncomingMalformedPacketCount < 100) { m_log.WarnFormat("[LLUDPSERVER]: Malformed data, cannot parse {0} byte packet from {1}, data {2}:", buffer.DataLength, buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); } RecordMalformedInboundPacket(endPoint); return; } #endregion Decoding #region Packet to Client Mapping // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { // We need to copy the endpoint so that it doesn't get changed when another thread reuses the // buffer. object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; Util.FireAndForget(HandleUseCircuitCode, array); return; } else if (packet.Type == PacketType.CompleteAgentMovement) { // Send ack straight away to let the viewer know that we got it. SendAckImmediate(endPoint, packet.Header.Sequence); // We need to copy the endpoint so that it doesn't get changed when another thread reuses the // buffer. object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; Util.FireAndForget(HandleCompleteMovementIntoRegion, array); return; } // Determine which agent this packet came from IClientAPI client; if (!m_scene.TryGetClient(endPoint, out client) || !(client is LLClientView)) { //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); IncomingOrphanedPacketCount++; if ((IncomingOrphanedPacketCount % 10000) == 0) m_log.WarnFormat( "[LLUDPSERVER]: Received {0} orphaned packets so far. Last was from {1}", IncomingOrphanedPacketCount, endPoint); return; } udpClient = ((LLClientView)client).UDPClient; if (!udpClient.IsConnected) return; #endregion Packet to Client Mapping // Stats tracking Interlocked.Increment(ref udpClient.PacketsReceived); int now = Environment.TickCount & Int32.MaxValue; udpClient.TickLastPacketReceived = now; #region ACK Receiving // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { // m_log.DebugFormat( // "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}", // packet.Header.AckList.Length, client.Name, m_scene.Name); for (int i = 0; i < packet.Header.AckList.Length; i++) udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent); } // Handle PacketAck packets if (packet.Type == PacketType.PacketAck) { PacketAckPacket ackPacket = (PacketAckPacket)packet; // m_log.DebugFormat( // "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}", // ackPacket.Packets.Length, client.Name, m_scene.Name); for (int i = 0; i < ackPacket.Packets.Length; i++) udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent); // We don't need to do anything else with PacketAck packets return; } #endregion ACK Receiving #region ACK Sending if (packet.Header.Reliable) { // m_log.DebugFormat( // "[LLUDPSERVER]: Adding ack request for {0} {1} from {2} in {3}", // packet.Type, packet.Header.Sequence, client.Name, m_scene.Name); udpClient.PendingAcks.Enqueue(packet.Header.Sequence); // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove // 2*MTU bytes from the value and send ACKs, and finally add the local value back to // client.BytesSinceLastACK. Lockless thread safety int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); bytesSinceLastACK += buffer.DataLength; if (bytesSinceLastACK > LLUDPServer.MTU * 2) { bytesSinceLastACK -= LLUDPServer.MTU * 2; SendAcks(udpClient); } Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); } #endregion ACK Sending #region Incoming Packet Accounting // Check the archive of received reliable packet IDs to see whether we already received this packet if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) { if (packet.Header.Resent) m_log.DebugFormat( "[LLUDPSERVER]: Received a resend of already processed packet #{0}, type {1} from {2}", packet.Header.Sequence, packet.Type, client.Name); else m_log.WarnFormat( "[LLUDPSERVER]: Received a duplicate (not marked as resend) of packet #{0}, type {1} from {2}", packet.Header.Sequence, packet.Type, client.Name); // Avoid firing a callback twice for the same packet return; } #endregion Incoming Packet Accounting #region BinaryStats LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); #endregion BinaryStats if (packet.Type == PacketType.AgentUpdate) { if (m_discardAgentUpdates) return; ((LLClientView)client).TotalAgentUpdates++; AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; LLClientView llClient = client as LLClientView; if (agentUpdate.AgentData.SessionID != client.SessionId || agentUpdate.AgentData.AgentID != client.AgentId || !(llClient == null || llClient.CheckAgentUpdateSignificance(agentUpdate.AgentData)) ) { PacketPool.Instance.ReturnPacket(packet); return; } } #region Ping Check Handling if (packet.Type == PacketType.StartPingCheck) { // m_log.DebugFormat("[LLUDPSERVER]: Handling ping from {0} in {1}", client.Name, m_scene.Name); // We don't need to do anything else with ping checks StartPingCheckPacket startPing = (StartPingCheckPacket)packet; CompletePing(udpClient, startPing.PingID.PingID); if ((Environment.TickCount - m_elapsedMSSinceLastStatReport) >= 3000) { udpClient.SendPacketStats(); m_elapsedMSSinceLastStatReport = Environment.TickCount; } return; } else if (packet.Type == PacketType.CompletePingCheck) { // We don't currently track client ping times return; } #endregion Ping Check Handling IncomingPacket incomingPacket; // Inbox insertion if (UsePools) { incomingPacket = m_incomingPacketPool.GetObject(); incomingPacket.Client = (LLClientView)client; incomingPacket.Packet = packet; } else { incomingPacket = new IncomingPacket((LLClientView)client, packet); } packetInbox.Enqueue(incomingPacket); }
protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
/// <summary> /// Start the process of sending a packet to the client. /// </summary> /// <param name="udpClient"></param> /// <param name="data"></param> /// <param name="type"></param> /// <param name="category"></param> /// <param name="method"> /// The method to call if the packet is not acked by the client. If null, then a standard /// resend of the packet is done. /// </param> public void SendPacketData( LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method) { 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(udpClient.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("[LLUDPSERVER]: 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) { Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } else { bufferSize = dataLength; buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); // m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + // type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length + ". Dropping packet"); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } } buffer.DataLength = dataLength; #region Queue or Send OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null); // If we were not provided a method for handling unacked, use the UDPServer default method outgoingPacket.UnackedMethod = ((method == null) ? delegate(OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method); // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will // continue to display the deleted object until relog. Therefore, we need to always queue a kill object // packet so that it isn't sent before a queued update packet. bool requestQueue = type == PacketType.KillObject; if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, requestQueue)) SendPacketFinal(outgoingPacket); #endregion Queue or Send }
protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) { // Stats tracking Interlocked.Add(ref Stats.SentBytes, bytesSent); Interlocked.Increment(ref Stats.SentPackets); Client.Network.RaisePacketSentEvent(buffer.Data, bytesSent, this); }
// these abstract methods must be implemented in a derived class to actually do // something with the packets that are sent and received. protected abstract void PacketReceived(UDPPacketBuffer buffer);
public void FreeUDPBuffer(UDPPacketBuffer buf) { m_udpServer.FreeUDPBuffer(buf); }
private void AsyncBeginReceive() { // this method actually kicks off the async read on the socket. // we aquire a reader lock here to ensure that no other thread // is trying to set shutdownFlag and close the socket. rwLock.AcquireReaderLock(-1); if (!shutdownFlag) { // increment the count of pending operations Interlocked.Increment(ref rwOperationCount); // allocate a packet buffer //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut(); UDPPacketBuffer buf = new UDPPacketBuffer(); try { // kick off an async read udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, //ref wrappedBuffer.Instance.RemoteEndPoint, ref buf.RemoteEndPoint, new AsyncCallback(AsyncEndReceive), //wrappedBuffer); buf); } catch (SocketException) { // something bad happened //Logger.Log( // "A SocketException occurred in UDPServer.AsyncBeginReceive()", // Helpers.LogLevel.Error, se); // an error occurred, therefore the operation is void. Decrement the reference count. Interlocked.Decrement(ref rwOperationCount); } } // we're done with the socket for now, release the reader lock. rwLock.ReleaseReaderLock(); }
public OutgoingPacket(Simulator simulator, UDPPacketBuffer buffer, PacketType type) { Simulator = simulator; Buffer = buffer; this.Type = type; }
/// <summary> /// Disconnect from this simulator /// </summary> public void Disconnect(bool sendCloseCircuit) { if (connected) { connected = false; // Destroy the timers if (AckTimer != null) AckTimer.Dispose(); if (StatsTimer != null) StatsTimer.Dispose(); if (PingTimer != null) PingTimer.Dispose(); AckTimer = null; StatsTimer = null; PingTimer = null; // Kill the current CAPS system if (Caps != null) { Caps.Disconnect(true); Caps = null; } if (sendCloseCircuit) { // Try to send the CloseCircuit notice CloseCircuitPacket close = new CloseCircuitPacket(); UDPPacketBuffer buf = new UDPPacketBuffer(remoteEndPoint); byte[] data = close.ToBytes(); Buffer.BlockCopy(data, 0, buf.Data, 0, data.Length); buf.DataLength = data.Length; AsyncBeginSend(buf); } // Shut the socket communication down Stop(); } }
public void AsyncBeginSend(UDPPacketBuffer buf) { if (!m_shutdownFlag) { try { m_udpSocket.BeginSendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint, AsyncEndSend, buf); } catch (SocketException) { } catch (ObjectDisposedException) { } } }
protected override void PacketReceived(UDPPacketBuffer buffer) { Packet packet = null; // Check if this packet came from the server we expected it to come from if (!remoteEndPoint.Address.Equals(((IPEndPoint)buffer.RemoteEndPoint).Address)) { Logger.Log("Received " + buffer.DataLength + " bytes of data from unrecognized source " + ((IPEndPoint)buffer.RemoteEndPoint).ToString(), Helpers.LogLevel.Warning, Client); return; } // Update the disconnect flag so this sim doesn't time out DisconnectCandidate = false; #region Packet Decoding int packetEnd = buffer.DataLength - 1; 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[8192] : null); } catch (MalformedDataException) { Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}", Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error); } // Fail-safe check if (packet == null) { Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning, Client); return; } Interlocked.Add(ref Stats.RecvBytes, buffer.DataLength); Interlocked.Increment(ref Stats.RecvPackets); #endregion Packet Decoding if (packet.Header.Resent) Interlocked.Increment(ref Stats.ReceivedResends); #region ACK Receiving // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { lock (NeedAck) { for (int i = 0; i < packet.Header.AckList.Length; i++) NeedAck.Remove(packet.Header.AckList[i]); } } // Handle PacketAck packets if (packet.Type == PacketType.PacketAck) { PacketAckPacket ackPacket = (PacketAckPacket)packet; lock (NeedAck) { for (int i = 0; i < ackPacket.Packets.Length; i++) NeedAck.Remove(ackPacket.Packets[i].ID); } } #endregion ACK Receiving #region ACK Sending // Add this packet to the list of ACKs that need to be sent out uint sequence = (uint)packet.Header.Sequence; PendingAcks.Enqueue(sequence); int pendingAckCount = Interlocked.Increment(ref PendingAckCount); // Send out ACKs if we have a lot of them if (pendingAckCount >= Client.Settings.MAX_PENDING_ACKS) SendAcks(); #endregion ACK Sending // Check the archive of received packet IDs to see whether we already received this packet if (packet.Header.Reliable && !PacketArchive.TryEnqueue(packet.Header.Sequence)) { if (packet.Header.Resent) Logger.DebugLog("Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); else Logger.Log("Received a duplicate (not marked as resend) of packet #" + packet.Header.Sequence + ", type: " + packet.Type, Helpers.LogLevel.Warning); // Avoid firing a callback twice for the same packet return; } #region Inbox Insertion NetworkManager.IncomingPacket incomingPacket; incomingPacket.Simulator = this; incomingPacket.Packet = packet; Network.PacketInbox.Enqueue(incomingPacket); #endregion Inbox Insertion #region Stats Tracking if (Client.Settings.TRACK_UTILIZATION) { Client.Stats.Update(packet.Type.ToString(), OpenMetaverse.Stats.Type.Packet, 0, packet.Length); } #endregion }
public void TestAddClient() { TestHelpers.InMethod(); // XmlConfigurator.Configure(); TestScene scene = SceneHelpers.SetupScene(); uint myCircuitCode = 123456; UUID myAgentUuid = TestHelpers.ParseTail(0x1); UUID mySessionUuid = TestHelpers.ParseTail(0x2); IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999); uint port = 0; AgentCircuitManager acm = scene.AuthenticateHandler; TestLLUDPServer llUdpServer = new TestLLUDPServer(IPAddress.Any, ref port, 0, false, new IniConfigSource(), acm); llUdpServer.AddScene(scene); UseCircuitCodePacket uccp = new UseCircuitCodePacket(); UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock = new UseCircuitCodePacket.CircuitCodeBlock(); uccpCcBlock.Code = myCircuitCode; uccpCcBlock.ID = myAgentUuid; uccpCcBlock.SessionID = mySessionUuid; uccp.CircuitCode = uccpCcBlock; byte[] uccpBytes = uccp.ToBytes(); UDPPacketBuffer upb = new UDPPacketBuffer(testEp, uccpBytes.Length); upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor. Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length); llUdpServer.PacketReceived(upb); // Presence shouldn't exist since the circuit manager doesn't know about this circuit for authentication yet Assert.That(scene.GetScenePresence(myAgentUuid), Is.Null); AgentCircuitData acd = new AgentCircuitData(); acd.AgentID = myAgentUuid; acd.SessionID = mySessionUuid; acm.AddNewCircuit(myCircuitCode, acd); llUdpServer.PacketReceived(upb); // Should succeed now ScenePresence sp = scene.GetScenePresence(myAgentUuid); Assert.That(sp.UUID, Is.EqualTo(myAgentUuid)); Assert.That(llUdpServer.PacketsSent.Count, Is.EqualTo(1)); Packet packet = llUdpServer.PacketsSent[0]; Assert.That(packet, Is.InstanceOf(typeof(PacketAckPacket))); PacketAckPacket ackPacket = packet as PacketAckPacket; Assert.That(ackPacket.Packets.Length, Is.EqualTo(1)); Assert.That(ackPacket.Packets[0].ID, Is.EqualTo(0)); }