0

我正在构建一个托管的 WCF 服务,允许客户端通过 HTTP 上传文件。服务逐块读取客户端的Stream块。这适用于只需要一次迭代的小文件。但是当在一些块之后上传更大的文件时,我IOExceptionAn exception has been thrown when reading the stream.Stream.EndRead().

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

读取块的数量各不相同,但我无法弄清楚是什么导致了这种差异。它的工作时间从 300 毫秒到 550 毫秒不等,处理时间从 ~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;
4

1 回答 1

3

uploadRequest.StreamStream由 WCF 提供的。此流可能以 WCF 与您的服务客户端维护的底层 TCP 连接为基础。

此流与您的服务客户端传入的对象实例不同。这是不可能的,因为客户端和服务器仅通过 TCP 连接它们不共享相同的地址空间,因此它们不能共享对象实例。

您正在从UploadFile流完全处理之前返回。WCF 无法知道您的后台线程仍在使用此 Stream 对象。因此,WCF 释放了流底层的资源(可能它关闭了与客户端的 TCP 连接)。

一旦您的请求处理方法返回,WCF 将关闭该流。然后,您的异步处理将不确定地失败。这是您使用流和 WCF 竞赛来关闭它之间的线程竞赛。

问题下的评论表明某处存在误解,但我不确定它是什么。如果您需要进一步澄清,请发表评论并说出您不同意的内容以及原因。

于 2013-05-09T13:26:59.013 回答