Как управлять временными проблемами сети при использовании UrlLoader?
Я создаю RSS-тикер для использования на нескольких дисплеях в офисе. Этот RSS-тикер будет работать совместно с читателем Twiter Feed, погодным виджетом и часами, все в одном.fla.
Поскольку существует множество таких дисплеев, я не хочу, чтобы каждый из них выходил в интернет для получения информации о RSS, Twitter и погоде. Все эти данные содержатся в нескольких простых XML-файлах, которые загружаются из Интернета.
Я создал службу Windows, которая работает на нашем сервере. Этот сервис выполняет всю работу, связанную с отслеживанием обновлений и загрузкой новых файлов данных в общий доступ в нашей сети. Затем у меня есть флэш-панели на всех дисплеях, которые получают информацию из сетевых копий. Таким образом, данные загружаются один раз, а все остальное просто обращается к этому локальному кешу.
Все работает, но случайно RSS-тикер перестанет работать. У меня нет никакого способа удаленного доступа к этим дисплеям, чтобы проверить их журналы ошибок, но у меня возникает точно такая же проблема в моей локальной тестовой среде, когда я отключаю кабель Ethernet. Приборная панель пытается получить доступ к сетевым копиям RSS, происходит сбой, а затем продолжает сбой с каждым тактом таймера, даже после восстановления соединения.
Я хочу быть в состоянии изящно переждать отключение, а затем перезапустить тикер RSS, как только дисплей снова подключится. Я понятия не имею, как это сделать. Вот мой код, связанный с RSS:
// used to load a local xml file (stored in the same folder as the dashboard) that tells
// the dashboards which RSS feeds can be found on the network, and where they are located.
// It also contains the limits for how long each feed should be shown (both a time limit,
// and a maximimum number of feed items)
var rssListXMLLoader:URLLoader = new URLLoader();
rssListXMLLoader.addEventListener(Event.COMPLETE, rssListXMLLoaded);
rssListXMLLoader.addEventListener(IOErrorEvent.IO_ERROR, ioRSSListXMLLoaderErrorHandler);
// used to load the individual RRS feeds
var rssFeedXMLLoader:URLLoader = new URLLoader();
rssFeedXMLLoader.addEventListener(Event.COMPLETE, rssFeedXMLLoaded);
rssFeedXMLLoader.addEventListener(IOErrorEvent.IO_ERROR, ioRSSFeedXMLLoaderErrorHandler);
// rssListXMLData:XML and rssFeedXMLData:XML are used to store the XML data that is returned
// by the above loaders.
var rssListXMLData:XML = new XML();
var rssFeedXMLData:XML = new XML();
var currentRSSIndex:int = 2^10;
var currentFeedIndex:int = 2^10;
var currentLimit_Feeds:int = 2^10;
var currentLimit_Minutes:int = 2^10;
var limitStartTime:Number = new Date().time;
var gettingNewFeed = false;
// the blow Timer is used to keep the ticker moving. Each tick, it checks to see of the
// current RSS on sceen is has any text left. If it does, it removes the first letter
// causing the string to appear as if it is scrolling from right to left. If it has no
// more text, it gets the next feed.
var rssTickerTMR:Timer = new Timer(100, 0);
rssTickerTMR.addEventListener(TimerEvent.TIMER, updateRSSTicker);
rssTickerTMR.start();
updateRSSTicker();
function updateRSSTicker(e:Event = null):void
{
if(ins_rssContent.text.length == 0)
{
if(!gettingNewFeed)
{
gettingNewFeed = true;
getNewFeed();
}
}
else
{
ins_rssContent.text = ins_rssContent.text.substring(1);
}
}
// the below function checks to see if the RSS List is loaded. If it isn't it loads it,
// if it is already loaded, it calls the rssListXMLLoaded. This way, rssListXMLLoaded is
// never called unless the RSS List is already loaded.
function getNewFeed():void
{
if (rssListXMLData == "")
{
trace("Loading RSS List XML");
rssListXMLLoader.load(new URLRequest("rssFeeds.xml"));
}
else
{
rssListXMLLoaded();
}
}
// the below function manages which feed needs to be used from the list.
function rssListXMLLoaded(e:Event = null):void
{
if (e != null)
{
rssListXMLData = new XML(e.target.data);
}
currentFeedIndex++;
if (
currentFeedIndex >= rssFeedXMLData.channel["item"].length() ||
currentFeedIndex >= currentLimit_Feeds ||
new Date().time - limitStartTime >= (currentLimit_Minutes * 1000 * 60)
)
{
limitStartTime = new Date().time;
currentFeedIndex = 0;
currentRSSIndex++;
currentRSSIndex = (currentRSSIndex >= rssListXML.children().length()) ? 0 : currentRSSIndex;
currentLimit_Feeds = Number(rssListXMLData.RSSFeed[currentRSSIndex].@limitFeeds)
currentLimit_Minutes = Number(rssListXMLData.RSSFeed[currentRSSIndex].@limitMinutes)
}
try
{
rssFeedXMLLoader.load(new URLRequest(rssListXMLData.RSSFeed[currentRSSIndex]));
}
catch(e:Error)
{
trace("whoops, try again");
}
}
// the below function sets the text on the dashboard, and sets gettingNewfeed to false. This allows the
// rssTickerTMR:Timer to start removing the first letter of the newly added feed, causing the text to "scroll"
function rssFeedXMLLoaded(e:Event):void
{
rssFeedXMLData = new XML(e.target.data);
if (rssFeedXMLData.channel["item"].length() > 0)
{
var dateString:String = rssFeedXMLData.channel["item"][currentFeedIndex].pubDate;
var date:Date = new Date(Date.parse(dateString.replace(" Z","")));
if (date.toString() != "Invalid Date")
{
dateString = " @ " + FormatDate(date) + " " + FormatTime(date);
}
else
{
dateString = " @ " + dateString;
}
ins_rssTitle.text = rssFeedXMLData.channel.title + dateString;
ins_rssContent.text = " "
+ cleanDescription(rssFeedXMLData.channel["item"][currentFeedIndex].title) + " - "
+ cleanDescription(rssFeedXMLData.channel["item"][currentFeedIndex].description);
gettingNewFeed = false;
}
else
{
ins_rssContent.text = "";
gettingNewFeed = false;
}
}
// removes the things from the RSS content that I don't want.
function cleanDescription(p_string:String):String
{
if (p_string == null)
{
return '';
}
var str:String = p_string.replace(/<\/?[^>]+>/igm, ''); //remove html tags.
str = str.split("\r").join(". "); //replace line breaks with '. '
str = str.replace("\t"," "); //replace tabs with a single space.
str = str.replace(/(?:\.\s){2,}/g,". "); //replace multiple '. ' with a single one.
str = str.replace(/((https?|ftp|gopher|telnet|file|notes|ms-help):
((\/\/)|(\\\\))+[\w\d:#@%\/;$()~_?\+-=\\\.&]*)/igm, ' '); //replaces URL text with a single space
str = str.replace(/#[\w]{3,}/igm,' ') //replaces hash tags with a single space
str = str.replace(/\s{2,}/igm," "); //replace multiple spaces with a single space
return str;
}
function ioRSSListXMLLoaderErrorHandler(e:Event):void
{
trace("ioRSSListXMLLoaderErrorHandler:");
trace(e.toString());
}
// the idea below is that it sets the text to "" and sets gettingNewFeed to false, which should get the
// rssTickerTMR:Timer to try to get a band new feed. Theoretically, it should try and fail over and again
// until the network is once again accessible. This doesn't work at all.
function ioRSSFeedXMLLoaderErrorHandler(e:Event):void
{
trace("ioRSSFeedXMLLoaderErrorHandler:");
trace(e.toString());
ins_rssContent.text = "";
gettingNewFeed = false;
}
Кто-нибудь знает лучший способ подойти к этому? Буду признателен за любую помощь, которую вы все сможете предложить. Спасибо!
РЕДАКТИРОВАТЬ!!!
Как предположил Уилл, я попытался создать подсистему мониторинга сети на основе таймера. В обработчике IO_ERROR на моем тикере RSS я останавливаю таймер RSS и запускаю таймер мониторинга сети. Каждые 10 секунд он пытается загрузить файл с сервера. Он продолжает работать до тех пор, пока не сработает, после чего отключит собственный таймер, перезапустит таймер RSS.
Должен работать в теории. Мало того, но это работает, если я переключаю его на загрузку веб-сайта вместо сетевого файла. При использовании сетевого файла, после сбоя, он продолжает отказывать, даже после восстановления соединения. Вот мой код:
...
function ioRSSFeedXMLLoaderErrorHandler(e:Event):void
{
rssTickerTMR.stop();
networkMonitorTMR.start();
trace("ioRSSFeedXMLLoaderErrorHandler:");
trace(e.toString());
ins_rssTitle.text = "Waiting for connection...";
ins_rssContent.text = "";
}
// netowrk monitor
var networkMonitorXMLLoader:URLLoader = new URLLoader();
networkMonitorXMLLoader.addEventListener(Event.COMPLETE, networkMonitorXMLLLoaded);
networkMonitorXMLLoader.addEventListener(IOErrorEvent.IO_ERROR, networkMonitorXMLLoaderErrorHandler);
var networkMonitorTMR:Timer = new Timer(10000,0);
networkMonitorTMR.addEventListener(TimerEvent.TIMER, checkNetwork);
function checkNetwork(e:Event = null):void
{
networkMonitorXMLLoader.load(new URLRequest("//server/share/file.xml"));
}
function networkMonitorXMLLoaderErrorHandler(e:Event = null) :void
{
trace("Still Off");
}
function networkMonitorXMLLLoaded(e:Event = null) :void
{
trace("Back On");
rssTickerTMR.start();
networkMonitorTMR.stop();
}
Как я уже сказал, если я переключаю "//server/shar/file.xml" на http://www.google.ca/, это работает. Когда я отключаю Ethernet, сетевой монитор запускается, когда я подключаю его обратно, он переключается обратно на RSS... но с моим сетевым ресурсом он просто продолжает отказывать...
1 ответ
Обычным способом решения подобных проблем является использование метода тайм-аута, который продолжает проверять соединение до тех пор, пока оно не будет восстановлено. В идеале вы должны делать это в фоновом режиме, чтобы пользователь не заметил. В вашем случае, если не удается установить соединение (IO_ERROR), установите таймер на метод, который повторяет попытку загрузки файла XML через определенный промежуток времени. Если это не удается, установите его снова. В то же время вы можете снова показывать свои текущие элементы с самого начала, обновляя их после загрузки нового канала.