Wcf通过http进行流式传输会在第一次调用时导致异常

时间:2013-10-28 14:03:39

标签: .net wcf http iis streaming

我们正在将.Net Remoting代码库转换为Wcf。我们之前使用的一小部分方法使用客户端和服务器之间的Stream实例来传输大文件等。客户端和服务器都是IIS中托管的Web应用程序。

最初,我们在Wcf上使用这些方法时遇到问题,因为httpTransport默认使用buffered传输模式,因此不支持Stream个对象作为参数或返回值。将传输配置更改为使用StreamedResponse(或StreamedStreamedResponseStreamedRequest)后,我们会在每次首次调用服务时遇到异常(例如,编译服务提供程序,或重新启动IIS):

The input source is not correctly formatted. 

[XmlException: The input source is not correctly formatted.]
   System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3) +815516
   System.Xml.XmlExceptionHelper.ThrowInvalidBinaryFormat(XmlDictionaryReader reader) +34
   System.Xml.XmlBinaryReader.ReadNode() +2367693
   System.ServiceModel.Channels.Message.ReadFromBodyContentsToEnd(XmlDictionaryReader reader, EnvelopeVersion envelopeVersion) +65
   System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest) +319
   System.ServiceModel.Dispatcher.OperationFormatter.DeserializeReply(Message message, Object[] parameters) +741

[CommunicationException: Error in deserializing body of reply message for operation '[MyMethod]'. The input source is not correctly formatted.]
   [webforms page 'Page_Load' stacktrace]

我们目前正在使用自定义绑定堆栈(通过http启用二进制编码),配置如下:

  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding>
          <binaryMessageEncoding/>
          <httpTransport transferMode="StreamedResponse" maxReceivedMessageSize="2147483647"/>
        </binding>
      </customBinding>
    </bindings>
    ...
  </system.serviceModel>

如何才能找到仅在主机Web应用程序需要启动时才会发生这种情况的原因?当我在此错误后立即刷新页面时,所有其他页面的所有内容似乎都能正常工作。也许它与某种程度上是超时相关的,因为在重新编译/池循环/ webconfig更改/等之后唤醒服务器需要时间?

更新

我测试了将所有超时增加到客户端和服务器绑定上的默认值,如下所示:

<binding closeTimeout="10:00:00" 
         openTimeout="10:00:00" 
         receiveTimeout="10:00:00" 
         sendTimeout="10:00:00">
  <binaryMessageEncoding />
  <httpTransport transferMode="Streamed" maxReceivedMessageSize="2147483647" />
</binding>

但似乎没有任何改变,因为在服务器更新后,我仍然在第一次访问时遇到相同的异常。

更新2:

我刚刚发现this post这似乎与我遇到的问题相同。作者说:

  

我忘了提到这种情况每次只发生一次   服务器已启动。

他的问题似乎是客户端和服务器端点堆栈之间的不匹配,但这不是我的情况。

1 个答案:

答案 0 :(得分:0)

进一步自定义环境后,此错误停止发生。

我所做的是从配置文件中的传输组件中删除参数。

然后我创建了一个自定义行为,根据它的契约接口动态地将它添加到端点:如果契约有任何返回或接收Stream对象的方法,我检查它的端点绑定并添加Streamed模式到绑定的httpTransport组件,如下所示:

public sealed class DynamicStreamingEndpointBehavior : IEndpointBehavior
{
    private bool m_contractHasStream;

    void IEndpointBehavior.Validate(ServiceEndpoint _endpoint)
    {
        IEnumerable<MethodInfo> streamMethods =
            from method in _endpoint.Contract.ContractType.GetMethods()
            where method.GetCustomAttribute<OperationContractAttribute>() != null && (
                typeof (Stream).IsAssignableFrom(method.ReturnType) ||
                method.GetParameters()
                    .Select(_parameter => _parameter.ParameterType)
                    .Any(_type => typeof (Stream).IsAssignableFrom(_type)))
            select method;

        m_contractHasStream = streamMethods.Any();
    }

    void IEndpointBehavior.AddBindingParameters(ServiceEndpoint _endpoint,
                                                BindingParameterCollection _bindingParameters) {}

    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint _endpoint, EndpointDispatcher _endpointDispatcher)
    {
        if (!m_contractHasStream) return;

        BindingElementCollection bindingElements = _endpoint.Binding.CreateBindingElements();
        bindingElements.OfType<HttpTransportBindingElement>().Single().TransferMode =
            TransferMode.Streamed;
        _endpoint.Binding = new CustomBinding(bindingElements);
    }

    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint _endpoint, ClientRuntime _clientRuntime)
    {
        if (!m_contractHasStream) return;

        BindingElementCollection bindingElements = _endpoint.Binding.CreateBindingElements();
        bindingElements.OfType<HttpTransportBindingElement>().Single().TransferMode =
            TransferMode.Streamed;
        _endpoint.Binding = new CustomBinding(bindingElements);
    }
}

然后,通过创建自定义扩展程序元素,我可以在config中全局配置它:

public sealed class DynamicStreamingBehaviorElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof (DynamicStreamingEndpointBehavior); }
    }

    protected override object CreateBehavior()
    {
        return new DynamicStreamingEndpointBehavior();
    }
}

以下是最终配置:

  <system.serviceModel>
    <extensions>
      <behaviorExtensions>
        ...
        <add name="dynamicStreaming" 
             type="[namespace].DynamicStreamingBehaviorElement, [assembly]"/>
        ...
      </behaviorExtensions>
    </extensions>
    ...
    <behaviors>
      <endpointBehaviors>
        <behavior>
          ...
          <dynamicStreaming />
          ...
        </behavior>
      </endpointBehaviors>
      ...
    </behaviors>
  </system.serviceModel>

如果我将Streamed模式保留在所有端点上,我仍然不确定为什么会出现问题。