7

私はかなりの量の検索を行いましたが、これに対する解決策や答えを見つけることができませんでした。「問題」を示すために作成したテストアプリがあります。(私は、これが WCF のしくみであり、私にできることは何もないという考えにオープンです。)

私のテスト アプリは、Visual Studio で生成されたプロキシを使用する単純な WCF サービスとクライアントです。稼働しているがサービスを実行していないリモート マシンにプロキシを指定します。その点は重要です。リモート マシンがまったく実行されていない場合、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>
4

1 に答える 1

0

非同期操作でプロキシ クラスを生成すると、より多くのマイレージが得られる可能性があり、コードはより単純になります。

// 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();

それが役に立ちそうな場合は、非同期パターンを読んでください。 この MSKBは驚くほど便利で、非同期コードを記述するいくつかの方法を示しています。

あなたの問題は、実際には進行中の WCF 操作をキャンセルする方法がないことにあるかもしれません。これには、独自のキャンセル メカニズムを作成する必要があります。たとえば、リクエストが実際に進行中であるかどうか (つまり、DoWork実行を中止する必要があるかどうか) や、ネットワークまたは他のマシンの遅延が原因であるかどうかを判断することはできません。

于 2012-12-11T18:51:08.990 に答える