流式传输的WCF服务失败

3

当我尝试从服务下载文件时,我的wcf客户端给了我以下错误:

"在接收http://mypc-pc/xmlLoadService/Service.svc的HTTP响应时发生错误。这可能是由于服务端点绑定未使用HTTP协议引起的。这也可能是由于服务器中止了HTTP请求上下文(可能是由于服务关闭)。请参阅服务器日志以获取更多详细信息。"

正在执行的代码是:

 System.IO.FileStream fs;
        try
        {
            fs = (System.IO.FileStream)client.Download(@"C:\UploadedFiles\Test.txt");
            byte[] arr = new byte[fs.Length];
            int read;
            do
            {
                read = fs.Read(arr, 0, arr.Length);

            } while (read != arr.Length);

            Console.WriteLine(ASCIIEncoding.ASCII.GetString(arr));
            Console.ReadLine();
        }

这是我的 web.config 配置:

<bindings>
   <basicHttpBinding>
       <binding name="basicHTTP" receiveTimeout="00:10:00" 
                sendTimeout="00:03:00" closeTimeout="00:10:00" 
                openTimeout="00:03:00" messageEncoding="Mtom" 
                maxBufferSize="100000" maxReceivedMessageSize="100000" 
                transferMode="Buffered">
       </binding>
   </basicHttpBinding>
</bindings>
<services>
   <service behaviorConfiguration="ServiceBehavior" name="Service">
       <endpoint address="" binding="wsHttpBinding" contract="IService">
           <identity>
               <dns value="localhost" />
           </identity>
       </endpoint>
       <endpoint address="mex" binding="mexHttpBinding" 
                 contract="IMetadataExchange" />
   </service>
   <service behaviorConfiguration="mexBehavior" 
            name="LoadXMLService.XMLOperations">
        <endpoint address="" 
                  binding="basicHttpBinding" 
                  bindingConfiguration="basicHTTP" 
                  behaviorConfiguration="" 
                  contract="LoadXMLService.IxmlLoad" />
   </service>
</services>

当服务器调用“下载”功能时,它会失败。
更新:从跟踪日志中我了解到,在服务器端没有打开文件。
这是应该打开文件的代码,逐步执行时不会抛出任何异常:
  public Stream Download( string path )
    {
        try
        {
            using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
            {

                return stream;
            }
        }
        catch (Exception ex)
        {
            string error = ex.Message;

            return null;
        }
    }

