由于远程服务未运行,如何中断在连接状态下被阻止的 WCF 代理

Posted

技术标签:

【中文标题】由于远程服务未运行,如何中断在连接状态下被阻止的 WCF 代理【英文标题】:How do I interrupt a WCF proxy blocked in Connecting state because remote service not running 【发布时间】:2012-11-22 13:28:15 【问题描述】:

我进行了大量搜索,但未能找到解决方案或答案。我有一个我写的测试应用程序来显示“问题”。 (我认为这正是 WCF 的工作方式,对此我无能为力。)

我的测试应用是一个简单的 WCF 服务和客户端,带有 Visual Studio 生成的代理。我将代理指向已启动但未运行服务的远程计算机。这一点很重要。如果远程计算机根本没有运行,WCF 调用将立即失败。

客户端应用程序创建客户端代理类的实例,启动一个新任务,然后尝试在服务接口上进行调用,这将阻塞,因为另一端的服务不存在。后台任务休眠 5 秒,然后关闭代理。我期待另一个线程在关闭代理时立即出错,但事实并非如此。在发出初始呼叫大约 20 秒后,它就会出现故障。

这是我的客户端代码:

using System;
using System.ServiceModel;
using System.Threading;
using System.Threading.Tasks;
using TestConsoleClient.MyTestServiceReference;

namespace TestConsoleClient

class Program

    static void Main(string[] args)
    
        MyTestServiceClient client = new MyTestServiceClient();

        // Start new thread and wait a little bit before trying to close connection.
        Task.Factory.StartNew(() =>
        
            LogMessage("Sleeping for 5 seconds ...");
            Thread.Sleep(5000);
            if (client == null)
            
                LogMessage("Woke up!  Proxy was closed before waking up.");
                return;
            

            LogMessage("Woke up!  Attempting to abort connection.");
            LogMessage(string.Format("Channel state before Close: 0", 
                ((ICommunicationObject)client).State));

            client.Close();
            ((IDisposable)client).Dispose();
            client.Abort();

            // Channel state is Closed, as expected, but blocked thread does not wake up?
            LogMessage(string.Format("Channel state after Abort: 0",
                ((ICommunicationObject)client).State));

            client = null;
        );

        // Start connecting.
        LogMessage("Starting connection ...");

        try
        
            LogMessage("Calling MyTestService.DoWork()");
            client.DoWork();  // Timeout is 60 seconds.  State is "Connecting"
        
        catch (Exception ex)
        
            LogMessage(string.Format("Exception caught: 0", ex.Message));
            if (client != null)
            
                client.Abort();
                ((IDisposable) client).Dispose();
                client = null;
            
        
        finally
        
            if (client != null)
            
                client.Close();
                client = null;
            
        

        LogMessage("Press Enter to exit ...");

        Console.ReadLine();
    

    private static void LogMessage(string msg)
    
        Console.WriteLine("0: [1] 2",
                          DateTime.Now.ToString("hh:mm:ss tt"),   Thread.CurrentThread.ManagedThreadId, msg);
    


这是我的程序的输出:

01:48:31 PM: [9] Starting connection ...
01:48:31 PM: [9] Calling MyTestService.DoWork()
01:48:31 PM: [10] Sleeping for 5 seconds ...
01:48:36 PM: [10] Woke up!  Attempting to abort connection.
01:48:36 PM: [10] Channel state before Close: Opening
01:48:36 PM: [10] Channel state after Abort: Closed
01:48:54 PM: [9] Exception caught: Could not connect to net.tcp://th-niconevm:80
80/MyTestService. The connection attempt lasted for a time span of 00:00:22.0573
009. TCP error code 10060: A connection attempt failed because the connected par
ty did not properly respond after a period of time, or established connection fa
iled because connected host has failed to respond 10.10.10.10:8080.
01:48:54 PM: [9] Press Enter to exit ...

