WCF - 如果服务器抛出FaultException,则从客户端到服务器的中止流

Hea*_*ore 9 .net vb.net wcf

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

类似问题:

如何从服务器端方法中止WCF文件上载

采取以下(简化的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
Run Code Online (Sandbox Code Playgroud)

示例服务器方法:

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
Run Code Online (Sandbox Code Playgroud)

示例客户端方法:

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()
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

的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>
Run Code Online (Sandbox Code Playgroud)

leo*_*eon 2

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

如果没有,那么一种好的方法是使用内存缓冲区将数据分块流式传输到服务器。我在下面列出了一些很好的例子。

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

这些引用利用一个StreamWithProgress类来处理客户端上的此逻辑。希望这可以帮助。

代码 项目参考

使用StreamWithProgress类的简单实现