123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- /*
- * 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 System.Collections.Generic;
- using System.Reflection;
- using System.Threading;
- using Nini.Config;
- using log4net;
- using OpenMetaverse;
- using OpenSim.Framework;
- using OpenSim.Framework.Monitoring;
- using OpenSim.Region.Framework.Interfaces;
- using OpenSim.Region.Framework.Scenes;
- using Mono.Addins;
- namespace OpenSim.Region.CoreModules.Agent.Xfer
- {
- [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XferModule")]
- public class XferModule : INonSharedRegionModule, IXfer
- {
- private Scene m_scene;
- private Dictionary<string, FileData> NewFiles = new Dictionary<string, FileData>();
- private Dictionary<ulong, XferDownLoad> Transfers = new Dictionary<ulong, XferDownLoad>();
- private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- private object timeTickLock = new object();
- private int lastTimeTick = 0;
- private int lastFilesExpire = 0;
- private bool inTimeTick = false;
- public struct XferRequest
- {
- public IClientAPI remoteClient;
- public ulong xferID;
- public string fileName;
- public DateTime timeStamp;
- }
- private class FileData
- {
- public byte[] Data;
- public int refsCount;
- public int timeStampMS;
- }
- #region INonSharedRegionModule Members
- public void Initialise(IConfigSource config)
- {
- lastTimeTick = (int)Util.GetTimeStampMS() + 30000;
- lastFilesExpire = lastTimeTick + 180000;
- }
- public void AddRegion(Scene scene)
- {
- m_scene = scene;
- m_scene.RegisterModuleInterface<IXfer>(this);
- m_scene.EventManager.OnNewClient += NewClient;
- m_scene.EventManager.OnRegionHeartbeatEnd += OnTimeTick;
- }
- public void RemoveRegion(Scene scene)
- {
- m_scene.EventManager.OnNewClient -= NewClient;
- m_scene.EventManager.OnRegionHeartbeatEnd -= OnTimeTick;
- m_scene.UnregisterModuleInterface<IXfer>(this);
- m_scene = null;
- }
- public void RegionLoaded(Scene scene)
- {
- }
- public Type ReplaceableInterface
- {
- get { return null; }
- }
- public void Close()
- {
- }
- public string Name
- {
- get { return "XferModule"; }
- }
- #endregion
- public void OnTimeTick(Scene scene)
- {
- // we are on a heartbeat thread we there can be several
- if(Monitor.TryEnter(timeTickLock))
- {
- if(!inTimeTick)
- {
- int now = (int)Util.GetTimeStampMS();
- if(now - lastTimeTick > 750)
- {
- if(Transfers.Count == 0 && NewFiles.Count == 0)
- lastTimeTick = now;
- else
- {
- inTimeTick = true;
- //don't overload busy heartbeat
- WorkManager.RunInThreadPool(
- delegate
- {
- transfersTimeTick(now);
- expireFiles(now);
- lastTimeTick = now;
- inTimeTick = false;
- },
- null,
- "XferTimeTick");
- }
- }
- }
- Monitor.Exit(timeTickLock);
- }
- }
- #region IXfer Members
- /// <summary>
- /// Let the Xfer module know about a file that the client is about to request.
- /// Caller is responsible for making sure that the file is here before
- /// the client starts the XferRequest.
- /// </summary>
- /// <param name="fileName"></param>
- /// <param name="data"></param>
- /// <returns></returns>
- public bool AddNewFile(string fileName, byte[] data)
- {
- lock (NewFiles)
- {
- int now = (int)Util.GetTimeStampMS();
- if (NewFiles.ContainsKey(fileName))
- {
- NewFiles[fileName].refsCount++;
- NewFiles[fileName].Data = data;
- NewFiles[fileName].timeStampMS = now;
- }
- else
- {
- FileData fd = new FileData();
- fd.refsCount = 1;
- fd.Data = data;
- fd.timeStampMS = now;
- NewFiles.Add(fileName, fd);
- }
- }
- return true;
- }
- #endregion
- public void expireFiles(int now)
- {
- lock (NewFiles)
- {
- // hopefully we will not have many files so nasty code will do it
- if(now - lastFilesExpire > 120000)
- {
- lastFilesExpire = now;
- List<string> expires = new List<string>();
- foreach(string fname in NewFiles.Keys)
- {
- if(NewFiles[fname].refsCount == 0 && now - NewFiles[fname].timeStampMS > 120000)
- expires.Add(fname);
- }
- foreach(string fname in expires)
- NewFiles.Remove(fname);
- }
- }
- }
- public void NewClient(IClientAPI client)
- {
- client.OnRequestXfer += RequestXfer;
- client.OnConfirmXfer += AckPacket;
- client.OnAbortXfer += AbortXfer;
- }
- public void OnClientClosed(IClientAPI client)
- {
- client.OnRequestXfer -= RequestXfer;
- client.OnConfirmXfer -= AckPacket;
- client.OnAbortXfer -= AbortXfer;
- }
- private void RemoveOrDecrementFile(string fileName)
- {
- // NewFiles must be locked
- if (NewFiles.ContainsKey(fileName))
- {
- if (NewFiles[fileName].refsCount == 1)
- NewFiles.Remove(fileName);
- else
- NewFiles[fileName].refsCount--;
- }
- }
- public void transfersTimeTick(int now)
- {
- XferDownLoad[] xfrs;
- lock(Transfers)
- {
- if(Transfers.Count == 0)
- return;
- xfrs = new XferDownLoad[Transfers.Count];
- Transfers.Values.CopyTo(xfrs,0);
- }
-
- foreach(XferDownLoad xfr in xfrs)
- {
- if(xfr.checkTime(now))
- {
- ulong xfrID = xfr.XferID;
- lock(Transfers)
- {
- if(Transfers.ContainsKey(xfrID))
- Transfers.Remove(xfrID);
- }
- }
- }
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="remoteClient"></param>
- /// <param name="xferID"></param>
- /// <param name="fileName"></param>
- public void RequestXfer(IClientAPI remoteClient, ulong xferID, string fileName)
- {
- lock (NewFiles)
- {
- if (NewFiles.ContainsKey(fileName))
- {
- lock(Transfers)
- {
- if (!Transfers.ContainsKey(xferID))
- {
- byte[] fileData = NewFiles[fileName].Data;
- int burstSize = remoteClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Task) >> 10;
- burstSize *= remoteClient.PingTimeMS;
- burstSize >>= 10; // ping is ms, 1 round trip
- if(burstSize > 32)
- burstSize = 32;
- XferDownLoad transaction =
- new XferDownLoad(fileName, fileData, xferID, remoteClient, burstSize);
- Transfers.Add(xferID, transaction);
- transaction.StartSend();
- // The transaction for this file is on its way
- RemoveOrDecrementFile(fileName);
- }
- }
- }
- else
- m_log.WarnFormat("[Xfer]: {0} not found", fileName);
- }
- }
- public void AckPacket(IClientAPI remoteClient, ulong xferID, uint packet)
- {
- lock (Transfers)
- {
- if (Transfers.ContainsKey(xferID))
- {
- if (Transfers[xferID].AckPacket(packet))
- Transfers.Remove(xferID);
- }
- }
- }
- public void AbortXfer(IClientAPI remoteClient, ulong xferID)
- {
- lock (Transfers)
- {
- if (Transfers.ContainsKey(xferID))
- {
- Transfers[xferID].done();
- Transfers.Remove(xferID);
- }
- }
- }
- #region Nested type: XferDownLoad
- public class XferDownLoad
- {
- public IClientAPI remoteClient;
- public byte[] Data = Array.Empty<byte>();
- public string FileName = String.Empty;
- public ulong XferID = 0;
- public bool isDeleted = false;
- private object myLock = new object();
- private int lastACKTimeMS;
- private int LastPacket;
- private int lastBytes;
- private int lastSentPacket;
- private int lastAckPacket;
- private int burstSize; // additional packets, so can be zero
- private int retries;
- public XferDownLoad(string fileName, byte[] data, ulong xferID, IClientAPI client, int burstsz)
- {
- FileName = fileName;
- Data = data;
- XferID = xferID;
- remoteClient = client;
- burstSize = burstsz;
- }
- public XferDownLoad()
- {
- }
- public void done()
- {
- if(!isDeleted)
- {
- Data = null;
- isDeleted = true;
- }
- }
- /// <summary>
- /// Start a transfer
- /// </summary>
- /// <returns>True if the transfer is complete, false if not</returns>
- public void StartSend()
- {
- lock(myLock)
- {
- if(Data.Length == 0) //??
- {
- LastPacket = 0;
- lastBytes = 0;
- burstSize = 0;
- }
- else
- {
- // payload of 1024bytes
- LastPacket = Data.Length >> 10;
- lastBytes = Data.Length & 0x3ff;
- if(lastBytes == 0)
- {
- lastBytes = 1024;
- LastPacket--;
- }
- }
- lastAckPacket = -1;
- lastSentPacket = -1;
- retries = 0;
- SendBurst();
- return;
- }
- }
- private void SendBurst()
- {
- int start = lastAckPacket + 1;
- int end = start + burstSize;
- if (end > LastPacket)
- end = LastPacket;
- while (start <= end)
- SendPacket(start++);
- lastACKTimeMS = (int)Util.GetTimeStampMS() + 1000; // reset timeout with some slack for queues delays
- }
- private void SendPacket(int pkt)
- {
- if(pkt > LastPacket)
- return;
- int pktsize;
- uint pktid;
- if (pkt == LastPacket)
- {
- pktsize = lastBytes;
- pktid = (uint)pkt | 0x80000000u;
- }
- else
- {
- pktsize = 1024;
- pktid = (uint)pkt;
- }
- remoteClient.SendXferPacket(XferID, pktid, Data, pkt << 10, pktsize, true);
- lastSentPacket = pkt;
- }
- /// <summary>
- /// Respond to an ack packet from the client
- /// </summary>
- /// <param name="packet"></param>
- /// <returns>True if the transfer is complete, false otherwise</returns>
- public bool AckPacket(uint packet)
- {
- lock(myLock)
- {
- if(isDeleted)
- return true;
- packet &= 0x7fffffff;
- if (lastAckPacket < packet)
- lastAckPacket = (int)packet;
- else if (lastAckPacket == LastPacket)
- {
- done();
- return true;
- }
- lastACKTimeMS = (int)Util.GetTimeStampMS();
- retries = 0;
- SendPacket(lastSentPacket + 1);
- return false;
- }
- }
- public bool checkTime(int now)
- {
- if (Monitor.TryEnter(myLock))
- {
- if (!isDeleted)
- {
- int timeMS = now - lastACKTimeMS;
- int tout = 5 * remoteClient.PingTimeMS;
- if (tout < 1000)
- tout = 1000;
- else if(tout > 10000)
- tout = 10000;
- if (timeMS > tout)
- {
- if (++retries > 4)
- done();
- else
- {
- burstSize = lastSentPacket - lastAckPacket;
- SendBurst();
- }
- }
- }
- bool isdel = isDeleted;
- Monitor.Exit(myLock);
- return isdel;
- }
- return false;
- }
- }
- #endregion
- }
- }
|