0

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

最初,这些方法在 Wcf 上工作时遇到了问题,因为httpTransport默认使用缓冲传输模式,因此不支持将Stream对象作为参数或返回值。在将传输配置更改为使用StreamedResponse(或Streamed,即StreamedResponse加号StreamedRequest)后,我们在每次首次调用服务时都会遇到异常(例如在编译服务提供程序或重新启动 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:

我刚刚发现这篇文章似乎与我遇到的问题相同。作者指出:

我忘了提到每次启动服务器时只会发生一次。

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

4

1 回答 1

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);
    }
}

然后,通过创建自定义扩展器元素,我可以在配置中对其进行全局配置:

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所有端点的模式下为什么会出现问题。

于 2013-11-06T20:08:25.217 回答