这个文件是一个文本文件,里面只有一行内容。以下是跟踪日志:
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>524340</EventID>
<Type>3</Type>
<SubType Name="Error">0</SubType>
<Level>2</Level>
<TimeCreated SystemTime="2009-10-04T15:20:22.7770000Z" />
<Source Name="System.ServiceModel" />
<Correlation ActivityID="{cb13d023-8122-4199-896c-78684b2461db}" />
<Execution ProcessName="WebDev.WebServer" ProcessID="8104" ThreadID="8" />
<Channel />
<Computer>TONY-PC</Computer>
</System>
<ApplicationData>
<TraceData>
<DataItem>
<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error">
<TraceIdentifier>http://msdn.microsoft.com/en-GB/library/System.ServiceModel.ServiceOperationExceptionOnReply.aspx</TraceIdentifier>
<Description>Replying to an operation threw a exception.</Description>
<AppDomain>b986c3da-11-128991431145070000</AppDomain>
<Source>System.ServiceModel.Dispatcher.ImmutableDispatchRuntime/48562646</Source>
<Exception>
<ExceptionType>System.ObjectDisposedException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>Cannot access a closed file.</Message>
<StackTrace>
at System.IO.__Error.FileNotOpen()
at System.IO.FileStream.Read(Byte[] array, Int32 offset, Int32 count)
at System.Xml.XmlMtomWriter.WriteXOPBinaryParts()
at System.Xml.XmlMtomWriter.WriteEndElement()
at System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer)
at System.ServiceModel.Channels.MtomMessageEncoder.WriteMessage(Message message, Stream stream, String startInfo, String boundary, String startUri, Boolean writeMessageHeaders)
at System.ServiceModel.Channels.MtomMessageEncoder.WriteMessage(Message message, Stream stream, String boundary)
at System.ServiceModel.Channels.HttpOutput.WriteStreamedMessage(TimeSpan timeout)
at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
at System.ServiceModel.Channels.HttpRequestContext.OnReply(Message message, TimeSpan timeout)
at System.ServiceModel.Activation.HostedHttpContext.OnReply(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.RequestContextBase.Reply(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.RequestContextBase.Reply(Message message)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Reply(MessageRpc&amp; rpc)
</StackTrace>
<ExceptionString>System.ObjectDisposedException: Cannot access a closed file.
   at System.IO.__Error.FileNotOpen()
   at System.IO.FileStream.Read(Byte[] array, Int32 offset, Int32 count)
   at System.Xml.XmlMtomWriter.WriteXOPBinaryParts()
   at System.Xml.XmlMtomWriter.WriteEndElement()
   at System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer)
   at System.ServiceModel.Channels.Message.WriteMessage(XmlDictionaryWriter writer)
   at System.ServiceModel.Channels.MtomMessageEncoder.WriteMessage(Message message, Stream stream, String startInfo, String boundary, String startUri, Boolean writeMessageHeaders)
   at System.ServiceModel.Channels.MtomMessageEncoder.WriteMessage(Message message, Stream stream, String boundary)
   at System.ServiceModel.Channels.HttpOutput.WriteStreamedMessage(TimeSpan timeout)
   at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
   at System.ServiceModel.Channels.HttpRequestContext.OnReply(Message message, TimeSpan timeout)
   at System.ServiceModel.Activation.HostedHttpContext.OnReply(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.RequestContextBase.Reply(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.RequestContextBase.Reply(Message message)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Reply(MessageRpc&amp; rpc)</ExceptionString>
</Exception>
</TraceRecord>
</DataItem>
</TraceData>
<System.Diagnostics xmlns="http://schemas.microsoft.com/2004/08/System.Diagnostics">
<LogicalOperationStack></LogicalOperationStack>
<Timestamp>921505170062</Timestamp>
<Callstack>
at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at System.Diagnostics.TraceEventCache.get_Callstack()
at System.Diagnostics.XmlWriterTraceListener.WriteFooter(TraceEventCache eventCache)
at System.Diagnostics.XmlWriterTraceListener.TraceData(TraceEventCache eventCache, String source, TraceEventType eventType, Int32 id, Object data)
at System.Diagnostics.TraceSource.TraceData(TraceEventType eventType, Int32 id, Object data)
at System.ServiceModel.Diagnostics.DiagnosticTrace.TraceEvent(TraceEventType type, TraceCode code, String description, TraceRecord trace, Exception exception, Object source)
at System.ServiceModel.Diagnostics.TraceUtility.TraceEvent(TraceEventType severity, TraceCode traceCode, TraceRecord extendedData, Object source, Exception exception)
at System.ServiceModel.Diagnostics.TraceUtility.TraceEvent(TraceEventType severity, TraceCode traceCode, Object source, Exception exception)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Reply(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessageCleanup(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch(MessageRpc& rpc, Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result)
at System.ServiceModel.Diagnostics.Utility.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
at System.ServiceModel.AsyncResult.Complete(Boolean completedSynchronously)
at System.ServiceModel.Channels.InputQueue`1.AsyncQueueReader.Set(Item item)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.InputQueue`1.EnqueueAndDispatch(T item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.InputQueueChannel`1.EnqueueAndDispatch(TDisposable item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, ItemDequeuedCallback dequeuedCallback, Boolean canDispatchOnThisThread)
at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, ItemDequeuedCallback callback)
at System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result)
at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest()
at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest()
at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object state)
at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()
at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()
at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()
at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(Object state)
at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</Callstack>
</System.Diagnostics>
</ApplicationData>
</E2ETraceEvent>

@Tony:如果你想在客户端读取文件,你需要设置 transferMode=StreamedResponse 以获取一个流返回。 - marc_s
啊,是的,好的 - 你不能把服务器代码放到 using() { .... } 块中 - 这会在块结束时再次关闭流! - marc_s
非常感谢!现在它可以工作了...我想我最好再学习一下WCF的工作原理。 - Tony The Lion
1个回答

4

由于您似乎正在使用流式响应 - 您的配置是什么样子的?您的客户端配置中是否有类似以下内容的设置?

  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="FileTransferBinding"
                  transferMode="StreamedResponse" />
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint name="DefaultEndpoint"
                address="....."
                binding="basicHttpBinding"
                bindingConfiguration="FileTransferBinding"
                contract="IYourContract" />
    </client>
  </system.serviceModel>
  • transferMode=StreamedtransferMode=StreamResponseBindingConfiguration
  • 一个客户端终结点,其binding=basicHttpBinding,且 bindingConfiguration=FileTransferBinding(其中绑定配置的名称与绑定配置声明中的上述名称相同)

这是你应该拥有的至少最基本的配置 - 但如果没有提供这个至关重要的信息,那么就只是瞎猜......

更新: 你遇到的问题很可能是你的绑定配置中没有transferMode="StreamResponse"设置。只有设置了这个参数,你才能够从服务调用的响应中读取一个!

更新2: 如果你仍然遇到问题,我强烈建议你配置并打开WCF跟踪 - 将以下片段添加到你的客户端配置中:

<configuration>
   <system.diagnostics>
      <sources>
            <source name="System.ServiceModel" 
                    switchValue="Information, ActivityTracing"
                    propagateActivity="true">
            <listeners>
               <add name="traceListener" 
                   type="System.Diagnostics.XmlWriterTraceListener" 
                   initializeData= "c:\log\Traces.svclog" />
            </listeners>
         </source>
      </sources>
   </system.diagnostics>
</configuration>

它将会把跟踪信息写入文件C:\log\traces.svclog(确保目录存在),您可以使用Service Trace Viewer (C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\中的SvcTraceViewer.exe)来检查这些信息。
有关如何配置和设置WCF跟踪的更多信息,请查看MSDN文档更新3:由于您想要实际返回流(而不是在服务器上关闭它),因此您不应该将流代码放入using() {...}块中!
请尝试使用以下代码:
public Stream Download( string path )
{
    try
    {
        FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
        return stream;
    }
    catch (Exception ex)
    {
        string error = ex.Message;
        return null;
    }
}

你需要在服务器上打开流并保持其处于打开状态,然后将其返回。客户端需要确保在读取完毕后关闭它。通常情况下,将该代码放在using() {....}块中是最佳实践,但在“服务器打开流并将其返回给客户端”的情况下无法使用。
Marc

我为你发布了我的web.config配置。希望这能有所帮助。 - Tony The Lion
所以我将配置更改为StreamedResponse,现在我收到了这个错误:“在通过HTTP通道传输数据时发生错误(无法从传输连接读取数据:连接已关闭。)”这可能与通道上的超时有关吗? - Tony The Lion
你应该查看WCF跟踪,并让其将错误消息跟踪到文件中并进行检查! - marc_s
谢谢Marc。我现在有一个跟踪日志。我将查找错误的事件ID,它是:131075。 - Tony The Lion
+2. 我已经养成了使用using(){}的习惯,结果遇到了这个问题。傻瓜;如果服务器正在流式传输结果,则在响应发送之前无法处理它。非常感谢第三次更新。 - Brad Christie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接