我构建了一个.NET Mono应用程序,它每分钟将文本文件上传到服务器。这个应用程序确实需要全天候运行,我用它来将.rss提要的数据传输到我们的图文电视服务器,以便在电视上传输。 在我看来,避免崩溃应用程序的最好方法是在上传代码周围使用try catch,以便捕获意外错误。
总是它正确地运行了大约2天,之后它突然崩溃了。我已经找了几个星期的原因,但找不到它。
我得到的错误是:
未处理的异常:System.Net.WebException:请求已中止 System.Net.FtpWebRequest.CheckIfAborted()[0x00000] in:0 at System.Net.FtpWebRequest.set_Sate(RequestState value) [0x00000] in:0 at System.Net.FtpWebRequest.ProcessRequest(RequestState value)[0x00000] in:at System.Threading.Thread.StartUnsafe() [0x00000] in:
上传代码在线程内运行,并且是:
private static readonly ILog log = LogManager.GetLogger(typeof(PheTextConnector));
static String ftp_path = ConfigurationManager.AppSettings["txtServerFTPPath"];
static String ip = ConfigurationManager.AppSettings["txtServerIP"];
static String username = ConfigurationManager.AppSettings["txtServerUsername"];
static String password = ConfigurationManager.AppSettings["txtServerPassword"];
public void UploadToTextServer(String filename, String uploadfolder)
{
try
{
UploadFile(ip, ftp_path, filename, username, password, uploadfolder);
}
catch(Exception ex)
{
log.Error(ex.ToString());
}
}
public void UploadFile(string FTPAddress, string directory, string filename, string username, string password, string uploadfolder)
{
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://" + FTPAddress + "/" + directory + "/" + Path.GetFileName(filename));
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(username, password);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = true;
FileStream fs = File.OpenRead(uploadfolder + "/" + filename);
int buffLength = 4096;
byte[] buffer = new byte[buffLength];
Stream strm = request.GetRequestStream();
int bytesRead = fs.Read(buffer, 0, buffer.Length);
while (true)
{
if (bytesRead == 0)
{
strm.Close();
fs.Close();
break;
}
else
{
strm.Write(buffer, 0, bytesRead);
bytesRead = fs.Read(buffer, 0, buffLength);
}
}
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
response.Close();
}
catch(Exception ex)
{
log.Error(ex.ToString());
}
}
}
调用上传方法的类的代码是:
using System;
using System.IO;
using System.Threading;
using log4net;
using System.Text.RegularExpressions;
using System.Configuration;
public class PhecapConnectionMain
{
private static readonly ILog log = LogManager.GetLogger(typeof(PhecapConnectionMain));
private Object thisLock = new Object();
private int waitTimeBetweenTxtUpdates = Int32.Parse(ConfigurationManager.AppSettings["txtServerTimeBetweenUpdates"]);
private String transferDirectory = ConfigurationManager.AppSettings["transferDirectoryName"];
public PhecapConnectionMain ()
{
try
{
log.Debug("Start Phecap Connection");
CreateDirectoryIfNotExists(transferDirectory);
FileSystemWatcher _watcher = new FileSystemWatcher();
_watcher.Path = transferDirectory+"/";
_watcher.NotifyFilter = System.IO.NotifyFilters.DirectoryName;
_watcher.NotifyFilter = _watcher.NotifyFilter | System.IO.NotifyFilters.FileName;
_watcher.NotifyFilter = _watcher.NotifyFilter | System.IO.NotifyFilters.Attributes;
_watcher.EnableRaisingEvents = true;
_watcher.IncludeSubdirectories = true;
_watcher.Created += new FileSystemEventHandler(eventRaised);
}
catch(Exception ex)
{
log.Error(ex.ToString());
}
}
/// <summary>
/// Triggered when an event is raised from the folder activity monitoring.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">containing all data send from the event that got executed.</param>
private void eventRaised (object sender, System.IO.FileSystemEventArgs e)
{
String changedFileName = e.Name;
changedFileName = Regex.Replace (changedFileName, @"//", @"//");
changedFileName = String.Format(@"{0}",changedFileName);
switch(e.ChangeType)
{
case WatcherChangeTypes.Created:
if(changedFileName.Contains("update.sem"))
{
Thread thread = new Thread(NewFolderCreatedThread);
thread.Start(e);
}
break;
default:
break;
}
}
/// <summary>
/// Creates a directory if not exists.
/// </summary>
/// <param name='folderName'>
/// Folder name.
/// </param>
private void CreateDirectoryIfNotExists(String folderName)
{
if(Directory.Exists(folderName) == false)
{
Directory.CreateDirectory(folderName);
}
}
/// <summary>
/// News the folder created thread.
/// </summary>
/// <param name='parameterObject'>
/// Parameter object.
/// </param>/
public void NewFolderCreatedThread(object parameterObject)
{
// Lock object, one thread at a time may upload files
lock(thisLock)
{
try
{
System.IO.FileSystemEventArgs fileInfo = (System.IO.FileSystemEventArgs) parameterObject;
String[] splitted = fileInfo.Name.Split('/');
String directory = transferDirectory+"/"+splitted[splitted.Length-2]+"/";
string [] fileEntries = Directory.GetFiles(directory);
PheTextConnector pheTextConnector = new PheTextConnector();
int percentage = 0;
double counter = 1;
double lengt = fileEntries.Length;
foreach(string fName in fileEntries)
{
String[] tempSplitted = fName.Split('/');
String fileName = tempSplitted[tempSplitted.Length-1];
if(!fileName.Equals("update.sem"))
{
pheTextConnector.UploadToTextServer(fileName, directory);
}
double percent = (counter / lengt) * 100.0;
percentage = (int) percent;
ConsoleFunctions.RenderConsoleProgress(percentage, '\u2590', ConsoleColor.Yellow, "Uploading file " + fileName);
counter++;
}
pheTextConnector.UploadToTextServer("update.sem", directory);
Directory.Delete(directory, true);
log.Debug("Files are uploaded to server, wait " + waitTimeBetweenTxtUpdates + " milliseconds for releasing lock");
Thread.Sleep(waitTimeBetweenTxtUpdates);
log.Debug("Waiting complete... release lock and allow new updates to be uploaded to the server");
}
catch(Exception ex)
{
log.Error(ex.ToString());
}
}
}
}
- 编辑:
我使用using语句改进了UploadMethod。并找到了通过将FTPWebRequest的超时值设置为100毫秒来重现此错误的方法。 而不是try catch捕获的超时错误,应用程序完全崩溃。
using System;
using System.IO;
using System.Net;
using System.Configuration;
using log4net;
public class PheTextConnector
{
private static readonly ILog log = LogManager.GetLogger(typeof(PheTextConnector));
static String ftp_path = ConfigurationManager.AppSettings["txtServerFTPPath"];
static String ip = ConfigurationManager.AppSettings["txtServerIP"];
static String username = ConfigurationManager.AppSettings["txtServerUsername"];
static String password = ConfigurationManager.AppSettings["txtServerPassword"];
public void UploadToTextServer(String filename, String uploadfolder)
{
try
{
UploadFile(ip, ftp_path, filename, username, password, uploadfolder);
GC.Collect();
GC.WaitForPendingFinalizers();
}
catch(Exception ex)
{
log.Error(ex.ToString());
}
}
public void UploadFile(string FTPAddress, string directory, string filename, string username, string password, string uploadfolder)
{
try
{
FtpWebRequest request = (FtpWebRequest) WebRequest.Create("ftp://" + FTPAddress + "/" + directory + "/" + Path.GetFileName(filename));
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(username, password);
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = true;
request.Timeout = 100;
byte[] buffer = new byte[4096];
using (FileStream fs = File.OpenRead(uploadfolder + "/" + filename))
{
using(Stream stream = request.GetRequestStream())
{
int bytesRead = fs.Read(buffer, 0, buffer.Length);
while (true)
{
if (bytesRead == 0)
{
stream.Close();
break;
}
else
{
stream.Write(buffer, 0, bytesRead);
bytesRead = fs.Read(buffer, 0, 4096);
}
}
}
}
using(FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
response.Close();
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
catch(Exception ex)
{
log.Error(ex.ToString());
}
}
}
System.Net.WebException: Request timed out
at System.Net.FtpWebRequest.EndGetRequestStream (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0
at System.Net.FtpWebRequest.GetRequestStream () [0x00000] in <filename unknown>:0
at PheTextConnector.UploadFile (System.String FTPAddress, System.String directory,
System.String filename, System.String username, System.String password, System.String
uploadfolder) [0x00094] in /svn/WOSTeletekst/WOSTxT/PheCapConnector
/PheTextConnector.cs:47
System.Net.WebException: Request aborted
at System.Net.FtpWebRequest.CheckIfAborted () [0x00000] in <filename unknown>:0
at System.Net.FtpWebRequest.set_State (RequestState value) [0x00000] in <filename unknown>:0
at System.Net.FtpWebRequest.UploadData () [0x00000] in <filename unknown>:0
at System.Net.FtpWebRequest.ProcessMethod () [0x00000] in <filename unknown>:0
at System.Net.FtpWebRequest.ProcessRequest () [0x00000] in <filename unknown>:0
答案 0 :(得分:1)
你的电视机可能没什么问题,我的意思是你的代码。
问题是Mono的FtpWebRequest实现不是很强大(在FTP会话的各个阶段处理不当)。我对类似的案例进行了分析并在那里发表了我的发现:
Mono for Android / MonoTouch System.Net.WebException: Request aborted
你的案例虽然不尽相同,但却指出了Mono的FtpWebRequest问题的常见“包”。
答案 1 :(得分:0)
这些错误很难找到。
你说你在一个线程中运行这个函数。所以我猜它是从while
循环而不是Thread.Sleep(1000*60);
或fire and forget
调用的。原因可能是,在下一次调用之前函数没有完成,或FtpWebRequest
仍未关闭,并且无关紧要,它在不同的上下文中运行。
我有类似的问题,你可以试试这里:
1)使FtpWebRequest request
全局并在函数调用之前检查它是否仍然存活。
2)在函数端调用垃圾收集器:
GC.Collect();
GC.WaitForPendingFinalizers();
如果有效,请告诉我。