2008-05-02 19:21:33 +00:00
/ *
* Copyright ( c ) Contributors , http : //opensimulator.org/
* See CONTRIBUTORS . TXT for a full list of copyright holders .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
2009-06-01 06:37:14 +00:00
* * Neither the name of the OpenSimulator Project nor the
2008-05-02 19:21:33 +00:00
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ` ` AS IS ' ' AND ANY
* EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
* LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
using System ;
using System.Collections.Generic ;
2011-02-08 20:06:14 +00:00
using System.Diagnostics ;
2009-11-05 20:01:40 +00:00
using System.IO ;
2008-05-02 19:21:33 +00:00
using System.Net ;
using System.Net.Sockets ;
using System.Reflection ;
2009-10-06 00:38:14 +00:00
using System.Threading ;
2008-05-02 19:21:33 +00:00
using log4net ;
2008-11-03 18:33:35 +00:00
using Nini.Config ;
2009-02-12 09:53:12 +00:00
using OpenMetaverse.Packets ;
2008-05-02 19:21:33 +00:00
using OpenSim.Framework ;
2012-10-16 20:55:00 +00:00
using OpenSim.Framework.Console ;
2012-07-25 22:11:50 +00:00
using OpenSim.Framework.Monitoring ;
2009-02-06 16:55:34 +00:00
using OpenSim.Region.Framework.Scenes ;
2009-10-06 00:38:14 +00:00
using OpenMetaverse ;
2008-05-02 19:21:33 +00:00
2009-10-21 01:19:17 +00:00
using TokenBucket = OpenSim . Region . ClientStack . LindenUDP . TokenBucket ;
2008-05-02 19:21:33 +00:00
namespace OpenSim.Region.ClientStack.LindenUDP
{
2009-10-06 19:13:16 +00:00
/// <summary>
/// A shim around LLUDPServer that implements the IClientNetworkServer interface
/// </summary>
public sealed class LLUDPServerShim : IClientNetworkServer
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
LLUDPServer m_udpServer ;
2008-09-09 01:26:48 +00:00
2009-10-06 00:38:14 +00:00
public LLUDPServerShim ( )
2008-05-02 19:21:33 +00:00
{
}
2009-10-06 00:38:14 +00:00
public void Initialise ( IPAddress listenIP , ref uint port , int proxyPortOffsetParm , bool allow_alternate_port , IConfigSource configSource , AgentCircuitManager circuitManager )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
m_udpServer = new LLUDPServer ( listenIP , ref port , proxyPortOffsetParm , allow_alternate_port , configSource , circuitManager ) ;
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
public void AddScene ( IScene scene )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
m_udpServer . AddScene ( scene ) ;
2012-11-15 01:14:18 +00:00
StatsManager . RegisterStat (
new Stat (
"IncomingPacketsProcessedCount" ,
"Number of inbound UDP packets processed" ,
"Number of inbound UDP packets processed" ,
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
2012-11-16 04:36:22 +00:00
MeasuresOfInterest . AverageChangeOverTime ,
2012-11-15 01:14:18 +00:00
stat = > stat . Value = m_udpServer . IncomingPacketsProcessed ,
StatVerbosity . Debug ) ) ;
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
public bool HandlesRegion ( Location x )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
return m_udpServer . HandlesRegion ( x ) ;
2008-05-02 19:21:33 +00:00
}
public void Start ( )
{
2009-10-06 00:38:14 +00:00
m_udpServer . Start ( ) ;
2008-05-02 19:21:33 +00:00
}
public void Stop ( )
{
2009-10-06 00:38:14 +00:00
m_udpServer . Stop ( ) ;
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
}
2009-10-06 19:13:16 +00:00
/// <summary>
/// The LLUDP server for a region. This handles incoming and outgoing
/// packets for all UDP connections to the region
/// </summary>
2009-10-08 21:25:14 +00:00
public class LLUDPServer : OpenSimUDPBase
2009-10-06 00:38:14 +00:00
{
2009-10-16 21:17:13 +00:00
/// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary>
public const int MTU = 1400 ;
2009-10-06 00:38:14 +00:00
private static readonly ILog m_log = LogManager . GetLogger ( MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2008-05-02 19:21:33 +00:00
2009-10-21 07:18:35 +00:00
/// <summary>The measured resolution of Environment.TickCount</summary>
public readonly float TickCountResolution ;
2012-10-12 00:39:37 +00:00
2010-05-12 22:59:48 +00:00
/// <summary>Number of prim updates to put on the queue each time the
2009-10-23 17:35:47 +00:00
/// OnQueueEmpty event is triggered for updates</summary>
2010-05-12 22:59:48 +00:00
public readonly int PrimUpdatesPerCallback ;
2012-10-12 00:39:37 +00:00
2009-10-23 17:35:47 +00:00
/// <summary>Number of texture packets to put on the queue each time the
/// OnQueueEmpty event is triggered for textures</summary>
public readonly int TextureSendLimit ;
2009-10-21 07:18:35 +00:00
2009-10-06 00:38:14 +00:00
/// <summary>Handlers for incoming packets</summary>
//PacketEventDictionary packetEvents = new PacketEventDictionary();
/// <summary>Incoming packets that are awaiting handling</summary>
private OpenMetaverse . BlockingQueue < IncomingPacket > packetInbox = new OpenMetaverse . BlockingQueue < IncomingPacket > ( ) ;
2012-10-12 00:39:37 +00:00
2009-10-06 00:38:14 +00:00
/// <summary></summary>
2009-10-13 21:50:03 +00:00
//private UDPClientCollection m_clients = new UDPClientCollection();
2009-10-06 00:38:14 +00:00
/// <summary>Bandwidth throttle for this UDP server</summary>
2009-10-27 10:51:47 +00:00
protected TokenBucket m_throttle ;
2011-01-21 00:38:16 +00:00
2009-10-06 00:38:14 +00:00
/// <summary>Bandwidth throttle rates for this UDP server</summary>
2011-01-21 00:38:16 +00:00
public ThrottleRates ThrottleRates { get ; private set ; }
2009-10-06 00:38:14 +00:00
/// <summary>Manages authentication for agent circuits</summary>
private AgentCircuitManager m_circuitManager ;
2012-10-12 00:39:37 +00:00
2009-10-06 00:38:14 +00:00
/// <summary>Reference to the scene this UDP server is attached to</summary>
2009-10-27 10:51:47 +00:00
protected Scene m_scene ;
2012-10-12 00:39:37 +00:00
2009-10-06 00:38:14 +00:00
/// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary>
private Location m_location ;
2012-10-12 00:39:37 +00:00
2009-10-14 18:43:31 +00:00
/// <summary>The size of the receive buffer for the UDP socket. This value
/// is passed up to the operating system and used in the system networking
/// stack. Use zero to leave this value as the default</summary>
private int m_recvBufferSize ;
2012-10-12 00:39:37 +00:00
2009-10-14 23:48:27 +00:00
/// <summary>Flag to process packets asynchronously or synchronously</summary>
private bool m_asyncPacketHandling ;
2012-10-12 00:39:37 +00:00
2009-10-21 18:59:48 +00:00
/// <summary>Tracks whether or not a packet was sent each round so we know
/// whether or not to sleep</summary>
private bool m_packetSent ;
2009-10-06 00:38:14 +00:00
2009-11-28 00:17:36 +00:00
/// <summary>Environment.TickCount of the last time that packet stats were reported to the scene</summary>
private int m_elapsedMSSinceLastStatReport = 0 ;
2012-10-12 00:39:37 +00:00
2009-10-21 22:22:23 +00:00
/// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary>
private int m_tickLastOutgoingPacketHandler ;
2012-10-12 00:39:37 +00:00
2009-10-21 22:22:23 +00:00
/// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary>
private int m_elapsedMSOutgoingPacketHandler ;
2012-10-12 00:39:37 +00:00
2009-10-21 22:22:23 +00:00
/// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary>
private int m_elapsed100MSOutgoingPacketHandler ;
2012-10-12 00:39:37 +00:00
2009-10-21 22:22:23 +00:00
/// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary>
private int m_elapsed500MSOutgoingPacketHandler ;
/// <summary>Flag to signal when clients should check for resends</summary>
2012-06-08 02:12:23 +00:00
protected bool m_resendUnacked ;
2009-10-21 22:22:23 +00:00
/// <summary>Flag to signal when clients should send ACKs</summary>
2012-06-08 02:12:23 +00:00
protected bool m_sendAcks ;
2009-10-21 22:22:23 +00:00
/// <summary>Flag to signal when clients should send pings</summary>
2012-06-08 02:12:23 +00:00
protected bool m_sendPing ;
2009-10-21 22:22:23 +00:00
2012-10-17 20:08:15 +00:00
private Pool < IncomingPacket > m_incomingPacketPool ;
2012-11-15 01:14:18 +00:00
/// <summary>
/// Stat for number of packets in the main pool awaiting use.
/// </summary>
private Stat m_poolCountStat ;
/// <summary>
/// Stat for number of packets in the inbound packet pool awaiting use.
/// </summary>
2012-10-23 01:44:15 +00:00
private Stat m_incomingPacketPoolStat ;
2009-10-30 22:07:56 +00:00
private int m_defaultRTO = 0 ;
private int m_maxRTO = 0 ;
2012-05-31 23:39:26 +00:00
private int m_ackTimeout = 0 ;
private int m_pausedAckTimeout = 0 ;
2010-08-08 00:57:02 +00:00
private bool m_disableFacelights = false ;
2009-10-06 00:38:14 +00:00
public Socket Server { get { return null ; } }
2011-07-27 06:35:19 +00:00
private int m_malformedCount = 0 ; // Guard against a spamming attack
2012-06-07 01:44:13 +00:00
/// <summary>
/// Record current outgoing client for monitoring purposes.
/// </summary>
private IClientAPI m_currentOutgoingClient ;
/// <summary>
/// Recording current incoming client for monitoring purposes.
/// </summary>
private IClientAPI m_currentIncomingClient ;
2012-10-16 22:35:05 +00:00
public LLUDPServer (
IPAddress listenIP , ref uint port , int proxyPortOffsetParm , bool allow_alternate_port ,
IConfigSource configSource , AgentCircuitManager circuitManager )
2009-10-09 08:53:06 +00:00
: base ( listenIP , ( int ) port )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
#region Environment.TickCount Measurement
// Measure the resolution of Environment.TickCount
2009-10-21 07:18:35 +00:00
TickCountResolution = 0f ;
2009-10-06 00:38:14 +00:00
for ( int i = 0 ; i < 5 ; i + + )
{
int start = Environment . TickCount ;
int now = start ;
while ( now = = start )
now = Environment . TickCount ;
2009-10-21 07:18:35 +00:00
TickCountResolution + = ( float ) ( now - start ) * 0.2f ;
2009-10-06 00:38:14 +00:00
}
m_log . Info ( "[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution + "ms" ) ;
2009-10-21 07:18:35 +00:00
TickCountResolution = ( float ) Math . Ceiling ( TickCountResolution ) ;
2009-10-06 00:38:14 +00:00
#endregion Environment.TickCount Measurement
m_circuitManager = circuitManager ;
2009-10-14 18:52:48 +00:00
int sceneThrottleBps = 0 ;
2012-10-23 01:44:15 +00:00
bool usePools = false ;
2009-10-06 00:38:14 +00:00
2009-10-14 18:43:31 +00:00
IConfig config = configSource . Configs [ "ClientStack.LindenUDP" ] ;
if ( config ! = null )
{
2010-10-30 00:35:12 +00:00
m_asyncPacketHandling = config . GetBoolean ( "async_packet_handling" , true ) ;
2009-10-14 18:43:31 +00:00
m_recvBufferSize = config . GetInt ( "client_socket_rcvbuf_size" , 0 ) ;
2009-10-14 18:52:48 +00:00
sceneThrottleBps = config . GetInt ( "scene_throttle_max_bps" , 0 ) ;
2009-10-23 17:35:47 +00:00
2010-05-12 22:59:48 +00:00
PrimUpdatesPerCallback = config . GetInt ( "PrimUpdatesPerCallback" , 100 ) ;
2009-10-23 17:35:47 +00:00
TextureSendLimit = config . GetInt ( "TextureSendLimit" , 20 ) ;
2009-10-30 22:07:56 +00:00
m_defaultRTO = config . GetInt ( "DefaultRTO" , 0 ) ;
m_maxRTO = config . GetInt ( "MaxRTO" , 0 ) ;
2010-08-08 00:57:02 +00:00
m_disableFacelights = config . GetBoolean ( "DisableFacelights" , false ) ;
2012-05-31 23:39:26 +00:00
m_ackTimeout = 1000 * config . GetInt ( "AckTimeout" , 60 ) ;
m_pausedAckTimeout = 1000 * config . GetInt ( "PausedAckTimeout" , 300 ) ;
2009-10-23 17:35:47 +00:00
}
else
{
2010-05-12 22:59:48 +00:00
PrimUpdatesPerCallback = 100 ;
2009-10-23 17:35:47 +00:00
TextureSendLimit = 20 ;
2012-05-31 23:39:26 +00:00
m_ackTimeout = 1000 * 60 ; // 1 minute
m_pausedAckTimeout = 1000 * 300 ; // 5 minutes
2009-10-14 18:43:31 +00:00
}
2012-10-05 00:43:29 +00:00
// FIXME: This actually only needs to be done once since the PacketPool is shared across all servers.
// However, there is no harm in temporarily doing it multiple times.
IConfig packetConfig = configSource . Configs [ "PacketPool" ] ;
if ( packetConfig ! = null )
{
PacketPool . Instance . RecyclePackets = packetConfig . GetBoolean ( "RecyclePackets" , true ) ;
PacketPool . Instance . RecycleDataBlocks = packetConfig . GetBoolean ( "RecycleDataBlocks" , true ) ;
2012-10-23 01:44:15 +00:00
usePools = packetConfig . GetBoolean ( "RecycleBaseUDPPackets" , usePools ) ;
2012-10-05 00:43:29 +00:00
}
2009-11-05 20:01:40 +00:00
#region BinaryStats
config = configSource . Configs [ "Statistics.Binary" ] ;
m_shouldCollectStats = false ;
if ( config ! = null )
{
2013-03-15 20:59:34 +00:00
m_shouldCollectStats = config . GetBoolean ( "Enabled" , false ) ;
binStatsMaxFilesize = TimeSpan . FromSeconds ( config . GetInt ( "packet_headers_period_seconds" , 300 ) ) ;
binStatsDir = config . GetString ( "stats_dir" , "." ) ;
m_aggregatedBWStats = config . GetBoolean ( "aggregatedBWStats" , false ) ;
}
#endregion BinaryStats
2009-11-05 20:01:40 +00:00
2011-04-11 16:06:28 +00:00
m_throttle = new TokenBucket ( null , sceneThrottleBps ) ;
2011-01-21 00:38:16 +00:00
ThrottleRates = new ThrottleRates ( configSource ) ;
2012-10-17 20:08:15 +00:00
2012-10-23 01:44:15 +00:00
if ( usePools )
EnablePools ( ) ;
2008-05-02 19:21:33 +00:00
}
2009-10-14 21:25:58 +00:00
public void Start ( )
2008-06-20 01:35:54 +00:00
{
2012-10-16 20:55:00 +00:00
StartInbound ( ) ;
StartOutbound ( ) ;
2009-10-06 00:38:14 +00:00
2012-10-16 20:55:00 +00:00
m_elapsedMSSinceLastStatReport = Environment . TickCount ;
}
private void StartInbound ( )
{
2012-06-07 01:44:13 +00:00
m_log . InfoFormat (
2012-10-16 22:35:05 +00:00
"[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}" ,
m_asyncPacketHandling ? "asynchronous" : "synchronous" , UsePools ) ;
2009-10-14 23:48:27 +00:00
2012-10-16 20:55:00 +00:00
base . StartInbound ( m_recvBufferSize , m_asyncPacketHandling ) ;
2009-10-06 00:38:14 +00:00
2012-10-16 20:55:00 +00:00
// This thread will process the packets received that are placed on the packetInbox
2012-02-24 05:02:33 +00:00
Watchdog . StartThread (
2012-06-07 01:44:13 +00:00
IncomingPacketHandler ,
string . Format ( "Incoming Packets ({0})" , m_scene . RegionInfo . RegionName ) ,
ThreadPriority . Normal ,
false ,
true ,
GetWatchdogIncomingAlarmData ,
2012-07-03 23:15:03 +00:00
Watchdog . DEFAULT_WATCHDOG_TIMEOUT_MS ) ;
2012-10-16 20:55:00 +00:00
}
2012-06-07 01:44:13 +00:00
2012-10-16 22:35:05 +00:00
private new void StartOutbound ( )
2012-10-16 20:55:00 +00:00
{
m_log . Info ( "[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server" ) ;
base . StartOutbound ( ) ;
2012-02-24 05:02:33 +00:00
Watchdog . StartThread (
2012-06-07 01:44:13 +00:00
OutgoingPacketHandler ,
string . Format ( "Outgoing Packets ({0})" , m_scene . RegionInfo . RegionName ) ,
ThreadPriority . Normal ,
false ,
true ,
GetWatchdogOutgoingAlarmData ,
2012-07-03 23:15:03 +00:00
Watchdog . DEFAULT_WATCHDOG_TIMEOUT_MS ) ;
2012-10-16 20:55:00 +00:00
}
2012-02-24 05:02:33 +00:00
2012-10-16 22:35:05 +00:00
public void Stop ( )
2012-10-16 20:55:00 +00:00
{
m_log . Info ( "[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene . RegionInfo . RegionName ) ;
base . StopOutbound ( ) ;
base . StopInbound ( ) ;
2008-06-20 01:35:54 +00:00
}
2012-10-23 01:44:15 +00:00
protected override bool EnablePools ( )
{
if ( ! UsePools )
{
base . EnablePools ( ) ;
m_incomingPacketPool = new Pool < IncomingPacket > ( ( ) = > new IncomingPacket ( ) , 500 ) ;
return true ;
}
return false ;
}
protected override bool DisablePools ( )
{
if ( UsePools )
{
base . DisablePools ( ) ;
StatsManager . DeregisterStat ( m_incomingPacketPoolStat ) ;
// We won't null out the pool to avoid a race condition with code that may be in the middle of using it.
return true ;
}
return false ;
}
2012-11-15 01:14:18 +00:00
/// <summary>
/// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene
/// stats.
/// </summary>
private void EnablePoolStats ( )
{
m_poolCountStat
= new Stat (
"UDPPacketBufferPoolCount" ,
"Objects within the UDPPacketBuffer pool" ,
"The number of objects currently stored within the UDPPacketBuffer pool" ,
"" ,
"clientstack" ,
m_scene . Name ,
StatType . Pull ,
stat = > stat . Value = Pool . Count ,
StatVerbosity . Debug ) ;
StatsManager . RegisterStat ( m_poolCountStat ) ;
m_incomingPacketPoolStat
= new Stat (
"IncomingPacketPoolCount" ,
"Objects within incoming packet pool" ,
"The number of objects currently stored within the incoming packet pool" ,
"" ,
"clientstack" ,
m_scene . Name ,
StatType . Pull ,
stat = > stat . Value = m_incomingPacketPool . Count ,
StatVerbosity . Debug ) ;
StatsManager . RegisterStat ( m_incomingPacketPoolStat ) ;
}
/// <summary>
/// Disables pool stats.
/// </summary>
private void DisablePoolStats ( )
{
StatsManager . DeregisterStat ( m_poolCountStat ) ;
m_poolCountStat = null ;
StatsManager . DeregisterStat ( m_incomingPacketPoolStat ) ;
m_incomingPacketPoolStat = null ;
}
2012-06-07 01:44:13 +00:00
/// <summary>
/// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
/// </summary>
/// <returns></returns>
private string GetWatchdogIncomingAlarmData ( )
{
return string . Format (
"Client is {0}" ,
m_currentIncomingClient ! = null ? m_currentIncomingClient . Name : "none" ) ;
}
/// <summary>
/// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
/// </summary>
/// <returns></returns>
private string GetWatchdogOutgoingAlarmData ( )
{
return string . Format (
"Client is {0}" ,
m_currentOutgoingClient ! = null ? m_currentOutgoingClient . Name : "none" ) ;
}
2009-10-06 00:38:14 +00:00
public void AddScene ( IScene scene )
{
2009-10-19 22:19:09 +00:00
if ( m_scene ! = null )
2008-11-03 19:02:44 +00:00
{
2009-10-19 22:19:09 +00:00
m_log . Error ( "[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene" ) ;
return ;
2009-10-06 00:38:14 +00:00
}
2009-10-19 22:19:09 +00:00
if ( ! ( scene is Scene ) )
2009-10-06 00:38:14 +00:00
{
2009-10-19 22:19:09 +00:00
m_log . Error ( "[LLUDPSERVER]: AddScene() called with an unrecognized scene type " + scene . GetType ( ) ) ;
return ;
2009-10-06 00:38:14 +00:00
}
2009-10-19 22:19:09 +00:00
m_scene = ( Scene ) scene ;
m_location = new Location ( m_scene . RegionInfo . RegionHandle ) ;
2012-11-15 02:02:59 +00:00
// XXX: These stats are also pool stats but we register them separately since they are currently not
// turned on and off by EnablePools()/DisablePools()
StatsManager . RegisterStat (
new PercentageStat (
"PacketsReused" ,
"Packets reused" ,
"Number of packets reused out of all requests to the packet pool" ,
"clientstack" ,
m_scene . Name ,
StatType . Pull ,
stat = >
{ PercentageStat pstat = ( PercentageStat ) stat ;
pstat . Consequent = PacketPool . Instance . PacketsRequested ;
pstat . Antecedent = PacketPool . Instance . PacketsReused ; } ,
StatVerbosity . Debug ) ) ;
StatsManager . RegisterStat (
new PercentageStat (
"PacketDataBlocksReused" ,
"Packet data blocks reused" ,
"Number of data blocks reused out of all requests to the packet pool" ,
"clientstack" ,
m_scene . Name ,
StatType . Pull ,
stat = >
{ PercentageStat pstat = ( PercentageStat ) stat ;
pstat . Consequent = PacketPool . Instance . BlocksRequested ;
pstat . Antecedent = PacketPool . Instance . BlocksReused ; } ,
StatVerbosity . Debug ) ) ;
StatsManager . RegisterStat (
new Stat (
"PacketsPoolCount" ,
"Objects within the packet pool" ,
"The number of objects currently stored within the packet pool" ,
"" ,
"clientstack" ,
m_scene . Name ,
StatType . Pull ,
stat = > stat . Value = PacketPool . Instance . PacketsPooled ,
StatVerbosity . Debug ) ) ;
StatsManager . RegisterStat (
new Stat (
"PacketDataBlocksPoolCount" ,
"Objects within the packet data block pool" ,
"The number of objects currently stored within the packet data block pool" ,
"" ,
"clientstack" ,
m_scene . Name ,
StatType . Pull ,
stat = > stat . Value = PacketPool . Instance . BlocksPooled ,
StatVerbosity . Debug ) ) ;
2012-11-15 01:14:18 +00:00
// We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by
// scene name
if ( UsePools )
EnablePoolStats ( ) ;
2012-10-16 20:55:00 +00:00
MainConsole . Instance . Commands . AddCommand (
"Debug" ,
false ,
"debug lludp start" ,
"debug lludp start <in|out|all>" ,
"Control LLUDP packet processing." ,
"No effect if packet processing has already started.\n"
+ "in - start inbound processing.\n"
+ "out - start outbound processing.\n"
+ "all - start in and outbound processing.\n" ,
HandleStartCommand ) ;
MainConsole . Instance . Commands . AddCommand (
"Debug" ,
false ,
"debug lludp stop" ,
"debug lludp stop <in|out|all>" ,
"Stop LLUDP packet processing." ,
"No effect if packet processing has already stopped.\n"
+ "in - stop inbound processing.\n"
+ "out - stop outbound processing.\n"
+ "all - stop in and outbound processing.\n" ,
HandleStopCommand ) ;
2012-10-23 01:44:15 +00:00
MainConsole . Instance . Commands . AddCommand (
"Debug" ,
false ,
"debug lludp pool" ,
"debug lludp pool <on|off>" ,
"Turn object pooling within the lludp component on or off." ,
HandlePoolCommand ) ;
2012-10-16 20:55:00 +00:00
MainConsole . Instance . Commands . AddCommand (
"Debug" ,
false ,
"debug lludp status" ,
"debug lludp status" ,
"Return status of LLUDP packet processing." ,
HandleStatusCommand ) ;
}
private void HandleStartCommand ( string module , string [ ] args )
{
if ( args . Length ! = 4 )
{
MainConsole . Instance . Output ( "Usage: debug lludp start <in|out|all>" ) ;
return ;
}
string subCommand = args [ 3 ] ;
if ( subCommand = = "in" | | subCommand = = "all" )
StartInbound ( ) ;
if ( subCommand = = "out" | | subCommand = = "all" )
StartOutbound ( ) ;
}
private void HandleStopCommand ( string module , string [ ] args )
{
if ( args . Length ! = 4 )
{
MainConsole . Instance . Output ( "Usage: debug lludp stop <in|out|all>" ) ;
return ;
}
string subCommand = args [ 3 ] ;
if ( subCommand = = "in" | | subCommand = = "all" )
StopInbound ( ) ;
if ( subCommand = = "out" | | subCommand = = "all" )
StopOutbound ( ) ;
}
2012-10-23 01:44:15 +00:00
private void HandlePoolCommand ( string module , string [ ] args )
{
if ( args . Length ! = 4 )
{
MainConsole . Instance . Output ( "Usage: debug lludp pool <on|off>" ) ;
return ;
}
string enabled = args [ 3 ] ;
if ( enabled = = "on" )
{
if ( EnablePools ( ) )
2012-11-15 01:14:18 +00:00
{
EnablePoolStats ( ) ;
2012-10-23 01:44:15 +00:00
MainConsole . Instance . OutputFormat ( "Packet pools enabled on {0}" , m_scene . Name ) ;
2012-11-15 01:14:18 +00:00
}
2012-10-23 01:44:15 +00:00
}
else if ( enabled = = "off" )
{
if ( DisablePools ( ) )
2012-11-15 01:14:18 +00:00
{
DisablePoolStats ( ) ;
2012-10-23 01:44:15 +00:00
MainConsole . Instance . OutputFormat ( "Packet pools disabled on {0}" , m_scene . Name ) ;
2012-11-15 01:14:18 +00:00
}
2012-10-23 01:44:15 +00:00
}
else
{
MainConsole . Instance . Output ( "Usage: debug lludp pool <on|off>" ) ;
}
}
2012-10-16 20:55:00 +00:00
private void HandleStatusCommand ( string module , string [ ] args )
{
MainConsole . Instance . OutputFormat (
"IN LLUDP packet processing for {0} is {1}" , m_scene . Name , IsRunningInbound ? "enabled" : "disabled" ) ;
MainConsole . Instance . OutputFormat (
"OUT LLUDP packet processing for {0} is {1}" , m_scene . Name , IsRunningOutbound ? "enabled" : "disabled" ) ;
2012-10-23 01:44:15 +00:00
MainConsole . Instance . OutputFormat ( "LLUDP pools in {0} are {1}" , m_scene . Name , UsePools ? "on" : "off" ) ;
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
public bool HandlesRegion ( Location x )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
return x = = m_location ;
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
public void BroadcastPacket ( Packet packet , ThrottleOutPacketType category , bool sendToPausedAgents , bool allowSplitting )
2008-10-09 18:09:25 +00:00
{
2009-10-30 18:12:32 +00:00
// CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way
if ( ( packet . Type = = PacketType . CoarseLocationUpdate | | packet . Type = = PacketType . AvatarGroupsReply ) & & allowSplitting )
2009-10-06 09:38:00 +00:00
allowSplitting = false ;
2009-10-06 00:38:14 +00:00
if ( allowSplitting & & packet . HasVariableBlocks )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
byte [ ] [ ] datas = packet . ToBytesMultiple ( ) ;
int packetCount = datas . Length ;
2008-10-09 18:09:25 +00:00
2009-10-30 07:08:41 +00:00
if ( packetCount < 1 )
m_log . Error ( "[LLUDPSERVER]: Failed to split " + packet . Type + " with estimated length " + packet . Length ) ;
2008-10-09 18:09:25 +00:00
2009-10-06 00:38:14 +00:00
for ( int i = 0 ; i < packetCount ; i + + )
{
byte [ ] data = datas [ i ] ;
2009-10-26 23:33:04 +00:00
m_scene . ForEachClient (
2009-10-13 21:50:03 +00:00
delegate ( IClientAPI client )
{
if ( client is LLClientView )
2011-04-18 23:48:49 +00:00
SendPacketData ( ( ( LLClientView ) client ) . UDPClient , data , packet . Type , category , null ) ;
2009-10-13 21:50:03 +00:00
}
) ;
2008-10-09 18:09:25 +00:00
}
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
else
2008-10-09 18:09:25 +00:00
{
2009-10-06 00:38:14 +00:00
byte [ ] data = packet . ToBytes ( ) ;
2009-10-26 23:33:04 +00:00
m_scene . ForEachClient (
2009-10-13 21:50:03 +00:00
delegate ( IClientAPI client )
{
if ( client is LLClientView )
2011-04-18 23:48:49 +00:00
SendPacketData ( ( ( LLClientView ) client ) . UDPClient , data , packet . Type , category , null ) ;
2009-10-13 21:50:03 +00:00
}
) ;
2009-09-30 16:00:09 +00:00
}
2008-08-09 05:26:33 +00:00
}
2009-10-06 00:38:14 +00:00
2010-12-01 22:23:42 +00:00
/// <summary>
/// Start the process of sending a packet to the client.
/// </summary>
/// <param name="udpClient"></param>
/// <param name="packet"></param>
/// <param name="category"></param>
/// <param name="allowSplitting"></param>
2011-10-12 18:22:30 +00:00
/// <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>
2011-12-08 21:45:02 +00:00
public virtual void SendPacket (
2011-10-12 18:22:30 +00:00
LLUDPClient udpClient , Packet packet , ThrottleOutPacketType category , bool allowSplitting , UnackedPacketMethod method )
2008-08-09 05:26:33 +00:00
{
2009-10-06 09:38:00 +00:00
// CoarseLocationUpdate packets cannot be split in an automated way
if ( packet . Type = = PacketType . CoarseLocationUpdate & & allowSplitting )
allowSplitting = false ;
2009-10-06 00:38:14 +00:00
if ( allowSplitting & & packet . HasVariableBlocks )
2008-10-31 19:41:07 +00:00
{
2009-10-06 00:38:14 +00:00
byte [ ] [ ] datas = packet . ToBytesMultiple ( ) ;
int packetCount = datas . Length ;
2009-10-30 07:08:41 +00:00
if ( packetCount < 1 )
m_log . Error ( "[LLUDPSERVER]: Failed to split " + packet . Type + " with estimated length " + packet . Length ) ;
2009-10-06 00:38:14 +00:00
for ( int i = 0 ; i < packetCount ; i + + )
2009-03-25 19:30:36 +00:00
{
2009-10-06 00:38:14 +00:00
byte [ ] data = datas [ i ] ;
2011-04-18 23:48:49 +00:00
SendPacketData ( udpClient , data , packet . Type , category , method ) ;
2009-03-25 19:30:36 +00:00
}
2009-10-06 00:38:14 +00:00
}
else
{
byte [ ] data = packet . ToBytes ( ) ;
2011-04-18 23:48:49 +00:00
SendPacketData ( udpClient , data , packet . Type , category , method ) ;
2009-10-06 00:38:14 +00:00
}
2012-10-11 22:58:37 +00:00
PacketPool . Instance . ReturnPacket ( packet ) ;
2009-10-06 00:38:14 +00:00
}
2009-03-25 19:30:36 +00:00
2010-12-01 22:23:42 +00:00
/// <summary>
/// Start the process of sending a packet to the client.
/// </summary>
/// <param name="udpClient"></param>
/// <param name="data"></param>
/// <param name="type"></param>
2011-10-12 18:22:30 +00:00
/// <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 )
2009-10-06 00:38:14 +00:00
{
2009-10-13 17:38:35 +00:00
int dataLength = data . Length ;
bool doZerocode = ( data [ 0 ] & Helpers . MSG_ZEROCODED ) ! = 0 ;
2009-10-16 21:26:58 +00:00
bool doCopy = true ;
2009-10-13 17:38:35 +00:00
2009-10-06 00:38:14 +00:00
// 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
2009-11-04 23:12:56 +00:00
int bufferSize = ( dataLength > 180 ) ? LLUDPServer . MTU : 200 ;
2009-10-06 00:38:14 +00:00
2009-10-13 18:14:45 +00:00
UDPPacketBuffer buffer = new UDPPacketBuffer ( udpClient . RemoteEndPoint , bufferSize ) ;
2008-10-31 19:41:07 +00:00
2009-10-06 00:38:14 +00:00
// Zerocode if needed
if ( doZerocode )
{
2009-10-16 21:26:58 +00:00
try
{
dataLength = Helpers . ZeroEncode ( data , dataLength , buffer . Data ) ;
doCopy = false ;
}
2009-10-06 00:38:14 +00:00
catch ( IndexOutOfRangeException )
{
// The packet grew larger than the bufferSize while zerocoding.
// Remove the MSG_ZEROCODED flag and send the unencoded data
// instead
2009-10-16 20:22:45 +00:00
m_log . Debug ( "[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength +
" and BufferLength=" + buffer . Data . Length + ". Removing MSG_ZEROCODED flag" ) ;
2009-10-06 00:38:14 +00:00
data [ 0 ] = ( byte ) ( data [ 0 ] & ~ Helpers . MSG_ZEROCODED ) ;
2009-03-25 19:30:36 +00:00
}
2009-10-06 00:38:14 +00:00
}
2009-10-16 21:26:58 +00:00
// If the packet data wasn't already copied during zerocoding, copy it now
if ( doCopy )
2009-10-06 00:38:14 +00:00
{
2009-10-16 21:26:58 +00:00
if ( dataLength < = buffer . Data . Length )
{
Buffer . BlockCopy ( data , 0 , buffer . Data , 0 , dataLength ) ;
}
else
{
2009-11-04 23:12:56 +00:00
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 ) ;
2009-10-16 21:26:58 +00:00
}
2009-10-06 00:38:14 +00:00
}
2009-10-16 21:26:58 +00:00
2009-10-06 00:38:14 +00:00
buffer . DataLength = dataLength ;
#region Queue or Send
2011-04-18 23:48:49 +00:00
OutgoingPacket outgoingPacket = new OutgoingPacket ( udpClient , buffer , category , null ) ;
// If we were not provided a method for handling unacked, use the UDPServer default method
2011-04-21 06:08:51 +00:00
outgoingPacket . UnackedMethod = ( ( method = = null ) ? delegate ( OutgoingPacket oPacket ) { ResendUnacked ( oPacket ) ; } : method ) ;
2009-10-06 00:38:14 +00:00
2010-12-02 01:55:49 +00:00
// 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 ) )
2009-10-06 00:38:14 +00:00
SendPacketFinal ( outgoingPacket ) ;
#endregion Queue or Send
}
2009-10-13 18:14:45 +00:00
public void SendAcks ( LLUDPClient udpClient )
2009-10-06 00:38:14 +00:00
{
uint ack ;
2009-10-13 18:14:45 +00:00
if ( udpClient . PendingAcks . Dequeue ( out ack ) )
2009-10-06 00:38:14 +00:00
{
List < PacketAckPacket . PacketsBlock > blocks = new List < PacketAckPacket . PacketsBlock > ( ) ;
PacketAckPacket . PacketsBlock block = new PacketAckPacket . PacketsBlock ( ) ;
block . ID = ack ;
blocks . Add ( block ) ;
2009-10-13 18:14:45 +00:00
while ( udpClient . PendingAcks . Dequeue ( out ack ) )
2009-06-25 07:50:02 +00:00
{
2009-10-06 00:38:14 +00:00
block = new PacketAckPacket . PacketsBlock ( ) ;
block . ID = ack ;
blocks . Add ( block ) ;
2009-06-25 07:50:02 +00:00
}
2009-10-06 00:38:14 +00:00
PacketAckPacket packet = new PacketAckPacket ( ) ;
packet . Header . Reliable = false ;
packet . Packets = blocks . ToArray ( ) ;
2011-04-18 23:48:49 +00:00
SendPacket ( udpClient , packet , ThrottleOutPacketType . Unknown , true , null ) ;
2009-03-25 19:30:36 +00:00
}
2009-09-30 16:00:09 +00:00
}
2008-10-31 19:41:07 +00:00
2009-10-13 18:14:45 +00:00
public void SendPing ( LLUDPClient udpClient )
2009-10-06 09:38:00 +00:00
{
2009-10-13 18:14:45 +00:00
StartPingCheckPacket pc = ( StartPingCheckPacket ) PacketPool . Instance . GetPacket ( PacketType . StartPingCheck ) ;
pc . Header . Reliable = false ;
pc . PingID . PingID = ( byte ) udpClient . CurrentPingSequence + + ;
2009-10-21 18:59:48 +00:00
// We *could* get OldestUnacked, but it would hurt performance and not provide any benefit
pc . PingID . OldestUnacked = 0 ;
2009-10-13 18:14:45 +00:00
2011-04-18 23:48:49 +00:00
SendPacket ( udpClient , pc , ThrottleOutPacketType . Unknown , false , null ) ;
2009-10-06 09:38:00 +00:00
}
2009-10-30 07:43:46 +00:00
public void CompletePing ( LLUDPClient udpClient , byte pingID )
{
CompletePingCheckPacket completePing = new CompletePingCheckPacket ( ) ;
completePing . PingID . PingID = pingID ;
2011-04-18 23:48:49 +00:00
SendPacket ( udpClient , completePing , ThrottleOutPacketType . Unknown , false , null ) ;
2009-10-30 07:43:46 +00:00
}
2012-06-08 02:53:03 +00:00
public void HandleUnacked ( LLClientView client )
2008-10-31 19:41:07 +00:00
{
2012-06-08 02:53:03 +00:00
LLUDPClient udpClient = client . UDPClient ;
2009-10-20 01:50:31 +00:00
if ( ! udpClient . IsConnected )
return ;
// Disconnect an agent if no packets are received for some time
2012-05-31 23:39:26 +00:00
int timeoutTicks = m_ackTimeout ;
// Allow more slack if the client is "paused" eg file upload dialogue is open
// Some sort of limit is needed in case the client crashes, loses its network connection
// or some other disaster prevents it from sendung the AgentResume
if ( udpClient . IsPaused )
timeoutTicks = m_pausedAckTimeout ;
2012-06-12 01:16:36 +00:00
if ( client . IsActive & &
2012-06-08 02:53:03 +00:00
( Environment . TickCount & Int32 . MaxValue ) - udpClient . TickLastPacketReceived > timeoutTicks )
2008-10-31 19:41:07 +00:00
{
2012-06-12 01:16:36 +00:00
// We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even
// though it's set later on by LLClientView.Close()
client . IsActive = false ;
2012-06-12 01:03:31 +00:00
2012-06-12 01:16:36 +00:00
// Fire this out on a different thread so that we don't hold up outgoing packet processing for
// everybody else if this is being called due to an ack timeout.
// This is the same as processing as the async process of a logout request.
Util . FireAndForget ( o = > DeactivateClientDueToTimeout ( client ) ) ;
2012-06-08 02:53:03 +00:00
2009-10-20 01:50:31 +00:00
return ;
}
2009-10-14 01:56:54 +00:00
2009-10-21 18:59:48 +00:00
// Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO
List < OutgoingPacket > expiredPackets = udpClient . NeedAcks . GetExpiredPackets ( udpClient . RTO ) ;
if ( expiredPackets ! = null )
2009-10-20 01:50:31 +00:00
{
2011-04-18 23:48:49 +00:00
//m_log.Debug("[LLUDPSERVER]: Handling " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO);
2009-10-25 07:40:21 +00:00
// Exponential backoff of the retransmission timeout
udpClient . BackoffRTO ( ) ;
2011-04-18 23:48:49 +00:00
for ( int i = 0 ; i < expiredPackets . Count ; + + i )
2011-04-21 06:08:51 +00:00
expiredPackets [ i ] . UnackedMethod ( expiredPackets [ i ] ) ;
2011-04-18 23:48:49 +00:00
}
}
2009-10-23 08:33:05 +00:00
2011-04-18 23:48:49 +00:00
public void ResendUnacked ( OutgoingPacket outgoingPacket )
{
//m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
// outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
2009-10-06 00:38:14 +00:00
2011-04-18 23:48:49 +00:00
// Set the resent flag
outgoingPacket . Buffer . Data [ 0 ] = ( byte ) ( outgoingPacket . Buffer . Data [ 0 ] | Helpers . MSG_RESENT ) ;
outgoingPacket . Category = ThrottleOutPacketType . Resend ;
2009-10-06 00:38:14 +00:00
2011-04-18 23:48:49 +00:00
// Bump up the resend count on this packet
Interlocked . Increment ( ref outgoingPacket . ResendCount ) ;
2009-10-06 00:38:14 +00:00
2011-04-18 23:48:49 +00:00
// Requeue or resend the packet
if ( ! outgoingPacket . Client . EnqueueOutgoing ( outgoingPacket , false ) )
SendPacketFinal ( outgoingPacket ) ;
2009-09-30 16:00:09 +00:00
}
2009-10-06 00:38:14 +00:00
2009-10-13 23:53:19 +00:00
public void Flush ( LLUDPClient udpClient )
2009-10-06 00:38:14 +00:00
{
2009-10-06 17:12:59 +00:00
// FIXME: Implement?
}
2010-09-17 00:16:21 +00:00
/// <summary>
/// Actually send a packet to a client.
/// </summary>
/// <param name="outgoingPacket"></param>
2009-10-06 17:12:59 +00:00
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 ;
2010-04-02 23:25:14 +00:00
bool isZerocoded = ( flags & Helpers . MSG_ZEROCODED ) ! = 0 ;
2009-10-13 18:14:45 +00:00
LLUDPClient udpClient = outgoingPacket . Client ;
2009-10-06 17:12:59 +00:00
2009-10-14 01:56:54 +00:00
if ( ! udpClient . IsConnected )
return ;
2009-10-06 17:12:59 +00:00
#region ACK Appending
int dataLength = buffer . DataLength ;
2010-04-02 23:25:14 +00:00
// NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here
if ( ! isZerocoded )
2009-10-06 17:12:59 +00:00
{
2010-04-02 23:25:14 +00:00
// 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 ;
}
2009-10-06 17:12:59 +00:00
2010-04-02 23:25:14 +00:00
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 ) ;
}
2009-10-06 17:12:59 +00:00
}
buffer . DataLength = dataLength ;
#endregion ACK Appending
2009-10-13 18:14:45 +00:00
#region Sequence Number Assignment
2009-10-06 17:12:59 +00:00
if ( ! isResend )
{
// Not a resend, assign a new sequence number
2009-10-13 18:14:45 +00:00
uint sequenceNumber = ( uint ) Interlocked . Increment ( ref udpClient . CurrentSequence ) ;
2009-10-06 17:12:59 +00:00
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
2009-10-13 18:14:45 +00:00
udpClient . NeedAcks . Add ( outgoingPacket ) ;
2009-10-06 17:12:59 +00:00
}
}
2011-02-02 20:00:50 +00:00
else
{
Interlocked . Increment ( ref udpClient . PacketsResent ) ;
}
2009-10-06 17:12:59 +00:00
2009-10-13 18:14:45 +00:00
#endregion Sequence Number Assignment
2009-10-06 17:12:59 +00:00
// Stats tracking
2009-10-13 18:14:45 +00:00
Interlocked . Increment ( ref udpClient . PacketsSent ) ;
2009-10-06 17:12:59 +00:00
// Put the UDP payload on the wire
AsyncBeginSend ( buffer ) ;
2009-10-21 20:47:16 +00:00
// Keep track of when this packet was sent out (right now)
outgoingPacket . TickCount = Environment . TickCount & Int32 . MaxValue ;
2009-10-06 00:38:14 +00:00
}
2011-12-08 20:52:34 +00:00
public override void PacketReceived ( UDPPacketBuffer buffer )
2008-10-23 17:16:13 +00:00
{
2009-10-06 00:38:14 +00:00
// Debugging/Profiling
2009-10-13 18:14:45 +00:00
//try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; }
2009-10-06 00:38:14 +00:00
//catch (Exception) { }
2011-12-08 20:52:34 +00:00
// m_log.DebugFormat(
// "[LLUDPSERVER]: Packet received from {0} in {1}", buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
2009-10-06 00:38:14 +00:00
2009-10-13 21:50:03 +00:00
LLUDPClient udpClient = null ;
2009-10-06 00:38:14 +00:00
Packet packet = null ;
int packetEnd = buffer . DataLength - 1 ;
2012-10-16 22:35:05 +00:00
IPEndPoint endPoint = ( IPEndPoint ) buffer . RemoteEndPoint ;
2009-10-06 00:38:14 +00:00
#region Decoding
2011-07-27 06:35:19 +00:00
if ( buffer . DataLength < 7 )
2011-12-08 20:52:34 +00:00
{
// m_log.WarnFormat(
// "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}",
// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
2012-10-16 22:35:05 +00:00
return ; // Drop undersized packet
2011-12-08 20:52:34 +00:00
}
2011-07-27 06:35:19 +00:00
int headerLen = 7 ;
if ( buffer . Data [ 6 ] = = 0xFF )
{
if ( buffer . Data [ 7 ] = = 0xFF )
headerLen = 10 ;
else
headerLen = 8 ;
}
if ( buffer . DataLength < headerLen )
2011-12-08 20:52:34 +00:00
{
// m_log.WarnFormat(
// "[LLUDPSERVER]: Dropping packet with malformed header received from {0} in {1}",
// buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
2011-07-27 06:35:19 +00:00
return ; // Malformed header
2011-12-08 20:52:34 +00:00
}
2011-07-27 06:35:19 +00:00
2008-10-23 17:16:13 +00:00
try
{
2012-10-12 00:39:37 +00:00
// 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);
2012-10-16 22:35:05 +00:00
// 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).
2012-10-12 00:39:37 +00:00
packet = PacketPool . Instance . GetPacket ( buffer . Data , ref packetEnd ,
2009-10-06 00:38:14 +00:00
// Only allocate a buffer for zerodecoding if the packet is zerocoded
( ( buffer . Data [ 0 ] & Helpers . MSG_ZEROCODED ) ! = 0 ) ? new byte [ 4096 ] : null ) ;
2008-10-23 17:16:13 +00:00
}
2009-10-06 00:38:14 +00:00
catch ( MalformedDataException )
2008-10-23 17:16:13 +00:00
{
2009-10-06 00:38:14 +00:00
}
2011-07-27 06:35:19 +00:00
catch ( IndexOutOfRangeException )
{
2011-12-08 20:52:34 +00:00
// m_log.WarnFormat(
// "[LLUDPSERVER]: Dropping short packet received from {0} in {1}",
// buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
2011-07-27 06:35:19 +00:00
return ; // Drop short packet
}
2012-10-12 00:39:37 +00:00
catch ( Exception e )
2011-07-27 06:35:19 +00:00
{
if ( m_malformedCount < 100 )
m_log . DebugFormat ( "[LLUDPSERVER]: Dropped malformed packet: " + e . ToString ( ) ) ;
2012-10-12 00:39:37 +00:00
2011-07-27 06:35:19 +00:00
m_malformedCount + + ;
2012-10-12 00:39:37 +00:00
2011-07-27 06:35:19 +00:00
if ( ( m_malformedCount % 100000 ) = = 0 )
m_log . DebugFormat ( "[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack." , m_malformedCount ) ;
}
2009-10-06 00:38:14 +00:00
// Fail-safe check
if ( packet = = null )
{
2010-02-23 22:58:36 +00:00
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 ) ) ;
2009-10-06 00:38:14 +00:00
return ;
}
2008-10-23 17:16:13 +00:00
2009-10-06 00:38:14 +00:00
#endregion Decoding
2009-10-13 18:28:08 +00:00
#region Packet to Client Mapping
2009-10-06 00:38:14 +00:00
2009-10-13 18:28:08 +00:00
// UseCircuitCode handling
2009-10-06 00:38:14 +00:00
if ( packet . Type = = PacketType . UseCircuitCode )
2010-11-08 12:48:35 +00:00
{
2012-10-16 22:35:05 +00:00
// 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 } ;
2009-10-21 20:47:16 +00:00
2011-02-09 01:53:01 +00:00
Util . FireAndForget ( HandleUseCircuitCode , array ) ;
2009-10-21 20:47:16 +00:00
return ;
2008-10-23 17:16:13 +00:00
}
2009-10-06 00:38:14 +00:00
// Determine which agent this packet came from
2009-10-13 21:50:03 +00:00
IClientAPI client ;
2012-10-16 22:35:05 +00:00
if ( ! m_scene . TryGetClient ( endPoint , out client ) | | ! ( client is LLClientView ) )
2008-10-23 17:16:13 +00:00
{
2011-01-05 15:14:09 +00:00
//m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName);
2009-10-06 00:38:14 +00:00
return ;
2009-09-30 16:00:09 +00:00
}
2008-05-16 01:22:11 +00:00
2009-10-13 21:50:03 +00:00
udpClient = ( ( LLClientView ) client ) . UDPClient ;
2009-10-14 01:56:54 +00:00
if ( ! udpClient . IsConnected )
return ;
2009-10-13 18:28:08 +00:00
#endregion Packet to Client Mapping
2009-10-06 00:38:14 +00:00
2009-10-06 17:12:59 +00:00
// Stats tracking
2009-10-13 21:50:03 +00:00
Interlocked . Increment ( ref udpClient . PacketsReceived ) ;
2009-10-06 00:38:14 +00:00
2009-10-21 20:47:16 +00:00
int now = Environment . TickCount & Int32 . MaxValue ;
2012-06-12 01:46:14 +00:00
udpClient . TickLastPacketReceived = now ;
2009-10-06 00:38:14 +00:00
2009-10-21 20:47:16 +00:00
#region ACK Receiving
2009-10-06 00:38:14 +00:00
// Handle appended ACKs
if ( packet . Header . AppendedAcks & & packet . Header . AckList ! = null )
2009-09-30 16:00:09 +00:00
{
2012-10-17 22:08:14 +00:00
// m_log.DebugFormat(
// "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}",
// packet.Header.AckList.Length, client.Name, m_scene.Name);
2009-10-21 18:59:48 +00:00
for ( int i = 0 ; i < packet . Header . AckList . Length ; i + + )
2011-04-21 22:40:32 +00:00
udpClient . NeedAcks . Acknowledge ( packet . Header . AckList [ i ] , now , packet . Header . Resent ) ;
2009-10-06 00:38:14 +00:00
}
// Handle PacketAck packets
if ( packet . Type = = PacketType . PacketAck )
{
PacketAckPacket ackPacket = ( PacketAckPacket ) packet ;
2012-10-17 22:08:14 +00:00
// m_log.DebugFormat(
// "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}",
// ackPacket.Packets.Length, client.Name, m_scene.Name);
2009-10-21 18:59:48 +00:00
for ( int i = 0 ; i < ackPacket . Packets . Length ; i + + )
2011-04-21 22:40:32 +00:00
udpClient . NeedAcks . Acknowledge ( ackPacket . Packets [ i ] . ID , now , packet . Header . Resent ) ;
2009-10-18 09:00:42 +00:00
// We don't need to do anything else with PacketAck packets
return ;
2009-10-06 00:38:14 +00:00
}
2008-06-04 17:43:07 +00:00
2009-10-06 00:38:14 +00:00
#endregion ACK Receiving
2008-05-02 19:21:33 +00:00
2009-10-06 00:38:14 +00:00
#region ACK Sending
if ( packet . Header . Reliable )
2009-10-18 09:00:42 +00:00
{
2012-10-17 22:08:14 +00:00
// m_log.DebugFormat(
// "[LLUDPSERVER]: Adding ack request for {0} {1} from {2} in {3}",
// packet.Type, packet.Header.Sequence, client.Name, m_scene.Name);
2009-10-13 21:50:03 +00:00
udpClient . PendingAcks . Enqueue ( packet . Header . Sequence ) ;
2009-10-06 00:38:14 +00:00
2009-10-18 09:00:42 +00:00
// 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 ) ;
2009-10-06 00:38:14 +00:00
}
#endregion ACK Sending
#region Incoming Packet Accounting
// Check the archive of received reliable packet IDs to see whether we already received this packet
2009-10-13 21:50:03 +00:00
if ( packet . Header . Reliable & & ! udpClient . PacketArchive . TryEnqueue ( packet . Header . Sequence ) )
2009-10-06 00:38:14 +00:00
{
if ( packet . Header . Resent )
2010-09-13 20:28:42 +00:00
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 ) ;
2009-10-06 00:38:14 +00:00
// Avoid firing a callback twice for the same packet
return ;
2009-09-30 16:00:09 +00:00
}
2008-11-10 09:50:05 +00:00
2009-10-06 00:38:14 +00:00
#endregion Incoming Packet Accounting
2008-11-10 09:50:05 +00:00
2009-11-05 20:01:40 +00:00
#region BinaryStats
LogPacketHeader ( true , udpClient . CircuitCode , 0 , packet . Type , ( ushort ) packet . Length ) ;
#endregion BinaryStats
2009-10-18 09:00:42 +00:00
#region Ping Check Handling
if ( packet . Type = = PacketType . StartPingCheck )
2009-10-06 00:38:14 +00:00
{
2012-10-17 22:08:14 +00:00
// m_log.DebugFormat("[LLUDPSERVER]: Handling ping from {0} in {1}", client.Name, m_scene.Name);
2009-10-18 09:00:42 +00:00
// We don't need to do anything else with ping checks
StartPingCheckPacket startPing = ( StartPingCheckPacket ) packet ;
2009-10-30 07:43:46 +00:00
CompletePing ( udpClient , startPing . PingID . PingID ) ;
2009-11-28 00:17:36 +00:00
if ( ( Environment . TickCount - m_elapsedMSSinceLastStatReport ) > = 3000 )
{
udpClient . SendPacketStats ( ) ;
m_elapsedMSSinceLastStatReport = Environment . TickCount ;
}
2009-10-18 09:00:42 +00:00
return ;
2009-10-06 00:38:14 +00:00
}
2009-10-18 09:00:42 +00:00
else if ( packet . Type = = PacketType . CompletePingCheck )
{
// We don't currently track client ping times
return ;
}
#endregion Ping Check Handling
2012-10-17 20:08:15 +00:00
IncomingPacket incomingPacket ;
2009-10-18 09:00:42 +00:00
// Inbox insertion
2012-10-17 20:08:15 +00:00
if ( UsePools )
{
incomingPacket = m_incomingPacketPool . GetObject ( ) ;
incomingPacket . Client = ( LLClientView ) client ;
incomingPacket . Packet = packet ;
}
else
{
incomingPacket = new IncomingPacket ( ( LLClientView ) client , packet ) ;
}
packetInbox . Enqueue ( incomingPacket ) ;
2009-10-06 00:38:14 +00:00
}
2008-11-10 09:50:05 +00:00
2009-11-05 20:01:40 +00:00
#region BinaryStats
public class PacketLogger
{
public DateTime StartTime ;
public string Path = null ;
public System . IO . BinaryWriter Log = null ;
}
public static PacketLogger PacketLog ;
protected static bool m_shouldCollectStats = false ;
// Number of seconds to log for
static TimeSpan binStatsMaxFilesize = TimeSpan . FromSeconds ( 300 ) ;
static object binStatsLogLock = new object ( ) ;
static string binStatsDir = "" ;
2013-03-15 20:59:34 +00:00
//for Aggregated In/Out BW logging
static bool m_aggregatedBWStats = false ;
static long m_aggregatedBytesIn = 0 ;
static long m_aggregatedByestOut = 0 ;
static object aggBWStatsLock = new object ( ) ;
public static long AggregatedLLUDPBytesIn
{
get { return m_aggregatedBytesIn ; }
}
public static long AggregatedLLUDPBytesOut
{
get { return m_aggregatedByestOut ; }
}
2009-11-05 20:01:40 +00:00
public static void LogPacketHeader ( bool incoming , uint circuit , byte flags , PacketType packetType , ushort size )
{
2013-03-15 20:59:34 +00:00
if ( m_aggregatedBWStats )
{
lock ( aggBWStatsLock )
{
if ( incoming )
m_aggregatedBytesIn + = size ;
else
m_aggregatedByestOut + = size ;
}
}
2009-11-05 20:01:40 +00:00
if ( ! m_shouldCollectStats ) return ;
// Binary logging format is TTTTTTTTCCCCFPPPSS, T=Time, C=Circuit, F=Flags, P=PacketType, S=size
// Put the incoming bit into the least significant bit of the flags byte
if ( incoming )
flags | = 0x01 ;
else
flags & = 0xFE ;
// Put the flags byte into the most significant bits of the type integer
uint type = ( uint ) packetType ;
type | = ( uint ) flags < < 24 ;
// m_log.Debug("1 LogPacketHeader(): Outside lock");
lock ( binStatsLogLock )
{
DateTime now = DateTime . Now ;
// m_log.Debug("2 LogPacketHeader(): Inside lock. now is " + now.Ticks);
try
{
if ( PacketLog = = null | | ( now > PacketLog . StartTime + binStatsMaxFilesize ) )
{
if ( PacketLog ! = null & & PacketLog . Log ! = null )
{
PacketLog . Log . Close ( ) ;
}
// First log file or time has expired, start writing to a new log file
PacketLog = new PacketLogger ( ) ;
PacketLog . StartTime = now ;
PacketLog . Path = ( binStatsDir . Length > 0 ? binStatsDir + System . IO . Path . DirectorySeparatorChar . ToString ( ) : "" )
+ String . Format ( "packets-{0}.log" , now . ToString ( "yyyyMMddHHmmss" ) ) ;
PacketLog . Log = new BinaryWriter ( File . Open ( PacketLog . Path , FileMode . Append , FileAccess . Write ) ) ;
}
// Serialize the data
byte [ ] output = new byte [ 18 ] ;
Buffer . BlockCopy ( BitConverter . GetBytes ( now . Ticks ) , 0 , output , 0 , 8 ) ;
Buffer . BlockCopy ( BitConverter . GetBytes ( circuit ) , 0 , output , 8 , 4 ) ;
Buffer . BlockCopy ( BitConverter . GetBytes ( type ) , 0 , output , 12 , 4 ) ;
Buffer . BlockCopy ( BitConverter . GetBytes ( size ) , 0 , output , 16 , 2 ) ;
// Write the serialized data to disk
if ( PacketLog ! = null & & PacketLog . Log ! = null )
PacketLog . Log . Write ( output ) ;
}
catch ( Exception ex )
{
m_log . Error ( "Packet statistics gathering failed: " + ex . Message , ex ) ;
if ( PacketLog . Log ! = null )
{
PacketLog . Log . Close ( ) ;
}
PacketLog = null ;
}
}
}
#endregion BinaryStats
2009-10-26 20:38:07 +00:00
private void HandleUseCircuitCode ( object o )
{
2012-10-16 22:35:05 +00:00
IPEndPoint endPoint = null ;
2012-02-16 01:39:12 +00:00
IClientAPI client = null ;
2009-10-26 20:38:07 +00:00
2012-02-16 01:25:54 +00:00
try
2011-12-08 22:00:59 +00:00
{
2012-02-16 01:25:54 +00:00
// DateTime startTime = DateTime.Now;
object [ ] array = ( object [ ] ) o ;
2012-10-16 22:35:05 +00:00
endPoint = ( IPEndPoint ) array [ 0 ] ;
2012-02-16 01:25:54 +00:00
UseCircuitCodePacket uccp = ( UseCircuitCodePacket ) array [ 1 ] ;
2012-04-26 21:35:25 +00:00
m_log . DebugFormat (
2012-06-15 04:01:36 +00:00
"[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}" ,
2012-10-16 22:35:05 +00:00
uccp . CircuitCode . Code , m_scene . RegionInfo . RegionName , endPoint ) ;
2012-02-16 01:25:54 +00:00
AuthenticateResponse sessionInfo ;
if ( IsClientAuthorized ( uccp , out sessionInfo ) )
{
// Begin the process of adding the client to the simulator
client
= AddClient (
uccp . CircuitCode . Code ,
uccp . CircuitCode . ID ,
uccp . CircuitCode . SessionID ,
2012-10-16 22:35:05 +00:00
endPoint ,
2012-02-16 01:25:54 +00:00
sessionInfo ) ;
// Send ack straight away to let the viewer know that the connection is active.
// The client will be null if it already exists (e.g. if on a region crossing the client sends a use
// circuit code to the existing child agent. This is not particularly obvious.
2012-10-16 22:35:05 +00:00
SendAckImmediate ( endPoint , uccp . Header . Sequence ) ;
2012-02-16 01:25:54 +00:00
}
else
{
// Don't create clients for unauthorized requesters.
m_log . WarnFormat (
2012-06-15 04:01:36 +00:00
"[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}" ,
2012-10-16 22:35:05 +00:00
uccp . CircuitCode . ID , m_scene . RegionInfo . RegionName , uccp . CircuitCode . Code , endPoint ) ;
2012-02-16 01:25:54 +00:00
}
// m_log.DebugFormat(
// "[LLUDPSERVER]: Handling UseCircuitCode request from {0} took {1}ms",
// buffer.RemoteEndPoint, (DateTime.Now - startTime).Milliseconds);
2011-12-08 22:00:59 +00:00
}
2012-02-16 01:25:54 +00:00
catch ( Exception e )
2011-12-08 22:00:59 +00:00
{
2012-02-16 01:25:54 +00:00
m_log . ErrorFormat (
"[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}" ,
2012-10-16 22:35:05 +00:00
endPoint ! = null ? endPoint . ToString ( ) : "n/a" ,
2012-02-16 01:25:54 +00:00
client ! = null ? client . Name : "unknown" ,
2012-02-16 01:39:12 +00:00
client ! = null ? client . AgentId . ToString ( ) : "unknown" ,
2012-02-16 01:25:54 +00:00
e . Message ,
e . StackTrace ) ;
2011-12-08 22:00:59 +00:00
}
2009-10-26 20:38:07 +00:00
}
2011-12-08 21:45:02 +00:00
/// <summary>
/// Send an ack immediately to the given endpoint.
/// </summary>
/// <remarks>
/// FIXME: Might be possible to use SendPacketData() like everything else, but this will require refactoring so
/// that we can obtain the UDPClient easily at this point.
/// </remarks>
/// <param name="remoteEndpoint"></param>
/// <param name="sequenceNumber"></param>
2009-10-21 20:47:16 +00:00
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 ;
2011-12-08 21:45:02 +00:00
SendAckImmediate ( remoteEndpoint , ack ) ;
}
public virtual void SendAckImmediate ( IPEndPoint remoteEndpoint , PacketAckPacket ack )
{
2009-10-21 20:47:16 +00:00
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 ) ;
}
2009-10-06 00:38:14 +00:00
private bool IsClientAuthorized ( UseCircuitCodePacket useCircuitCode , out AuthenticateResponse sessionInfo )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
UUID agentID = useCircuitCode . CircuitCode . ID ;
UUID sessionID = useCircuitCode . CircuitCode . SessionID ;
uint circuitCode = useCircuitCode . CircuitCode . Code ;
2008-05-02 19:21:33 +00:00
2009-10-06 00:38:14 +00:00
sessionInfo = m_circuitManager . AuthenticateSession ( sessionID , agentID , circuitCode ) ;
return sessionInfo . Authorised ;
}
2008-06-03 13:49:58 +00:00
2011-12-08 18:34:23 +00:00
/// <summary>
/// Add a client.
/// </summary>
/// <param name="circuitCode"></param>
/// <param name="agentID"></param>
/// <param name="sessionID"></param>
/// <param name="remoteEndPoint"></param>
/// <param name="sessionInfo"></param>
/// <returns>The client if it was added. Null if the client already existed.</returns>
protected virtual IClientAPI AddClient (
uint circuitCode , UUID agentID , UUID sessionID , IPEndPoint remoteEndPoint , AuthenticateResponse sessionInfo )
2008-05-02 19:21:33 +00:00
{
2011-12-08 18:34:23 +00:00
IClientAPI client = null ;
2012-10-09 23:26:43 +00:00
// We currently synchronize this code across the whole scene to avoid issues such as
// http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done
// consistently, this lock could probably be removed.
lock ( this )
2009-10-13 21:50:03 +00:00
{
2011-12-08 18:34:23 +00:00
if ( ! m_scene . TryGetClient ( agentID , out client ) )
2011-02-09 01:53:01 +00:00
{
LLUDPClient udpClient = new LLUDPClient ( this , ThrottleRates , m_throttle , circuitCode , agentID , remoteEndPoint , m_defaultRTO , m_maxRTO ) ;
2012-10-09 23:26:43 +00:00
2012-07-12 22:48:42 +00:00
client = new LLClientView ( m_scene , this , udpClient , sessionInfo , agentID , sessionID , circuitCode ) ;
2011-02-09 01:53:01 +00:00
client . OnLogout + = LogoutHandler ;
2012-10-09 23:26:43 +00:00
2011-12-08 18:34:23 +00:00
( ( LLClientView ) client ) . DisableFacelights = m_disableFacelights ;
2012-10-09 23:26:43 +00:00
2011-02-09 01:53:01 +00:00
client . Start ( ) ;
}
2009-10-13 21:50:03 +00:00
}
2011-12-08 18:34:23 +00:00
return client ;
2009-10-13 21:50:03 +00:00
}
2012-06-12 01:16:36 +00:00
/// <summary>
/// Deactivates the client if we don't receive any packets within a certain amount of time (default 60 seconds).
/// </summary>
/// <remarks>
/// If a connection is active then we will always receive packets even if nothing else is happening, due to
/// regular client pings.
/// </remarks>
/// <param name='client'></param>
2012-07-19 21:32:27 +00:00
private void DeactivateClientDueToTimeout ( LLClientView client )
2009-10-13 21:50:03 +00:00
{
2012-07-19 21:32:27 +00:00
lock ( client . CloseSyncLock )
{
m_log . WarnFormat (
"[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}" ,
client . SceneAgent . IsChildAgent ? "child" : "root" , client . Name , m_scene . RegionInfo . RegionName ) ;
StatsManager . SimExtraStats . AddAbnormalClientThreadTermination ( ) ;
if ( ! client . SceneAgent . IsChildAgent )
client . Kick ( "Simulator logged you out due to connection timeout" ) ;
client . CloseWithoutChecks ( ) ;
}
2012-06-12 01:03:31 +00:00
}
2009-10-06 00:38:14 +00:00
private void IncomingPacketHandler ( )
{
2009-10-06 09:38:00 +00:00
// Set this culture for the thread that incoming packets are received
// on to en-US to avoid number parsing issues
Culture . SetCurrentCulture ( ) ;
2012-10-17 22:08:14 +00:00
while ( IsRunningInbound )
2008-05-02 19:21:33 +00:00
{
2009-10-18 00:19:18 +00:00
try
{
2009-10-20 21:41:20 +00:00
IncomingPacket incomingPacket = null ;
2009-10-27 21:16:01 +00:00
// HACK: This is a test to try and rate limit packet handling on Mono.
// If it works, a more elegant solution can be devised
if ( Util . FireAndForgetCount ( ) < 2 )
{
//m_log.Debug("[LLUDPSERVER]: Incoming packet handler is sleeping");
Thread . Sleep ( 30 ) ;
}
2009-10-18 00:19:18 +00:00
if ( packetInbox . Dequeue ( 100 , ref incomingPacket ) )
2012-10-17 20:08:15 +00:00
{
2009-12-13 07:08:28 +00:00
ProcessInPacket ( incomingPacket ) ; //, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket);
2012-10-17 20:08:15 +00:00
if ( UsePools )
m_incomingPacketPool . ReturnObject ( incomingPacket ) ;
}
2009-10-18 00:19:18 +00:00
}
catch ( Exception ex )
{
m_log . Error ( "[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex . Message , ex ) ;
}
2009-10-22 19:33:23 +00:00
Watchdog . UpdateThread ( ) ;
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
if ( packetInbox . Count > 0 )
m_log . Warn ( "[LLUDPSERVER]: IncomingPacketHandler is shutting down, dropping " + packetInbox . Count + " packets" ) ;
packetInbox . Clear ( ) ;
2009-10-22 19:33:23 +00:00
Watchdog . RemoveThread ( ) ;
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
private void OutgoingPacketHandler ( )
2008-05-02 19:21:33 +00:00
{
2009-10-06 09:38:00 +00:00
// Set this culture for the thread that outgoing packets are sent
// on to en-US to avoid number parsing issues
Culture . SetCurrentCulture ( ) ;
2009-10-23 20:14:29 +00:00
// Typecast the function to an Action<IClientAPI> once here to avoid allocating a new
// Action generic every round
Action < IClientAPI > clientPacketHandler = ClientOutgoingPacketHandler ;
2012-10-16 20:55:00 +00:00
while ( base . IsRunningOutbound )
2008-07-11 17:58:26 +00:00
{
2009-10-20 19:43:09 +00:00
try
2009-10-06 09:38:00 +00:00
{
2009-10-21 18:59:48 +00:00
m_packetSent = false ;
2009-10-06 00:38:14 +00:00
2009-10-21 22:22:23 +00:00
#region Update Timers
m_resendUnacked = false ;
m_sendAcks = false ;
m_sendPing = false ;
// Update elapsed time
int thisTick = Environment . TickCount & Int32 . MaxValue ;
if ( m_tickLastOutgoingPacketHandler > thisTick )
m_elapsedMSOutgoingPacketHandler + = ( ( Int32 . MaxValue - m_tickLastOutgoingPacketHandler ) + thisTick ) ;
else
m_elapsedMSOutgoingPacketHandler + = ( thisTick - m_tickLastOutgoingPacketHandler ) ;
m_tickLastOutgoingPacketHandler = thisTick ;
// Check for pending outgoing resends every 100ms
if ( m_elapsedMSOutgoingPacketHandler > = 100 )
{
m_resendUnacked = true ;
m_elapsedMSOutgoingPacketHandler = 0 ;
m_elapsed100MSOutgoingPacketHandler + = 1 ;
}
// Check for pending outgoing ACKs every 500ms
if ( m_elapsed100MSOutgoingPacketHandler > = 5 )
{
m_sendAcks = true ;
m_elapsed100MSOutgoingPacketHandler = 0 ;
m_elapsed500MSOutgoingPacketHandler + = 1 ;
}
// Send pings to clients every 5000ms
if ( m_elapsed500MSOutgoingPacketHandler > = 10 )
{
m_sendPing = true ;
m_elapsed500MSOutgoingPacketHandler = 0 ;
}
#endregion Update Timers
2011-02-09 17:50:26 +00:00
// Use this for emergency monitoring -- bug hunting
//if (m_scene.EmergencyMonitoring)
// clientPacketHandler = MonitoredClientOutgoingPacketHandler;
//else
// clientPacketHandler = ClientOutgoingPacketHandler;
2011-02-08 20:06:14 +00:00
2009-10-21 07:18:35 +00:00
// Handle outgoing packets, resends, acknowledgements, and pings for each
2009-10-21 18:59:48 +00:00
// client. m_packetSent will be set to true if a packet is sent
2010-03-16 19:03:04 +00:00
m_scene . ForEachClient ( clientPacketHandler ) ;
2009-10-20 19:43:09 +00:00
2012-06-07 01:44:13 +00:00
m_currentOutgoingClient = null ;
2009-10-21 21:03:49 +00:00
// If nothing was sent, sleep for the minimum amount of time before a
// token bucket could get more tokens
if ( ! m_packetSent )
2009-10-21 18:59:48 +00:00
Thread . Sleep ( ( int ) TickCountResolution ) ;
2009-10-22 19:33:23 +00:00
Watchdog . UpdateThread ( ) ;
2009-10-20 21:41:20 +00:00
}
catch ( Exception ex )
{
m_log . Error ( "[LLUDPSERVER]: OutgoingPacketHandler loop threw an exception: " + ex . Message , ex ) ;
}
}
2009-10-22 19:33:23 +00:00
Watchdog . RemoveThread ( ) ;
2009-10-20 21:41:20 +00:00
}
2009-10-13 21:50:03 +00:00
2012-06-08 02:12:23 +00:00
protected void ClientOutgoingPacketHandler ( IClientAPI client )
2009-10-20 21:41:20 +00:00
{
2012-06-07 01:44:13 +00:00
m_currentOutgoingClient = client ;
2009-10-20 21:41:20 +00:00
try
{
if ( client is LLClientView )
{
2012-06-08 02:53:03 +00:00
LLClientView llClient = ( LLClientView ) client ;
LLUDPClient udpClient = llClient . UDPClient ;
2009-10-20 21:41:20 +00:00
if ( udpClient . IsConnected )
{
2009-10-21 22:22:23 +00:00
if ( m_resendUnacked )
2012-06-08 02:53:03 +00:00
HandleUnacked ( llClient ) ;
2009-10-20 21:41:20 +00:00
2009-10-21 22:22:23 +00:00
if ( m_sendAcks )
2009-10-21 07:18:35 +00:00
SendAcks ( udpClient ) ;
2009-10-21 22:22:23 +00:00
if ( m_sendPing )
2009-10-21 07:18:35 +00:00
SendPing ( udpClient ) ;
2009-10-06 00:38:14 +00:00
2009-10-20 21:41:20 +00:00
// Dequeue any outgoing packets that are within the throttle limits
2009-10-21 18:59:48 +00:00
if ( udpClient . DequeueOutgoing ( ) )
m_packetSent = true ;
2009-10-20 21:41:20 +00:00
}
2009-10-20 19:43:09 +00:00
}
2008-05-02 19:21:33 +00:00
}
2009-10-20 21:41:20 +00:00
catch ( Exception ex )
{
2012-06-07 01:44:13 +00:00
m_log . Error (
string . Format ( "[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw " , client . Name ) , ex ) ;
2009-10-20 21:41:20 +00:00
}
2008-05-02 19:21:33 +00:00
}
2011-02-08 20:06:14 +00:00
#region Emergency Monitoring
2011-02-09 17:50:26 +00:00
// Alternative packet handler fuull of instrumentation
// Handy for hunting bugs
2011-02-08 20:06:14 +00:00
private Stopwatch watch1 = new Stopwatch ( ) ;
private Stopwatch watch2 = new Stopwatch ( ) ;
private float avgProcessingTicks = 0 ;
private float avgResendUnackedTicks = 0 ;
private float avgSendAcksTicks = 0 ;
private float avgSendPingTicks = 0 ;
private float avgDequeueTicks = 0 ;
private long nticks = 0 ;
private long nticksUnack = 0 ;
private long nticksAck = 0 ;
private long nticksPing = 0 ;
2011-02-08 22:49:50 +00:00
private int npacksSent = 0 ;
private int npackNotSent = 0 ;
2011-02-08 20:06:14 +00:00
2012-11-15 01:14:18 +00:00
/// <summary>
/// Number of inbound packets processed since startup.
/// </summary>
public long IncomingPacketsProcessed { get ; private set ; }
2011-02-08 20:06:14 +00:00
private void MonitoredClientOutgoingPacketHandler ( IClientAPI client )
{
nticks + + ;
watch1 . Start ( ) ;
2012-06-07 01:44:13 +00:00
m_currentOutgoingClient = client ;
2011-02-08 20:06:14 +00:00
try
{
if ( client is LLClientView )
{
2012-06-08 02:53:03 +00:00
LLClientView llClient = ( LLClientView ) client ;
LLUDPClient udpClient = llClient . UDPClient ;
2011-02-08 20:06:14 +00:00
if ( udpClient . IsConnected )
{
if ( m_resendUnacked )
{
nticksUnack + + ;
watch2 . Start ( ) ;
2012-06-08 02:53:03 +00:00
HandleUnacked ( llClient ) ;
2011-02-08 20:06:14 +00:00
watch2 . Stop ( ) ;
avgResendUnackedTicks = ( nticksUnack - 1 ) / ( float ) nticksUnack * avgResendUnackedTicks + ( watch2 . ElapsedTicks / ( float ) nticksUnack ) ;
watch2 . Reset ( ) ;
}
if ( m_sendAcks )
{
nticksAck + + ;
watch2 . Start ( ) ;
SendAcks ( udpClient ) ;
watch2 . Stop ( ) ;
avgSendAcksTicks = ( nticksAck - 1 ) / ( float ) nticksAck * avgSendAcksTicks + ( watch2 . ElapsedTicks / ( float ) nticksAck ) ;
watch2 . Reset ( ) ;
}
if ( m_sendPing )
{
nticksPing + + ;
watch2 . Start ( ) ;
SendPing ( udpClient ) ;
watch2 . Stop ( ) ;
avgSendPingTicks = ( nticksPing - 1 ) / ( float ) nticksPing * avgSendPingTicks + ( watch2 . ElapsedTicks / ( float ) nticksPing ) ;
watch2 . Reset ( ) ;
}
watch2 . Start ( ) ;
// Dequeue any outgoing packets that are within the throttle limits
if ( udpClient . DequeueOutgoing ( ) )
2011-02-08 22:49:50 +00:00
{
2011-02-08 20:06:14 +00:00
m_packetSent = true ;
2011-02-08 22:49:50 +00:00
npacksSent + + ;
}
else
2012-11-15 01:14:18 +00:00
{
2011-02-08 22:49:50 +00:00
npackNotSent + + ;
2012-11-15 01:14:18 +00:00
}
2011-02-08 22:49:50 +00:00
2011-02-08 20:06:14 +00:00
watch2 . Stop ( ) ;
avgDequeueTicks = ( nticks - 1 ) / ( float ) nticks * avgDequeueTicks + ( watch2 . ElapsedTicks / ( float ) nticks ) ;
watch2 . Reset ( ) ;
}
else
2012-11-15 01:14:18 +00:00
{
2011-02-08 20:06:14 +00:00
m_log . WarnFormat ( "[LLUDPSERVER]: Client is not connected" ) ;
2012-11-15 01:14:18 +00:00
}
2011-02-08 20:06:14 +00:00
}
}
catch ( Exception ex )
{
m_log . Error ( "[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client . Name +
" threw an exception: " + ex . Message , ex ) ;
}
watch1 . Stop ( ) ;
avgProcessingTicks = ( nticks - 1 ) / ( float ) nticks * avgProcessingTicks + ( watch1 . ElapsedTicks / ( float ) nticks ) ;
watch1 . Reset ( ) ;
2011-02-08 22:49:50 +00:00
// reuse this -- it's every ~100ms
2011-02-08 20:06:14 +00:00
if ( m_scene . EmergencyMonitoring & & nticks % 100 = = 0 )
{
2011-02-08 22:49:50 +00:00
m_log . InfoFormat ( "[LLUDPSERVER]: avg processing ticks: {0} avg unacked: {1} avg acks: {2} avg ping: {3} avg dequeue: {4} (TickCountRes: {5} sent: {6} notsent: {7})" ,
avgProcessingTicks , avgResendUnackedTicks , avgSendAcksTicks , avgSendPingTicks , avgDequeueTicks , TickCountResolution , npacksSent , npackNotSent ) ;
npackNotSent = npacksSent = 0 ;
2011-02-08 20:06:14 +00:00
}
}
#endregion
2012-06-08 00:43:58 +00:00
private void ProcessInPacket ( IncomingPacket incomingPacket )
2009-10-06 19:13:16 +00:00
{
Packet packet = incomingPacket . Packet ;
2012-06-08 00:43:58 +00:00
LLClientView client = incomingPacket . Client ;
2009-10-06 19:13:16 +00:00
2012-06-08 00:43:58 +00:00
if ( client . IsActive )
2009-10-06 19:13:16 +00:00
{
2012-06-07 01:44:13 +00:00
m_currentIncomingClient = client ;
2009-10-13 18:14:45 +00:00
try
{
// Process this packet
client . ProcessInPacket ( packet ) ;
}
catch ( ThreadAbortException )
{
// If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down
m_log . Info ( "[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server" ) ;
Stop ( ) ;
}
catch ( Exception e )
{
// Don't let a failure in an individual client thread crash the whole sim.
2012-06-08 00:43:58 +00:00
m_log . Error (
string . Format (
"[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw " ,
client . Name , packet . Type ) ,
e ) ;
2009-10-13 18:14:45 +00:00
}
2012-06-07 01:44:13 +00:00
finally
{
m_currentIncomingClient = null ;
}
2009-10-06 19:13:16 +00:00
}
2009-10-13 18:14:45 +00:00
else
2009-10-06 19:13:16 +00:00
{
2012-04-26 15:04:49 +00:00
m_log . DebugFormat (
"[LLUDPSERVER]: Dropped incoming {0} for dead client {1} in {2}" ,
2012-06-08 00:43:58 +00:00
packet . Type , client . Name , m_scene . RegionInfo . RegionName ) ;
2009-10-06 19:13:16 +00:00
}
2012-11-15 01:14:18 +00:00
IncomingPacketsProcessed + + ;
2009-10-06 19:13:16 +00:00
}
2009-10-27 10:51:47 +00:00
protected void LogoutHandler ( IClientAPI client )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
client . SendLogoutPacket ( ) ;
2012-06-12 01:16:36 +00:00
2012-06-08 03:12:22 +00:00
if ( ! client . IsLoggingOut )
2012-06-12 01:16:36 +00:00
{
client . IsLoggingOut = true ;
client . Close ( ) ;
}
2009-10-13 19:50:59 +00:00
}
2008-05-02 19:21:33 +00:00
}
2012-06-08 00:51:28 +00:00
}