【问题标题】:WCF Service TcpNetBinding StreamRequest not receiving streamWCF 服务 TcpNetBinding StreamRequest 未接收流
【发布时间】:2011-04-26 12:55:47
【问题描述】:

我的第一个问题,所以要温柔=)

以下是VS2010中的所有.Net 4、VB.Net。最终目标是通过 Tcp 绑定从客户端接收(完整)流到 IIS 托管 WCF 服务。我面临的问题是该服务无法从提供的流中读取任何字节。现在有了细节......为简洁起见,我删除了相当多的内容,但是如果我遗漏了重要的内容,请告诉我。

服务合同如下:

<ServiceContract(Namespace:="ImageSystem")> _
Public Interface IUploadService
    <OperationContract()> _
    Function UploadFile(ByVal file As ImageUpload) As ImageUpload
End Interface

数据合约ImageUpload如下:

<MessageContract()> _
Public Class ImageUpload

#Region " Message Header "

    Private _ImageID As Nullable(Of Long)
    <MessageHeader()> _
    Public Property ImageID() As Nullable(Of Long)
        Get
            Return _ImageID
        End Get
        Set(ByVal value As Nullable(Of Long))
            _ImageID = value
        End Set
    End Property

    '... a few other value type properties

#End Region

#Region " Message Body"
    ' Do not add any more members to the message body or streaming support will be disabled!

    <MessageBodyMember()> _
    Public Data As System.IO.Stream

#End Region

End Class

相关的服务器配置/绑定如下(这些显然只是开发环境设置):

<system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="netTcpStreamBinding" transferMode="Streamed" maxBufferSize="20971520" maxReceivedMessageSize="20971520"/>
      </netTcpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="UploadServiceBehaviour"
        name="ImageSystem.SVC.UploadService">
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="netTcpStreamBinding"
          contract="ImageSystem.SVC.IUploadService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:809/UploadService" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="UploadServiceBehaviour">
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

WCF 服务是一个服务库,由 Web 应用程序托管。 Web 应用程序项目在我的本地 IIS 7.5 中运行。 IIS 已配置为启用 TCP 连接,并为应用程序池标识配置了合约实现的相关权限。 VS2010 以管理员身份运行以在 IIS 中启用调试。