我想要完成的是让用户中断连接,以便他们可以在必要时进行调整,然后重试。正如您在输出中看到的那样,通道随后从“Opening”状态变为“Closed”状态,但服务调用一直处于阻塞状态,直到超时。我当然可以解决它,但似乎应该有办法打断它。

这是客户端 app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
    <bindings>
        <netTcpBinding>
            <binding name="NetTcpBinding_IMyTestService" closeTimeout="00:01:00"
                openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
                hostNameComparisonMode="StrongWildcard" listenBacklog="10"
                maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
                maxReceivedMessageSize="65536">
                <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                    maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                <reliableSession ordered="true" inactivityTimeout="00:10:00"
                    enabled="false" />
                <security mode="Transport">
                    <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
                    <message clientCredentialType="Windows" />
                </security>
            </binding>
        </netTcpBinding>
    </bindings>
    <client>
        <endpoint address="net.tcp://remotehost:8080/MyTestService" binding="netTcpBinding"
            bindingConfiguration="NetTcpBinding_IMyTestService" contract="MyTestServiceReference.IMyTestService"
            name="NetTcpBinding_IMyTestService">
            <identity>
                <userPrincipalName value="remotehost\ClientUser" />
            </identity>
        </endpoint>
    </client>
</system.serviceModel>
</configuration>

【问题讨论】:

绑定配置呢?特别是接收超时? 将客户端 app.config 添加到原始问题... 在任务操作中尝试为 Close() 调用传递显式超时:client.Close(TimeSpan.FromSeconds(2)); 所以理论上它应该在最多 2 秒后关闭/引发异常 我已经尝试将 app.config 中的 closeTimeout 更改为 00:00:01 并且它对超时没有影响。我也会尝试在代码中明确设置它。 很遗憾,这也没有效果。 【参考方案1】:

如果您使用异步操作生成代理类,您可能会获得更多的收益,然后您的代码会变得更加简单。

// Asynchronously call DoWork
MyTestServiceClient client = new MyTestServiceClient();
IAsyncResult iar = client.BeginDoWork(null, null);

// Sleep, dance, do the shopping, whatever
Thread.Sleep(5000);

bool requestCompletedInFiveSeconds = iar.IsCompleted;

// When you're ready, remember to call EndDoWork to tidy up
client.EndDoWork(iar);
client.Close();

如果这听起来可能有帮助,请阅读异步模式。 This MSKB 非常有用,它展示了几种编写异步代码的方法。

您的问题实际上可能是无法取消正在进行的 WCF 操作;您必须为此编写自己的取消机制。例如,您将无法判断请求是否实际在进行中(即:DoWork 执行需要中止)或者某些网络或其他机器延迟是否是阻塞。

【讨论】:

我很快意识到,如果应用程序最初是在考虑异步的情况下编写的,那么这将不是问题。不幸的是,我正在研究基于同步 WCF 调用(双向 TCP)的重要代码库,修改框架和使用它的表示层将是大量工作。这让我很难过,但这就是我所在的地方。提出我最初的问题,真的没有办法在超时之前中断连接吗?在本机套接字层,关闭套接字句柄将中断任何阻塞的调用。所以,我知道这是可能的。 不幸的是,我不这么认为。您需要异步调用DoWork,以便您可以控制。你可以翻转你的代码,而不是产生一个任务来监视客户端,而是产生一个任务来调用DoWork?然后,您可以监视该任务,如果花费的时间太长,则将其终止。

以上是关于由于远程服务未运行,如何中断在连接状态下被阻止的 WCF 代理的主要内容,如果未能解决你的问题,请参考以下文章

远程连接出现“由于没有远程桌面授权服务器可以提供许可证,远程会话被中断”这怎么解决啊?

阿里云服务器远程连接错误:由于一个协议错误(代码:0x112f),远程会话将被中断。

由于没有远程桌面授权服务器可以提供许可证,远程会话被中断。

如何调试SSH连接

如何调试SSH连接

文件和打印共享资源处于联机状态,但未对连接尝试做出响应。 检测到 远程计算机不接受端口 445 。