mirror of
https://github.com/opensim/opensim.git
synced 2026-08-04 16:22:50 +08:00
Merge branch 'master' of brain.opensimulator.org:/var/git/opensim
This commit is contained in:
39
OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs
Normal file → Executable file
39
OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs
Normal file → Executable file
@@ -59,7 +59,7 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||
private string m_GetTextureURL;
|
||||
private string m_GetMeshURL;
|
||||
private string m_GetMesh2URL;
|
||||
// private string m_GetAssetURL;
|
||||
private string m_GetAssetURL;
|
||||
|
||||
class APollRequest
|
||||
{
|
||||
@@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||
private Dictionary<UUID, string> m_capsDictTexture = new Dictionary<UUID, string>();
|
||||
private Dictionary<UUID, string> m_capsDictGetMesh = new Dictionary<UUID, string>();
|
||||
private Dictionary<UUID, string> m_capsDictGetMesh2 = new Dictionary<UUID, string>();
|
||||
//private Dictionary<UUID, string> m_capsDictGetAsset = new Dictionary<UUID, string>();
|
||||
private Dictionary<UUID, string> m_capsDictGetAsset = new Dictionary<UUID, string>();
|
||||
|
||||
#region Region Module interfaceBase Members
|
||||
|
||||
@@ -113,11 +113,11 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||
m_GetMesh2URL = config.GetString("Cap_GetMesh2", string.Empty);
|
||||
if (m_GetMesh2URL != string.Empty)
|
||||
m_Enabled = true;
|
||||
/*
|
||||
|
||||
m_GetAssetURL = config.GetString("Cap_GetAsset", string.Empty);
|
||||
if (m_GetAssetURL != string.Empty)
|
||||
m_Enabled = true;
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
public void AddRegion(Scene pScene)
|
||||
@@ -204,19 +204,26 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||
|
||||
private static void DoAssetRequests()
|
||||
{
|
||||
while (m_NumberScenes > 0)
|
||||
try
|
||||
{
|
||||
APollRequest poolreq;
|
||||
if(m_queue.TryTake(out poolreq, 4500))
|
||||
while (m_NumberScenes > 0)
|
||||
{
|
||||
if (m_NumberScenes <= 0)
|
||||
break;
|
||||
APollRequest poolreq;
|
||||
if (m_queue.TryTake(out poolreq, 4500))
|
||||
{
|
||||
if (m_NumberScenes <= 0)
|
||||
break;
|
||||
Watchdog.UpdateThread();
|
||||
if (poolreq.reqID != UUID.Zero)
|
||||
poolreq.thepoll.Process(poolreq);
|
||||
poolreq = null;
|
||||
}
|
||||
Watchdog.UpdateThread();
|
||||
if (poolreq.reqID != UUID.Zero)
|
||||
poolreq.thepoll.Process(poolreq);
|
||||
poolreq = null;
|
||||
}
|
||||
Watchdog.UpdateThread();
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
Thread.ResetAbort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +448,6 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||
else if (m_GetMesh2URL != string.Empty)
|
||||
caps.RegisterHandler("GetMesh2", m_GetMesh2URL);
|
||||
|
||||
/* we can't support this cap. Current viewers connect to the wrong regions.
|
||||
//ViewerAsset
|
||||
if (m_GetAssetURL == "localhost")
|
||||
{
|
||||
@@ -459,7 +465,7 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||
}
|
||||
else if (m_GetAssetURL != string.Empty)
|
||||
caps.RegisterHandler("ViewerAsset", m_GetMesh2URL);
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
private void DeregisterCaps(UUID agentID, Caps caps)
|
||||
@@ -480,13 +486,12 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||
MainServer.Instance.RemovePollServiceHTTPHandler("", capUrl);
|
||||
m_capsDictGetMesh2.Remove(agentID);
|
||||
}
|
||||
/*
|
||||
|
||||
if (m_capsDictGetAsset.TryGetValue(agentID, out capUrl))
|
||||
{
|
||||
MainServer.Instance.RemovePollServiceHTTPHandler("", capUrl);
|
||||
m_capsDictGetAsset.Remove(agentID);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
Normal file → Executable file
23
OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
Normal file → Executable file
@@ -395,17 +395,26 @@ namespace OpenSim.Region.ClientStack.Linden
|
||||
|
||||
private static void DoInventoryRequests()
|
||||
{
|
||||
while (true)
|
||||
bool running = true;
|
||||
while (running)
|
||||
{
|
||||
APollRequest poolreq;
|
||||
if (m_queue.TryTake(out poolreq, 4500))
|
||||
try
|
||||
{
|
||||
APollRequest poolreq;
|
||||
if (m_queue.TryTake(out poolreq, 4500))
|
||||
{
|
||||
Watchdog.UpdateThread();
|
||||
if (poolreq.thepoll != null)
|
||||
poolreq.thepoll.Process(poolreq);
|
||||
poolreq = null;
|
||||
}
|
||||
Watchdog.UpdateThread();
|
||||
if (poolreq.thepoll != null)
|
||||
poolreq.thepoll.Process(poolreq);
|
||||
poolreq = null;
|
||||
}
|
||||
Watchdog.UpdateThread();
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
Thread.ResetAbort();
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -120,7 +120,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
/// <summary>Circuit code that this client is connected on</summary>
|
||||
public readonly uint CircuitCode;
|
||||
/// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary>
|
||||
public IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200);
|
||||
public IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(256);
|
||||
|
||||
/// <summary>Packets we have sent that need to be ACKed by the client</summary>
|
||||
public UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
|
||||
@@ -210,12 +210,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is the percentage of the udp texture queue to add to the task queue since
|
||||
/// textures are now generally handled through http.
|
||||
/// </summary>
|
||||
private double m_cannibalrate = 0.0;
|
||||
|
||||
private ClientInfo m_info = new ClientInfo();
|
||||
|
||||
/// <summary>
|
||||
@@ -257,8 +251,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// Create an array of token buckets for this clients different throttle categories
|
||||
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
|
||||
|
||||
m_cannibalrate = rates.CannibalizeTextureRate;
|
||||
|
||||
m_burst = rates.Total * rates.BrustTime;
|
||||
|
||||
for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
|
||||
@@ -449,12 +441,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
asset = Math.Max(asset, LLUDPServer.MTU);
|
||||
*/
|
||||
|
||||
// Since most textures are now delivered through http, make it possible
|
||||
// to cannibalize some of the bw from the texture throttle to use for
|
||||
// the task queue (e.g. object updates)
|
||||
task = task + (int)(m_cannibalrate * texture);
|
||||
texture = (int)((1 - m_cannibalrate) * texture);
|
||||
|
||||
int total = resend + land + wind + cloud + task + texture + asset;
|
||||
|
||||
float m_burst = total * m_burstTime;
|
||||
@@ -575,22 +561,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
DoubleLocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
|
||||
|
||||
if (m_deliverPackets == false)
|
||||
if (forceQueue || m_deliverPackets == false)
|
||||
{
|
||||
queue.Enqueue(packet, highPriority);
|
||||
return true;
|
||||
}
|
||||
|
||||
TokenBucket bucket = m_throttleCategories[category];
|
||||
|
||||
// Don't send this packet if queue is not empty
|
||||
// need to enqueue if queue is not empty
|
||||
if (queue.Count > 0 || m_nextPackets[category] != null)
|
||||
{
|
||||
queue.Enqueue(packet, highPriority);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!forceQueue && bucket.CheckTokens(packet.Buffer.DataLength))
|
||||
// check bandwidth
|
||||
TokenBucket bucket = m_throttleCategories[category];
|
||||
if (bucket.CheckTokens(packet.Buffer.DataLength))
|
||||
{
|
||||
// enough tokens so it can be sent imediatly by caller
|
||||
bucket.RemoveTokens(packet.Buffer.DataLength);
|
||||
@@ -608,7 +594,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// We don't have a token bucket for this category, so it will not be queued
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -650,6 +635,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// leaving a dequeued packet still waiting to be sent out. Try to
|
||||
// send it again
|
||||
OutgoingPacket nextPacket = m_nextPackets[i];
|
||||
if(nextPacket.Buffer == null)
|
||||
{
|
||||
if (m_packetOutboxes[i].Count < 5)
|
||||
emptyCategories |= CategoryToFlag(i);
|
||||
continue;
|
||||
}
|
||||
if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
|
||||
{
|
||||
// Send the packet
|
||||
@@ -681,21 +672,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
// A packet was pulled off the queue. See if we have
|
||||
// enough tokens in the bucket to send it out
|
||||
if (bucket.RemoveTokens(packet.Buffer.DataLength))
|
||||
if(packet.Buffer == null)
|
||||
{
|
||||
// Send the packet
|
||||
m_udpServer.SendPacketFinal(packet);
|
||||
packetSent = true;
|
||||
|
||||
// packet canceled elsewhere (by a ack for example)
|
||||
if (queue.Count < 5)
|
||||
emptyCategories |= CategoryToFlag(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save the dequeued packet for the next iteration
|
||||
m_nextPackets[i] = packet;
|
||||
}
|
||||
if (bucket.RemoveTokens(packet.Buffer.DataLength))
|
||||
{
|
||||
// Send the packet
|
||||
m_udpServer.SendPacketFinal(packet);
|
||||
packetSent = true;
|
||||
|
||||
if (queue.Count < 5)
|
||||
emptyCategories |= CategoryToFlag(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save the dequeued packet for the next iteration
|
||||
m_nextPackets[i] = packet;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -803,8 +802,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fires the OnQueueEmpty callback and sets the minimum time that it
|
||||
/// can be called again
|
||||
@@ -843,6 +840,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void FreeUDPBuffer(UDPPacketBuffer buf)
|
||||
{
|
||||
m_udpServer.FreeUDPBuffer(buf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a
|
||||
/// flag value
|
||||
@@ -853,34 +855,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
ThrottleOutPacketType category = (ThrottleOutPacketType)i;
|
||||
|
||||
/*
|
||||
* Land = 1,
|
||||
/// <summary>Wind data</summary>
|
||||
Wind = 2,
|
||||
/// <summary>Cloud data</summary>
|
||||
Cloud = 3,
|
||||
/// <summary>Any packets that do not fit into the other throttles</summary>
|
||||
Task = 4,
|
||||
/// <summary>Texture assets</summary>
|
||||
Texture = 5,
|
||||
/// <summary>Non-texture assets</summary>
|
||||
Asset = 6,
|
||||
*/
|
||||
|
||||
switch (category)
|
||||
{
|
||||
case ThrottleOutPacketType.Land:
|
||||
return ThrottleOutPacketTypeFlags.Land;
|
||||
return ThrottleOutPacketTypeFlags.Land; // Terrain data
|
||||
case ThrottleOutPacketType.Wind:
|
||||
return ThrottleOutPacketTypeFlags.Wind;
|
||||
return ThrottleOutPacketTypeFlags.Wind; // Wind data
|
||||
case ThrottleOutPacketType.Cloud:
|
||||
return ThrottleOutPacketTypeFlags.Cloud;
|
||||
return ThrottleOutPacketTypeFlags.Cloud; // Cloud data
|
||||
case ThrottleOutPacketType.Task:
|
||||
return ThrottleOutPacketTypeFlags.Task;
|
||||
return ThrottleOutPacketTypeFlags.Task; // Object updates and everything not on the other categories
|
||||
case ThrottleOutPacketType.Texture:
|
||||
return ThrottleOutPacketTypeFlags.Texture;
|
||||
return ThrottleOutPacketTypeFlags.Texture; // Textures data (also impacts http texture and mesh by default)
|
||||
case ThrottleOutPacketType.Asset:
|
||||
return ThrottleOutPacketTypeFlags.Asset;
|
||||
return ThrottleOutPacketTypeFlags.Asset; // Non-texture Assets data
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
350
OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
Normal file → Executable file
350
OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
Normal file → Executable file
@@ -256,6 +256,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
|
||||
/// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary>
|
||||
public const int MTU = 1400;
|
||||
public const int MAXPAYLOAD = 1250;
|
||||
|
||||
/// <summary>Number of forced client logouts due to no receipt of packets before timeout.</summary>
|
||||
public int ClientLogoutsDueToNoReceives { get; protected set; }
|
||||
@@ -274,10 +275,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
/// <summary>The measured resolution of Environment.TickCount</summary>
|
||||
public readonly float TickCountResolution;
|
||||
|
||||
/// <summary>Number of prim updates to put on the queue each time the
|
||||
/// OnQueueEmpty event is triggered for updates</summary>
|
||||
public readonly int PrimUpdatesPerCallback;
|
||||
|
||||
/// <summary>Number of texture packets to put on the queue each time the
|
||||
/// OnQueueEmpty event is triggered for textures</summary>
|
||||
public readonly int TextureSendLimit;
|
||||
@@ -344,18 +341,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
|
||||
protected ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>> m_pendingCache = new ExpiringCache<IPEndPoint, Queue<UDPPacketBuffer>>();
|
||||
|
||||
protected Pool<IncomingPacket> m_incomingPacketPool;
|
||||
|
||||
/// <summary>
|
||||
/// Stat for number of packets in the main pool awaiting use.
|
||||
/// </summary>
|
||||
protected Stat m_poolCountStat;
|
||||
|
||||
/// <summary>
|
||||
/// Stat for number of packets in the inbound packet pool awaiting use.
|
||||
/// </summary>
|
||||
protected Stat m_incomingPacketPoolStat;
|
||||
|
||||
protected int m_defaultRTO = 0;
|
||||
protected int m_maxRTO = 0;
|
||||
protected int m_ackTimeout = 0;
|
||||
@@ -452,7 +437,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0);
|
||||
sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0);
|
||||
|
||||
PrimUpdatesPerCallback = config.GetInt("PrimUpdatesPerCallback", 100);
|
||||
TextureSendLimit = config.GetInt("TextureSendLimit", 20);
|
||||
|
||||
m_defaultRTO = config.GetInt("DefaultRTO", 0);
|
||||
@@ -463,7 +447,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
}
|
||||
else
|
||||
{
|
||||
PrimUpdatesPerCallback = 100;
|
||||
TextureSendLimit = 20;
|
||||
m_ackTimeout = 1000 * 60; // 1 minute
|
||||
m_pausedAckTimeout = 1000 * 300; // 5 minutes
|
||||
@@ -498,7 +481,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
|
||||
// if (usePools)
|
||||
// EnablePools();
|
||||
base.DisablePools();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
@@ -554,83 +536,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
OqrEngine.Stop();
|
||||
}
|
||||
|
||||
public override bool EnablePools()
|
||||
{
|
||||
if (!UsePools)
|
||||
{
|
||||
base.EnablePools();
|
||||
|
||||
m_incomingPacketPool = new Pool<IncomingPacket>(() => new IncomingPacket(), 500);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene
|
||||
/// stats.
|
||||
/// </summary>
|
||||
protected internal void EnablePoolStats()
|
||||
{
|
||||
m_poolCountStat
|
||||
= new Stat(
|
||||
"UDPPacketBufferPoolCount",
|
||||
"Objects within the UDPPacketBuffer pool",
|
||||
"The number of objects currently stored within the UDPPacketBuffer pool",
|
||||
"",
|
||||
"clientstack",
|
||||
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",
|
||||
Scene.Name,
|
||||
StatType.Pull,
|
||||
stat => stat.Value = m_incomingPacketPool.Count,
|
||||
StatVerbosity.Debug);
|
||||
|
||||
StatsManager.RegisterStat(m_incomingPacketPoolStat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables pool stats.
|
||||
/// </summary>
|
||||
protected internal void DisablePoolStats()
|
||||
{
|
||||
StatsManager.DeregisterStat(m_poolCountStat);
|
||||
m_poolCountStat = null;
|
||||
|
||||
StatsManager.DeregisterStat(m_incomingPacketPoolStat);
|
||||
m_incomingPacketPoolStat = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
|
||||
/// </summary>
|
||||
@@ -658,8 +563,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
string.Format("Incoming Packet Async Handling Engine ({0})", Scene.Name),
|
||||
"INCOMING PACKET ASYNC HANDLING ENGINE");
|
||||
*/
|
||||
OqrEngine
|
||||
= new JobEngine(
|
||||
OqrEngine = new JobEngine(
|
||||
string.Format("Outgoing Queue Refill Engine ({0})", Scene.Name),
|
||||
"OUTGOING QUEUE REFILL ENGINE");
|
||||
|
||||
@@ -769,15 +673,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
stat => stat.Value = OqrEngine.JobsWaiting,
|
||||
StatVerbosity.Debug));
|
||||
|
||||
// We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by
|
||||
// scene name
|
||||
if (UsePools)
|
||||
EnablePoolStats();
|
||||
|
||||
StatsManager.RegisterStat(
|
||||
new Stat(
|
||||
"UDPBuffersPoolCount",
|
||||
"Buffers in the UDP buffers pool",
|
||||
"The number of buffers currently stored within the UDP buffers pool",
|
||||
"",
|
||||
"clientstack",
|
||||
Scene.Name,
|
||||
StatType.Pull,
|
||||
stat => stat.Value = m_udpBuffersPoolPtr + 1,
|
||||
StatVerbosity.Debug));
|
||||
|
||||
LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this);
|
||||
commands.Register();
|
||||
|
||||
}
|
||||
|
||||
public bool HandlesRegion(Location x)
|
||||
@@ -939,9 +848,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// 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;
|
||||
//int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200;
|
||||
|
||||
UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize);
|
||||
//UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize);
|
||||
UDPPacketBuffer buffer = GetNewUDPBuffer(udpClient.RemoteEndPoint);
|
||||
|
||||
// Zerocode if needed
|
||||
if (doZerocode)
|
||||
@@ -971,7 +881,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// If the packet data wasn't already copied during zerocoding, copy it now
|
||||
if (doCopy)
|
||||
{
|
||||
if (dataLength <= buffer.Data.Length)
|
||||
//if (dataLength <= buffer.Data.Length)
|
||||
if (dataLength <= LLUDPServer.MTU)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
|
||||
}
|
||||
@@ -979,7 +890,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
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);
|
||||
buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, dataLength);
|
||||
// buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, dataLength);
|
||||
buffer = GetNewUDPBuffer(udpClient.RemoteEndPoint);
|
||||
Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
|
||||
}
|
||||
}
|
||||
@@ -1017,6 +929,89 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
#endregion Queue or Send
|
||||
}
|
||||
|
||||
public unsafe UDPPacketBuffer ZeroEncode(UDPPacketBuffer input)
|
||||
{
|
||||
UDPPacketBuffer zb = GetNewUDPBuffer(null);
|
||||
int srclen = input.DataLength;
|
||||
byte[] src = input.Data;
|
||||
byte[] dest = zb.Data;
|
||||
|
||||
int zerolen = 6;
|
||||
byte zerocount = 0;
|
||||
|
||||
for (int i = zerolen; i < srclen; i++)
|
||||
{
|
||||
if (src[i] == 0x00)
|
||||
{
|
||||
zerocount++;
|
||||
if (zerocount == 0)
|
||||
{
|
||||
dest[zerolen++] = 0x00;
|
||||
dest[zerolen++] = 0xff;
|
||||
zerocount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zerocount != 0)
|
||||
{
|
||||
dest[zerolen++] = 0x00;
|
||||
dest[zerolen++] = zerocount;
|
||||
zerocount = 0;
|
||||
}
|
||||
|
||||
dest[zerolen++] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (zerocount != 0)
|
||||
{
|
||||
dest[zerolen++] = 0x00;
|
||||
dest[zerolen++] = zerocount;
|
||||
}
|
||||
|
||||
if(zerolen >= srclen)
|
||||
{
|
||||
FreeUDPBuffer(zb);
|
||||
|
||||
src[0] &= unchecked((byte)~Helpers.MSG_ZEROCODED);
|
||||
return input;
|
||||
}
|
||||
|
||||
Buffer.BlockCopy(src, 0, dest, 0, 6);
|
||||
|
||||
zb.RemoteEndPoint = input.RemoteEndPoint;
|
||||
zb.DataLength = zerolen;
|
||||
|
||||
FreeUDPBuffer(input);
|
||||
|
||||
return zb;
|
||||
}
|
||||
|
||||
public void SendUDPPacket(
|
||||
LLUDPClient udpClient, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method, bool forcequeue, bool zerocode)
|
||||
{
|
||||
bool highPriority = false;
|
||||
|
||||
if(zerocode)
|
||||
buffer = ZeroEncode(buffer);
|
||||
|
||||
if (category != ThrottleOutPacketType.Unknown && (category & ThrottleOutPacketType.HighPriority) != 0)
|
||||
{
|
||||
category = (ThrottleOutPacketType)((int)category & 127);
|
||||
highPriority = true;
|
||||
}
|
||||
|
||||
OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null);
|
||||
|
||||
// If we were not provided a method for handling unacked, use the UDPServer default method
|
||||
if ((outgoingPacket.Buffer.Data[0] & Helpers.MSG_RELIABLE) != 0)
|
||||
outgoingPacket.UnackedMethod = ((method == null) ? delegate (OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method);
|
||||
|
||||
if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forcequeue, highPriority))
|
||||
SendPacketFinal(outgoingPacket);
|
||||
}
|
||||
|
||||
public void SendAcks(LLUDPClient udpClient)
|
||||
{
|
||||
uint ack;
|
||||
@@ -1066,7 +1061,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
LLUDPClient udpClient = client.UDPClient;
|
||||
|
||||
if (!udpClient.IsConnected)
|
||||
if (!client.IsActive || !udpClient.IsConnected)
|
||||
return;
|
||||
|
||||
// Disconnect an agent if no packets are received for some time
|
||||
@@ -1136,14 +1131,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
internal void SendPacketFinal(OutgoingPacket outgoingPacket)
|
||||
{
|
||||
UDPPacketBuffer buffer = outgoingPacket.Buffer;
|
||||
if(buffer == null) // canceled packet
|
||||
return;
|
||||
LLUDPClient udpClient = outgoingPacket.Client;
|
||||
if (!udpClient.IsConnected)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
int dataLength = buffer.DataLength;
|
||||
|
||||
@@ -1154,7 +1151,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// no more ACKs to append
|
||||
int ackCount = 0;
|
||||
uint ack;
|
||||
while (dataLength + 5 < buffer.Data.Length && ackCount < 256 && udpClient.PendingAcks.Dequeue(out ack))
|
||||
while (dataLength + 5 < MTU && ackCount < 256 && udpClient.PendingAcks.Dequeue(out ack))
|
||||
{
|
||||
Utils.UIntToBytesBig(ack, buffer.Data, dataLength);
|
||||
dataLength += 4;
|
||||
@@ -1168,22 +1165,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// Set the appended ACKs flag on this packet
|
||||
buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS);
|
||||
}
|
||||
buffer.DataLength = dataLength;
|
||||
}
|
||||
|
||||
buffer.DataLength = dataLength;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1196,9 +1186,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
PacketsSentCount++;
|
||||
|
||||
SyncSend(buffer);
|
||||
|
||||
// Keep track of when this packet was sent out (right now)
|
||||
outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue;
|
||||
|
||||
if (outgoingPacket.UnackedMethod == null)
|
||||
FreeUDPBuffer(buffer);
|
||||
else if(!isResend)
|
||||
{
|
||||
// Add this packet to the list of ACK responses we are waiting on from the server
|
||||
udpClient.NeedAcks.Add(outgoingPacket);
|
||||
}
|
||||
|
||||
if (udpClient.DebugDataOutLevel > 0)
|
||||
m_log.DebugFormat(
|
||||
"[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}",
|
||||
@@ -1240,7 +1239,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
|
||||
|
||||
RecordMalformedInboundPacket(endPoint);
|
||||
|
||||
FreeUDPBuffer(buffer);
|
||||
return; // Drop undersized packet
|
||||
}
|
||||
|
||||
@@ -1260,21 +1259,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
|
||||
|
||||
RecordMalformedInboundPacket(endPoint);
|
||||
|
||||
FreeUDPBuffer(buffer);
|
||||
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);
|
||||
// get a buffer for zero decode using the udp buffers pool
|
||||
UDPPacketBuffer zerodecodebufferholder = null;
|
||||
byte[] zerodecodebuffer = null;
|
||||
// only if needed
|
||||
if (((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0))
|
||||
{
|
||||
zerodecodebufferholder = GetNewUDPBuffer(null);
|
||||
zerodecodebuffer = zerodecodebufferholder.Data;
|
||||
}
|
||||
|
||||
packet = Packet.BuildPacket(buffer.Data, ref packetEnd, zerodecodebuffer);
|
||||
// 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);
|
||||
// packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, zerodecodebuffer);
|
||||
if(zerodecodebufferholder != null)
|
||||
FreeUDPBuffer(zerodecodebufferholder);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -1292,7 +1299,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
}
|
||||
|
||||
RecordMalformedInboundPacket(endPoint);
|
||||
|
||||
FreeUDPBuffer(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1311,17 +1318,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
lock (m_pendingCache)
|
||||
{
|
||||
if (m_pendingCache.Contains(endPoint))
|
||||
{
|
||||
FreeUDPBuffer(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
m_pendingCache.AddOrUpdate(endPoint, new Queue<UDPPacketBuffer>(), 60);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
Util.FireAndForget(HandleUseCircuitCode, new object[] { endPoint, packet });
|
||||
FreeUDPBuffer(buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1336,24 +1342,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
queue.Enqueue(buffer);
|
||||
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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
FreeUDPBuffer(buffer);
|
||||
|
||||
// Determine which agent this packet came from
|
||||
if (client == null || !(client is LLClientView))
|
||||
{
|
||||
@@ -1471,10 +1463,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length);
|
||||
#endregion BinaryStats
|
||||
|
||||
|
||||
//AgentUpdate removed from here
|
||||
|
||||
|
||||
#region Ping Check Handling
|
||||
|
||||
if (packet.Type == PacketType.StartPingCheck)
|
||||
@@ -1506,17 +1494,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
|
||||
IncomingPacket incomingPacket;
|
||||
|
||||
// Inbox insertion
|
||||
if (UsePools)
|
||||
{
|
||||
incomingPacket = m_incomingPacketPool.GetObject();
|
||||
incomingPacket.Client = (LLClientView)client;
|
||||
incomingPacket.Packet = packet;
|
||||
}
|
||||
else
|
||||
{
|
||||
incomingPacket = new IncomingPacket((LLClientView)client, packet);
|
||||
}
|
||||
incomingPacket = new IncomingPacket((LLClientView)client, packet);
|
||||
|
||||
// if (incomingPacket.Packet.Type == PacketType.AgentUpdate ||
|
||||
// incomingPacket.Packet.Type == PacketType.ChatFromViewer)
|
||||
@@ -1525,7 +1503,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// else
|
||||
// packetInbox.Enqueue(incomingPacket);
|
||||
packetInbox.Add(incomingPacket);
|
||||
|
||||
}
|
||||
|
||||
#region BinaryStats
|
||||
@@ -1685,7 +1662,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
m_log.DebugFormat("[LLUDPSERVER]: Client created but no pending queue present");
|
||||
return;
|
||||
|
||||
}
|
||||
m_pendingCache.Remove(endPoint);
|
||||
}
|
||||
@@ -1881,13 +1857,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
byte[] packetData = ack.ToBytes();
|
||||
int length = packetData.Length;
|
||||
|
||||
UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndpoint, length);
|
||||
UDPPacketBuffer buffer = GetNewUDPBuffer(remoteEndpoint);
|
||||
buffer.DataLength = length;
|
||||
|
||||
Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length);
|
||||
|
||||
// AsyncBeginSend(buffer);
|
||||
SyncSend(buffer);
|
||||
FreeUDPBuffer(buffer);
|
||||
}
|
||||
|
||||
protected bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo)
|
||||
@@ -1982,21 +1959,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
Scene.ThreadAlive(1);
|
||||
try
|
||||
{
|
||||
packetInbox.TryTake(out incomingPacket, 250);
|
||||
packetInbox.TryTake(out incomingPacket, 4500);
|
||||
|
||||
if (incomingPacket != null && IsRunningInbound)
|
||||
{
|
||||
ProcessInPacket(incomingPacket);
|
||||
|
||||
if (UsePools)
|
||||
{
|
||||
incomingPacket.Client = null;
|
||||
m_incomingPacketPool.ReturnObject(incomingPacket);
|
||||
}
|
||||
incomingPacket = null;
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
Thread.ResetAbort();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_log.Error("[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex.Message, ex);
|
||||
}
|
||||
@@ -2025,7 +2000,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
Scene.ThreadAlive(2);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
m_packetSent = false;
|
||||
@@ -2080,7 +2054,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
}
|
||||
else if (!m_packetSent)
|
||||
// Thread.Sleep((int)TickCountResolution); outch this is bad on linux
|
||||
Thread.Sleep(15); // match the 16ms of windows7, dont ask 16 or win may decide to do 32ms.
|
||||
Thread.Sleep(15); // match the 16ms of windows, dont ask 16 or win may decide to do 32ms.
|
||||
|
||||
Watchdog.UpdateThread();
|
||||
}
|
||||
@@ -2104,14 +2078,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
|
||||
if (udpClient.IsConnected)
|
||||
{
|
||||
if (m_resendUnacked)
|
||||
if (client.IsActive && m_resendUnacked)
|
||||
HandleUnacked(llClient);
|
||||
|
||||
if (m_sendAcks)
|
||||
SendAcks(udpClient);
|
||||
if (client.IsActive)
|
||||
{
|
||||
if (m_sendAcks)
|
||||
SendAcks(udpClient);
|
||||
|
||||
if (m_sendPing)
|
||||
SendPing(udpClient);
|
||||
if (m_sendPing)
|
||||
SendPing(udpClient);
|
||||
}
|
||||
|
||||
// Dequeue any outgoing packets that are within the throttle limits
|
||||
if (udpClient.DequeueOutgoing())
|
||||
@@ -2124,7 +2101,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
m_log.Error(
|
||||
string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex);
|
||||
}
|
||||
client = null;
|
||||
}
|
||||
|
||||
#region Emergency Monitoring
|
||||
|
||||
@@ -777,41 +777,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
m_udpServer.StopOutbound();
|
||||
}
|
||||
|
||||
private void HandlePoolCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
return;
|
||||
|
||||
if (args.Length != 4)
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
|
||||
return;
|
||||
}
|
||||
|
||||
string enabled = args[3];
|
||||
|
||||
if (enabled == "on")
|
||||
{
|
||||
if (m_udpServer.EnablePools())
|
||||
{
|
||||
m_udpServer.EnablePoolStats();
|
||||
MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_udpServer.Scene.Name);
|
||||
}
|
||||
}
|
||||
else if (enabled == "off")
|
||||
{
|
||||
if (m_udpServer.DisablePools())
|
||||
{
|
||||
m_udpServer.DisablePoolStats();
|
||||
MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_udpServer.Scene.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MainConsole.Instance.Output("Usage: debug lludp pool <on|off>");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAgentUpdateCommand(string module, string[] args)
|
||||
{
|
||||
if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
|
||||
@@ -834,8 +799,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled");
|
||||
|
||||
MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_udpServer.Scene.Name, m_udpServer.UsePools ? "on" : "off");
|
||||
|
||||
MainConsole.Instance.OutputFormat(
|
||||
"Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel);
|
||||
}
|
||||
|
||||
279
OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs
Normal file
279
OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* 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 OpenSim.Framework;
|
||||
using Nini.Config;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
public sealed class LLUDPZeroEncoder
|
||||
{
|
||||
private byte[] m_tmp = new byte[16];
|
||||
private byte[] m_dest;
|
||||
private int zerocount;
|
||||
private int pos;
|
||||
|
||||
public LLUDPZeroEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
public LLUDPZeroEncoder(byte[] data)
|
||||
{
|
||||
m_dest = data;
|
||||
zerocount = 0;
|
||||
}
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_dest;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_dest = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int ZeroCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return zerocount;
|
||||
}
|
||||
set
|
||||
{
|
||||
zerocount = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
set
|
||||
{
|
||||
pos = value;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void AddZeros(int len)
|
||||
{
|
||||
zerocount += len;
|
||||
while (zerocount > 255)
|
||||
{
|
||||
m_dest[pos++] = 0x00;
|
||||
m_dest[pos++] = 0xff;
|
||||
zerocount -= 256;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe int Finish()
|
||||
{
|
||||
if(zerocount > 0)
|
||||
{
|
||||
m_dest[pos++] = 0x00;
|
||||
m_dest[pos++] = (byte)zerocount;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
public unsafe void AddBytes(byte[] src, int srclen)
|
||||
{
|
||||
for (int i = 0; i < srclen; ++i)
|
||||
{
|
||||
if (src[i] == 0x00)
|
||||
{
|
||||
zerocount++;
|
||||
if (zerocount == 0)
|
||||
{
|
||||
m_dest[pos++] = 0x00;
|
||||
m_dest[pos++] = 0xff;
|
||||
zerocount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zerocount != 0)
|
||||
{
|
||||
m_dest[pos++] = 0x00;
|
||||
m_dest[pos++] = (byte)zerocount;
|
||||
zerocount = 0;
|
||||
}
|
||||
|
||||
m_dest[pos++] = src[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void AddByte(byte v)
|
||||
{
|
||||
if (v == 0x00)
|
||||
{
|
||||
zerocount++;
|
||||
if (zerocount == 0)
|
||||
{
|
||||
m_dest[pos++] = 0x00;
|
||||
m_dest[pos++] = 0xff;
|
||||
zerocount++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zerocount != 0)
|
||||
{
|
||||
m_dest[pos++] = 0x00;
|
||||
m_dest[pos++] = (byte)zerocount;
|
||||
zerocount = 0;
|
||||
}
|
||||
|
||||
m_dest[pos++] = v;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddInt16(short v)
|
||||
{
|
||||
if (v == 0)
|
||||
AddZeros(2);
|
||||
else
|
||||
{
|
||||
Utils.Int16ToBytes(v, m_tmp, 0);
|
||||
AddBytes(m_tmp, 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddUInt16(ushort v)
|
||||
{
|
||||
if (v == 0)
|
||||
AddZeros(2);
|
||||
else
|
||||
{
|
||||
Utils.UInt16ToBytes(v, m_tmp, 0);
|
||||
AddBytes(m_tmp, 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddInt(int v)
|
||||
{
|
||||
if (v == 0)
|
||||
AddZeros(4);
|
||||
else
|
||||
{
|
||||
Utils.IntToBytesSafepos(v, m_tmp, 0);
|
||||
AddBytes(m_tmp, 4);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void AddUInt(uint v)
|
||||
{
|
||||
if (v == 0)
|
||||
AddZeros(4);
|
||||
else
|
||||
{
|
||||
Utils.UIntToBytesSafepos(v, m_tmp, 0);
|
||||
AddBytes(m_tmp, 4);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFloatToUInt16(float v, float range)
|
||||
{
|
||||
Utils.FloatToUInt16Bytes(v, range, m_tmp, 0);
|
||||
AddBytes(m_tmp, 2);
|
||||
}
|
||||
|
||||
public void AddFloat(float v)
|
||||
{
|
||||
if (v == 0f)
|
||||
AddZeros(4);
|
||||
else
|
||||
{
|
||||
Utils.FloatToBytesSafepos(v, m_tmp, 0);
|
||||
AddBytes(m_tmp, 4);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddInt64(long v)
|
||||
{
|
||||
if (v == 0)
|
||||
AddZeros(8);
|
||||
else
|
||||
{
|
||||
Utils.Int64ToBytesSafepos(v, m_tmp, 0);
|
||||
AddBytes(m_tmp, 8);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddUInt64(ulong v)
|
||||
{
|
||||
if (v == 0)
|
||||
AddZeros(8);
|
||||
else
|
||||
{
|
||||
Utils.UInt64ToBytesSafepos(v, m_tmp, 0);
|
||||
AddBytes(m_tmp, 8);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVector3(Vector3 v)
|
||||
{
|
||||
if (v == Vector3.Zero)
|
||||
AddZeros(12);
|
||||
else
|
||||
{
|
||||
v.ToBytes(m_tmp, 0);
|
||||
AddBytes(m_tmp, 12);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddVector4(Vector4 v)
|
||||
{
|
||||
if (v == Vector4.Zero)
|
||||
AddZeros(16);
|
||||
else
|
||||
{
|
||||
v.ToBytes(m_tmp, 0);
|
||||
AddBytes(m_tmp, 16);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddNormQuat(Quaternion v)
|
||||
{
|
||||
v.ToBytes(m_tmp, 0);
|
||||
AddBytes(m_tmp, 12);
|
||||
}
|
||||
|
||||
public void AddUUID(UUID v)
|
||||
{
|
||||
v.ToBytes(m_tmp, 0);
|
||||
AddBytes(m_tmp, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
@@ -57,15 +58,9 @@ namespace OpenMetaverse
|
||||
/// <summary>UDP socket, used in either client or server mode</summary>
|
||||
private Socket m_udpSocket;
|
||||
|
||||
/// <summary>
|
||||
/// Are we to use object pool(s) to reduce memory churn when receiving data?
|
||||
/// </summary>
|
||||
public bool UsePools { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pool to use for handling data. May be null if UsePools = false;
|
||||
/// </summary>
|
||||
protected OpenSim.Framework.Pool<UDPPacketBuffer> Pool { get; private set; }
|
||||
public static Object m_udpBuffersPoolLock = new Object();
|
||||
public static UDPPacketBuffer[] m_udpBuffersPool = new UDPPacketBuffer[1000];
|
||||
public static int m_udpBuffersPoolPtr = -1;
|
||||
|
||||
/// <summary>Returns true if the server is currently listening for inbound packets, otherwise false</summary>
|
||||
public bool IsRunningInbound { get; private set; }
|
||||
@@ -186,6 +181,37 @@ namespace OpenMetaverse
|
||||
if(m_udpSocket !=null)
|
||||
try { m_udpSocket.Close(); } catch { }
|
||||
}
|
||||
|
||||
public UDPPacketBuffer GetNewUDPBuffer(IPEndPoint remoteEndpoint)
|
||||
{
|
||||
lock (m_udpBuffersPoolLock)
|
||||
{
|
||||
if (m_udpBuffersPoolPtr >= 0)
|
||||
{
|
||||
UDPPacketBuffer buf = m_udpBuffersPool[m_udpBuffersPoolPtr];
|
||||
m_udpBuffersPool[m_udpBuffersPoolPtr] = null;
|
||||
m_udpBuffersPoolPtr--;
|
||||
buf.RemoteEndPoint = remoteEndpoint;
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
return new UDPPacketBuffer(remoteEndpoint);
|
||||
}
|
||||
|
||||
public void FreeUDPBuffer(UDPPacketBuffer buf)
|
||||
{
|
||||
lock (m_udpBuffersPoolLock)
|
||||
{
|
||||
if (m_udpBuffersPoolPtr < 999)
|
||||
{
|
||||
buf.RemoteEndPoint = null;
|
||||
buf.DataLength = 0;
|
||||
m_udpBuffersPoolPtr++;
|
||||
m_udpBuffersPool[m_udpBuffersPoolPtr] = buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start inbound UDP packet handling.
|
||||
/// </summary>
|
||||
@@ -202,6 +228,7 @@ namespace OpenMetaverse
|
||||
/// manner (not throwing an exception when the remote side resets the
|
||||
/// connection). This call is ignored on Mono where the flag is not
|
||||
/// necessary</remarks>
|
||||
|
||||
public virtual void StartInbound(int recvBufferSize)
|
||||
{
|
||||
if (!IsRunningInbound)
|
||||
@@ -306,101 +333,55 @@ namespace OpenMetaverse
|
||||
IsRunningOutbound = false;
|
||||
}
|
||||
|
||||
public virtual bool EnablePools()
|
||||
{
|
||||
if (!UsePools)
|
||||
{
|
||||
Pool = new Pool<UDPPacketBuffer>(() => new UDPPacketBuffer(), 500);
|
||||
|
||||
UsePools = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool DisablePools()
|
||||
{
|
||||
if (UsePools)
|
||||
{
|
||||
UsePools = false;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
private void AsyncBeginReceive()
|
||||
{
|
||||
UDPPacketBuffer buf;
|
||||
if (!IsRunningInbound)
|
||||
return;
|
||||
|
||||
// FIXME: Disabled for now as this causes issues with reused packet objects interfering with each other
|
||||
// on Windows with m_asyncPacketHandling = true, though this has not been seen on Linux.
|
||||
// Possibly some unexpected issue with fetching UDP data concurrently with multiple threads. Requires more investigation.
|
||||
// if (UsePools)
|
||||
// buf = Pool.GetObject();
|
||||
// else
|
||||
buf = new UDPPacketBuffer();
|
||||
|
||||
if (IsRunningInbound)
|
||||
UDPPacketBuffer buf = GetNewUDPBuffer(new IPEndPoint(IPAddress.Any, 0)); // we need a fresh one here, for now at least
|
||||
try
|
||||
{
|
||||
try
|
||||
// kick off an async read
|
||||
m_udpSocket.BeginReceiveFrom(
|
||||
buf.Data,
|
||||
0,
|
||||
buf.Data.Length,
|
||||
SocketFlags.None,
|
||||
ref buf.RemoteEndPoint,
|
||||
AsyncEndReceive,
|
||||
buf);
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
if (e.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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_udpSocket.BeginReceiveFrom(
|
||||
buf.Data,
|
||||
0,
|
||||
buf.Data.Length,
|
||||
SocketFlags.None,
|
||||
ref buf.RemoteEndPoint,
|
||||
AsyncEndReceive,
|
||||
buf);
|
||||
salvaged = true;
|
||||
}
|
||||
|
||||
m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort);
|
||||
catch (SocketException) { }
|
||||
catch (ObjectDisposedException) { return; }
|
||||
}
|
||||
|
||||
m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort);
|
||||
}
|
||||
catch (ObjectDisposedException e)
|
||||
{
|
||||
m_log.Error(
|
||||
string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error(
|
||||
string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error(
|
||||
string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,11 +434,6 @@ namespace OpenMetaverse
|
||||
UdpReceives, se.ErrorCode),
|
||||
se);
|
||||
}
|
||||
catch (ObjectDisposedException e)
|
||||
{
|
||||
m_log.Error(
|
||||
string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error(
|
||||
@@ -465,14 +441,12 @@ namespace OpenMetaverse
|
||||
}
|
||||
finally
|
||||
{
|
||||
// if (UsePools)
|
||||
// Pool.ReturnObject(buffer);
|
||||
|
||||
AsyncBeginReceive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* not in use
|
||||
public void AsyncBeginSend(UDPPacketBuffer buf)
|
||||
{
|
||||
// if (IsRunningOutbound)
|
||||
@@ -511,9 +485,11 @@ namespace OpenMetaverse
|
||||
catch (SocketException) { }
|
||||
catch (ObjectDisposedException) { }
|
||||
}
|
||||
|
||||
*/
|
||||
public void SyncSend(UDPPacketBuffer buf)
|
||||
{
|
||||
if(buf.RemoteEndPoint == null)
|
||||
return; // already expired
|
||||
try
|
||||
{
|
||||
m_udpSocket.SendTo(
|
||||
@@ -527,7 +503,7 @@ namespace OpenMetaverse
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
m_log.Warn("[UDPBASE]: sync send SocketException {0} " + e.Message);
|
||||
m_log.WarnFormat("[UDPBASE]: sync send SocketException {0} {1}", buf.RemoteEndPoint, e.Message);
|
||||
}
|
||||
catch (ObjectDisposedException) { }
|
||||
}
|
||||
|
||||
@@ -66,9 +66,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
/// </summary>
|
||||
public Int64 MinimumAdaptiveThrottleRate;
|
||||
|
||||
/// <summary>Amount of the texture throttle to steal for the task throttle</summary>
|
||||
public double CannibalizeTextureRate;
|
||||
|
||||
public int ClientMaxRate;
|
||||
public float BrustTime;
|
||||
|
||||
@@ -104,12 +101,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false);
|
||||
AdaptiveThrottlesEnabled = false;
|
||||
MinimumAdaptiveThrottleRate = throttleConfig.GetInt("adaptive_throttle_min_bps", 32000);
|
||||
|
||||
// http textures do use udp bandwidth setting
|
||||
// CannibalizeTextureRate = (double)throttleConfig.GetFloat("CannibalizeTextureRate", 0.0f);
|
||||
// CannibalizeTextureRate = Util.Clamp<double>(CannibalizeTextureRate,0.0, 0.9);
|
||||
CannibalizeTextureRate = 0f;
|
||||
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
@@ -189,8 +189,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
// Process all the pending adds
|
||||
OutgoingPacket pendingAdd;
|
||||
while (m_pendingAdds.TryDequeue(out pendingAdd))
|
||||
{
|
||||
if (pendingAdd != null)
|
||||
m_packets[pendingAdd.SequenceNumber] = pendingAdd;
|
||||
}
|
||||
|
||||
// Process all the pending removes, including updating statistics and round-trip times
|
||||
PendingAck pendingAcknowledgement;
|
||||
@@ -204,13 +206,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
{
|
||||
m_packets.Remove(pendingAcknowledgement.SequenceNumber);
|
||||
|
||||
// Update stats
|
||||
Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
|
||||
|
||||
ackedPacket.Client.FreeUDPBuffer(ackedPacket.Buffer);
|
||||
ackedPacket.Buffer = null;
|
||||
|
||||
// As with other network applications, assume that an acknowledged packet is an
|
||||
// indication that the network can handle a little more load, speed up the transmission
|
||||
ackedPacket.Client.FlowThrottle.AcknowledgePackets(1);
|
||||
|
||||
// Update stats
|
||||
Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
|
||||
|
||||
if (!pendingAcknowledgement.FromResend)
|
||||
{
|
||||
// Calculate the round-trip time for this packet and its ACK
|
||||
@@ -244,6 +249,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||
|
||||
// Update stats
|
||||
Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength);
|
||||
|
||||
removedPacket.Client.FreeUDPBuffer(removedPacket.Buffer);
|
||||
removedPacket.Buffer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user