2013-05-06 201 views
0

我正在構建託管的WCF服務,允許客戶端通過HTTP上傳文件。該服務按塊讀取客戶端的Stream塊。這適用於只需要一次迭代的小文件。但是當上傳大塊文件後,我得到IOExceptionAn exception has been thrown when reading the stream.Stream.EndRead()通過HTTP通過WCF流​​式傳輸文件上傳IOException

內部異常是The I/O operation has been aborted because of either a thread exit or an application request.

讀塊的數量不同,但我真的不能弄清楚,是什麼原因導致的差異。它的工作時間從300ms到550ms不等,大約1MB到大約2MB處理。

有沒有人有線索?

的接口被定義如下:

[ServiceContract] 
    public interface IServiceFileserver 
    { 
    [OperationContract] 
    UploadResponse UploadFile(UploadRequest uploadRequest); 

    // All status feedback related code is left out for simplicity 
    // [OperationContract] 
    // RunningTaskStatus GetProgress(Guid taskId); 
    } 

    [MessageContract] 
    public class UploadRequest 
    { 
    [MessageHeader()] 
    public string FileName { get; set; } 

    [MessageHeader()] 
    public long SizeInByte { get; set; } 

    [MessageBodyMember(Order = 1)] 
    public Stream Stream { get; set; } 
    } 

    [MessageContract] 
    public class UploadResponse 
    { 
    [MessageBodyMember()] 
    public Guid TaskId { get; set; } 
    } 

這裏是服務實現:

const int bufferSize = 4 * 1024; 
// This is called from the client side 
public UploadResponse UploadFile(UploadRequest uploadRequest) 
{ 
    Guid taskId = Guid.NewGuid(); 
    Stream stream = null; 
    try 
    { 
    stream = uploadRequest.Stream; 
    string filename = uploadRequest.FileName; 
    long sizeInBytes = uploadRequest.SizeInByte; 
    byte[] buffer = new byte[bufferSize]; 

    stream.BeginRead(buffer, 0, bufferSize, ReadAsyncCallback, new AsyncHelper(buffer, stream, sizeInBytes)); 
    } 
    catch (Exception ex) 
    { 
    if (stream != null) 
     stream.Close(); 
    } 
    return new UploadResponse() { TaskId = taskId }; 
} 

// Helper class for the async reading 
public class AsyncHelper 
{ 
    public Byte[] ByteArray { get; set; } 
    public Stream SourceStream { get; set; } 
    public long TotalSizeInBytes { get; set; } 
    public long BytesRead { get; set; } 

    public AsyncHelper(Byte[] array, Stream sourceStream, long totalSizeInBytes) 
    { 
    this.ByteArray = array; 
    this.SourceStream = sourceStream; 
    this.TotalSizeInBytes = totalSizeInBytes; 
    this.BytesRead = 0; 
    } 
} 

// Internal reading of a chunk from the stream 
private void ReadAsyncCallback(IAsyncResult ar) 
{ 
    AsyncHelper info = ar.AsyncState as AsyncHelper; 
    int amountRead = 0; 
    try 
    { 
    amountRead = info.SourceStream.EndRead(ar); 
    } 
    catch (IOException ex) 
    { 
    Trace.WriteLine(ex.Message); 
    info.SourceStream.Close(); 
    return; 
    } 

    // Do something with the stream 
    info.BytesRead += amountRead; 
    Trace.WriteLine("info.BytesRead: " + info.BytesRead); 

    if (info.SourceStream.Position < info.TotalSizeInBytes) 
    { 
    try 
    { // Read next chunk from stream 
     info.SourceStream.BeginRead(info.ByteArray, 0, info.ByteArray.Length, ReadAsyncCallback, info); 
    } 
    catch (IOException ex) 
    { 
     info.SourceStream.Close(); 
    } 
    } 
    else 
    { 
    info.SourceStream.Close();  
    } 
} 

綁定是這樣定義:

BasicHttpBinding binding = new BasicHttpBinding(); 
binding.TransferMode = TransferMode.Streamed; 
binding.MessageEncoding = WSMessageEncoding.Mtom; 
binding.MaxReceivedMessageSize = 3 * 1024 * 1024; 
binding.MaxBufferSize = 64 * 1024; 
binding.CloseTimeout = new TimeSpan(0, 1, 0); 
binding.OpenTimeout = new TimeSpan(0, 1, 0); 
binding.ReceiveTimeout = new TimeSpan(0, 10, 0); 
binding.SendTimeout = new TimeSpan(0, 1, 0); 
binding.Security.Mode = BasicHttpSecurityMode.None; 
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; 
+0

在流完全處理之前,您正從'UploadFile'返回。你爲什麼期望這個工作?這是返回的函數和正在處理的流之間的競爭。 – usr 2013-05-06 20:07:44

+0

@usr:不,我在處理異步流時返回後臺任務的ID。使用該ID,客戶可以向服務詢問任務的進度。但爲了簡單起見,我在代碼中省略了這部分。 – gumo 2013-05-07 06:47:30

+0

「uploadRequest.Stream」是WCF提供的流嗎?一旦您的請求處理方法返回,WCF將關閉該流。您的異步處理將會非確定性地失敗。情況會是這樣嗎? – usr 2013-05-07 11:08:36

回答

2

uploadRequest.StreamStream那由WCF提供。此流可能饋送WCF與您的服務的客戶端維護的底層TCP連接。

此流爲而不是您的服務客戶端已經通過的同一對象實例。這是不可能的,因爲客戶端和服務器只通過TCP連接。它們不共享相同的地址空間,因此它們不能共享對象實例。

在流完全處理之前,您從UploadFile返回。 WCF無法知道您的後臺線程仍在使用此Stream對象。因此,WCF釋放流的底層資源(可能會關閉到客戶端的TCP連接)。

一旦您的請求處理方法返回,WCF將關閉該流。您的異步處理將會非確定性地失敗。這是使用流和WCF賽車關閉它之間的線程競賽。

該問題下的評論顯示,某處存在誤解,但我不完全確定它是什麼。如果您需要進一步澄清,請留下評論並說出您不同意的原因以及原因。