为了测试合同实施,我将 Windows 控制台应用程序设置为(测试)客户端。客户端代理类是通过在 IIS 主机 (http://localhost/ImageSystem/UploadService.svc) 中添加对服务的服务引用来生成的。服务引用被配置为生成异步方法。

相关的自动生成的客户端配置如下(注意,我尝试增加maxBufferPoolSize、maxBufferSize和maxReceivedMessageSize来匹配“20971520”的服务器配置,但无济于事):

[编辑:根据 Sixto Saez 的建议,reliableSessions 部分已注释掉,但无济于事]

<system.serviceModel>

    <bindings>
        <binding name="NetTcpBinding_IUploadService" closeTimeout="00:01:00"
          openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
          transactionFlow="false" transferMode="Streamed" transactionProtocol="OleTransactions"
          hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="20971520"
          maxBufferSize="20971520" maxConnections="10" maxReceivedMessageSize="20971520">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <!--<reliableSession ordered="true" inactivityTimeout="00:10:00"
            enabled="false" />-->
          <security mode="None">
            <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
            <message clientCredentialType="Windows" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>

    <client>
      <endpoint address="net.tcp://mycomputername.mydomain/ImageSystem/UploadService.svc"
        binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IUploadService"
        contract="UploadService.Local.IUploadService" name="NetTcpBinding_IUploadService">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
    </client>

  </system.serviceModel>

客户端使用情况如下:

Public Sub Test()
    Dim serviceClient As UploadService.Local.UploadServiceClient = New UploadService.Local.UploadServiceClient
    AddHandler serviceClient.UploadFileCompleted, AddressOf LocalTestCallback
    Dim ms As MemoryStream = New MemoryStream
    My.Resources.Penguins.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
    serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "MYDOMAIN"
    serviceClient.ClientCredentials.Windows.ClientCredential.UserName = "User"
    serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "Password123"
    serviceClient.UploadFileAsync(Nothing, ..., ms, ms) '"..." is obviously not actually here, other values omitted. "ms" is passed as UserState object in addition to fulfilling the 'Data' parameter
End Sub

如果您想知道(或很重要),企鹅图像是 Windows 7 提供的示例图片目录中的图像。图像为 777,835 字节(应在相关请求/缓冲区的最大大小范围内)。

我尝试了两种方法来读取服务器端的图像。

方法一:

Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile

    Dim uploadBuffer(Helper.Settings.AppSettings(Of Integer)("UploadBufferSize", True) - 1) As Byte
    Dim ms As MemoryStream = New MemoryStream()
    Dim bytesRead As Integer
    Do
        bytesRead = file.Data.Read(uploadBuffer, 0, uploadBuffer.Length)
        ms.Write(uploadBuffer, 0, bytesRead)
    Loop Until bytesRead = 0

End Function

方法二:

Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile

    Dim reader As StreamReader = New StreamReader(file.Data)
    Dim imageB64 As String = reader.ReadToEnd
    ms = New MemoryStream(Convert.FromBase64String(imageB64))

End Function

在这两种情况下,ms.Length = 0。更清楚的是,在第二种方法中,imageB64 = ""(空字符串)。

为什么我没有从流中收到任何内容?另外,作为一个偷偷摸摸的子问题,为什么生成的代理类不提供接受ImageUpload类型对象的重载?

提前谢谢你!!

【问题讨论】:

  • 有什么办法可以解决这个问题吗?只有 26 次观看,还没有答案。 =(

标签: vb.net wcf visual-studio-2010 .net-4.0 stream


【解决方案1】:

您会遇到您提到的问题对我来说似乎很奇怪,所以我很好奇并使用您的服务合同整理了一个实现。那一个实际上立即起作用了。我实际上不知道您的情况出了什么问题(这并不明显),但让我在这里发布一个可行的解决方案,希望这能帮助您解决问题。

很遗憾,由于多年前我放弃了 VB,我只能提供 C# 代码。希望没关系。

服务器 Web.config(在 IIS 中测试,使用 net.tcp 绑定):

  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding transferMode="Streamed" maxReceivedMessageSize="1000000">
          <security mode="None"/>
        </binding>
      </netTcpBinding>
    </bindings>
    <services>
      <service name="ImageSystem.SVC.UploadService">
        <endpoint address="" binding="netTcpBinding" contract="ImageSystem.SVC.IUploadService">
        </endpoint>
        <endpoint address="mex" kind="mexEndpoint" binding="mexTcpBinding"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

客户端 app.config(控制台测试应用):

<system.serviceModel>
    <bindings>
        <netTcpBinding>
            <binding transferMode="Streamed" maxReceivedMessageSize="1000000">
                <security mode="None"/>
            </binding>
        </netTcpBinding>
    </bindings>
    <client>
        <endpoint 
            address="net.tcp://localhost/WcfService1/UploadService.svc" 
            binding="netTcpBinding" 
            contract="ImageServices.IUploadService" 
            name="NetTcpBinding_IUploadService">
        </endpoint>
    </client>
</system.serviceModel>

服务合同和实施:

[ServiceContract(Namespace="urn:ImageSystem")]
public interface IUploadService
{
    [OperationContract]
    ImageUpload UploadFile(ImageUpload file);
}

[MessageContract]
public class ImageUpload
{
    [MessageHeader]
    public long? ImageID { get; set; }
    [MessageBodyMember]
    public Stream Data;
}

public class UploadService : IUploadService
{

    public ImageUpload UploadFile(ImageUpload file)
    {
        long length;

        using (var ms = new MemoryStream())
        {
            file.Data.CopyTo(ms);
            length = ms.Length;
        }

        return new ImageUpload { ImageID = length, Data = new MemoryStream() };
    }
}

测试应用:

private static readonly string imgPath = @"C:\Pictures\somepicture.jpg";
private static readonly EventWaitHandle waitHandle = new AutoResetEvent(false);

static void Main()
{
    long? result;

    using (var service = new ImageServices.UploadServiceClient("NetTcpBinding_IUploadService"))
    {
        var image = new ImageServices.ImageUpload();
        using (var imgStream = File.OpenRead(imgPath))
        {
            image.Data = imgStream;
            service.UploadFileCompleted += (sender, e) => 
            { 
                result = e.Result;
                if (e.Data != null) image.Data.Dispose();
                waitHandle.Set();
            };

            service.UploadFileAsync(null, imgStream);

            waitHandle.WaitOne();
        }
    }
}

首先,如您所见,配置文件可以简单得多。尤其是大的BufferSize 值是不必要的。然后,关于服务合同,我不清楚为什么Upload 操作会收到并返回ImageUpload 消息。在我的实现中,我在ImageID 参数中返回上传的文件大小,当然只是为了演示目的。我不知道您签订该合同的原因是什么,以及您实际上想要回报什么。


实际上,当我知道您的代码可能失败的原因时,我正要点击“发送”。在您的测试客户端中,在调用 serviceClient.UploadFileAsync() 之前,将此行添加到您的代码中:ms.Seek(0, SeekOrigin.Begin)

这会将 MemoryStream 的位置重置回其开头。如果您不这样做,则 MemoryStream 将仅从其当前位置(即其结束)被消耗 - 这解释了在服务端接收到的流的 Length = 0!

【讨论】:

  • 你是我的英雄。我还没有完全阅读所有配置更改,当我阅读它时答案很明显......“ms.Seek(0,SeekOrigin.Begin)”。不敢相信我错过了。感谢您抽出宝贵的时间。服务器将 transferMode 设置为 Streamed 的原因是因为合约上还有其他几个操作,其中一些返回流,所以虽然没有一个操作同时接收和响应流,但整个合约确实如此。我使用的配置可能非常冗长,因为大部分是在 WCF 配置向导中完成的。 C# 很好
  • 在 24 小时期限结束之前,我不能授予您赏金。我会在 4 小时后回来查看以奖励它。干杯
【解决方案2】:

您可能已经有seen the information in this MSDN article,但您应该查看名为“流数据”的部分及其列出的限制。您的客户端配置显示了可靠会话元素,其 ordered 属性设置为“true”,不支持流式传输。

我不知道这是否是您问题的具体原因,但这是一个开始。该文章还很好地布置了流式传输所需的基本配置,因此您应确保您的配置符合其建议。

【讨论】:

  • 我看过那篇文章,但没有注意到客户端自动生成配置上的可靠会话。现在将尝试并让您知道!编辑:我删除了可靠会话部分,但没有这样的运气。如果我删除该部分,它会默认启用可靠会话吗?
  • 我会为你回答(尽管如果我错了请纠正我)...This article 说 netTcpBindings “支持可靠会话作为选项,但默认情况下不启用”。不过,感谢您花时间发现这一点。
  • 没问题,但要清楚一点,通过 netTcpBinding 流式传输不支持使用可靠会话。我引用的文章中的引言是“由于这些功能限制,您只能对流式传输使用传输级安全选项,并且您无法打开可靠会话”(强调)。
  • 是的,我记得读过同样的东西。在我的评论中,我试图仔细检查从配置中删除可靠会话部分是否会禁用可靠会话,而不是删除对它们的任何提及,但实际上由于某些潜在的默认值而使它们保持打开状态。再次感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-27
相关资源
最近更新 更多