【问题标题】:WCF - Abort stream from client to server if server throws a FaultExceptionWCF - 如果服务器抛出 FaultException,则中止从客户端到服务器的流
【发布时间】:2017-09-27 14:19:18
【问题描述】:

我们有一个 WCF 服务,它接受来自客户端的流(客户端将文件上传到服务器)。但是,如果服务器在流之前或期间抛出 FaultException,客户端只会继续流直到结束,此时它会从服务器接收到 FaultException - 浪费客户端的时间和带宽。

类似问题:

How to Abort a WCF File Upload from the Server-Side Method

采取以下(简化的WCF服务)

Namespace JP_WCF
    <ServiceContract> _
    Public Interface IJP_WCF
        <OperationContract> _
        <FaultContract(GetType(JP_WCF_Fault))> _
        Sub UploadFile(request As JP_WCF_FileUpload)

        <OperationContract> _
        <FaultContract(GetType(JP_WCF_Fault))> _
        Function fakeError(ByVal int1 As Integer, ByVal int2 As Integer) As Integer

        <OperationContract> _
        <FaultContract(GetType(JP_WCF_Fault))> _
        Function Ping() As Date
    End Interface

    <MessageContract> _
    Public Class JP_WCF_FileUpload
        Implements IDisposable

        <MessageHeader(MustUnderstand:=True)> _
        Public FileName As String

        <MessageHeader(MustUnderstand:=True)> _
        Public Length As Long

        <MessageBodyMember(Order:=1)> _
        Public FileByteStream As System.IO.Stream

        Public Sub Dispose() Implements IDisposable.Dispose
            If FileByteStream IsNot Nothing Then
                FileByteStream.Close()
                FileByteStream = Nothing
            End If
        End Sub
    End Class

    <DataContract> _
    Public Class JP_WCF_Fault
        <DataMember> _
        Public Property EventID() As Integer
        <DataMember> _
        Public Property Message() As String
        <DataMember> _
        Public Property Description() As String

        Public Sub New(ByVal _EventID As Integer, ByVal _Message As String, ByVal _Description As String)
            Me.EventID = _EventID
            Me.Message = _Message
            Me.Description = _Description
        End Sub
    End Class

End Namespace

示例服务器方法:

Try
    Dim sourceStream As Stream = request.FileByteStream
    Dim uploadFolder As String = "C:\upload\"
    Dim filePath As String = Path.Combine(uploadFolder, request.FileName)

    Using targetStream = New FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)
        sourceStream.CopyTo(targetStream)
        targetStream.Close()
        sourceStream.Close()
    End Using
Catch ex As Exception
    Throw New FaultException(Of JP_WCF_Fault)(New JP_WCF_Fault(8, ex.Message, ex.ToString), ex.Message)
End Try

示例客户端方法:

Dim fileInfo As New System.IO.FileInfo(filePath)
Dim startTime As DateTime = DateTime.Now
Console.WriteLine("Starting V2 upload: " + DateTime.Now.ToString())

Dim JPCS As New JP_WCFService.JP_WCFClient()

Using stream As New System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read)
    Using uploadStreamWithProgress As New JP_StreamWithProgress(stream)
        AddHandler uploadStreamWithProgress.ProgressChanged, AddressOf uploadStreamWithProgress_ProgressChanged
        Try
            JPCS.UploadFile(fileInfo.Name, fileInfo.Length, uploadStreamWithProgress)
        Catch ex As FaultException(Of JP_WCFService.JP_WCF_Fault)
            Console.WriteLine("Upload Error: " & ex.Detail.Message & " (EventID: " & ex.Detail.EventID.ToString & ")")
        End Try
    End Using
End Using
Dim endTime As DateTime = DateTime.Now
Dim durationInMS As Double = (endTime - startTime).TotalMilliseconds
Console.WriteLine(vbCr & "V2 Upload Completed: " + DateTime.Now.ToString() + " (" + durationInMS.ToString() + ")")
JPCS.Close()

web.config

<system.serviceModel>
    <bindings>
        <customBinding>
            <binding name="JP_WCFBinding">
                <!-- maxReceivedMessageSize 600MB, maxBufferSize 2MB -->
                <binaryMessageEncoding compressionFormat="GZip" />
                <httpsTransport transferMode="Streamed" maxReceivedMessageSize="629145600" maxBufferSize="2097152"/>
            </binding>
        </customBinding>
    </bindings>
    <services>
        <service behaviorConfiguration="JP_WCFbehavior" name="JP_WCF.JP_WCFServices">
            <endpoint address="" binding="customBinding" bindingConfiguration="JP_WCFBinding" contract="JP_WCF.IJP_WCF"/>
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="JP_WCFbehavior">
                <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
                <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
        </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>

app.config

 <system.serviceModel>
    <bindings>
        <customBinding>
            <binding name="CustomBinding_IJP_WCF">
                <binaryMessageEncoding compressionFormat="GZip" />
                <httpsTransport transferMode="Streamed" />
            </binding>
        </customBinding>
    </bindings>
    <client>
        <endpoint address="https://dev-wcf.localhost/JP_WCF.svc"
            binding="customBinding" bindingConfiguration="CustomBinding_IJP_WCF"
            contract="JP_WCFService.IJP_WCF" name="CustomBinding_IJP_WCF" />
    </client>
</system.serviceModel>

【问题讨论】:

  • 我想知道,您是如何检测到发生异常后继续流式传输的?
  • 另外,uploadStreamWithProgress 的实现是什么?

标签: .net vb.net wcf


【解决方案1】:

如果您担心此调用的性能,您始终可以在流式传输之前调用服务器以检查此上传的有效性。这样,如果出现问题,您就可以完全避免流式传输文件,并避免应用程序中出现任何异常状态(也很昂贵)。

因此,您可以相对快速地访问服务器以验证诸如

之类的内容
  1. 有效的文件位置
  2. 写入该位置的权限
  3. 有效文件的大小
  4. 任何相关的业务规则

然后,您可以拨打电话,而无需尝试使用异常来管理应用程序流。请记住:异常应该针对特殊情况。这样,如果您的应用程序确实引发了异常,则意味着发生了非常异常的事情并且速度下降更可口(因为这种情况在理论上非常很少见)。

【讨论】:

    【解决方案2】:

    您的客户有双工通道吗?如果是这样,那么在上传文件时回调客户端合约以发送信息是非常直接的。

    如果不是,那么这里的一种好方法是使用内存缓冲区将数据以块的形式流式传输到服务器。下面我举了几个很好的例子。

    要点是您在客户端将文件拆分为多个块,然后将其逐块发送到服务器。如果任何块失败,那么它可以重试该块,或者优雅地失败而不发送任何更多数据。

    这些引用使用StreamWithProgress 类在客户端处理此逻辑。希望这会有所帮助。

    Code Project reference

    Simple implementation using StreamWithProgress class

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多