18

これを機能させる方法、または機能しない理由についての決定的な答えを本当に知る必要があるため、賞金を再び更新します。

ここに問題の別の説明を追加しました。

双方向 (IsOneWay = false) の WCF クライアント サーバーを .Net 3/3.5 で動作させるのに非常に苦労しました。

クライアントがサービスに正常に登録された後、サービスの定期的な Announcement() が、登録されたクライアントにコールバックします。2 秒に調整されたサーバーの SendTimeout が経過するまで、クライアントまたはサーバーのいずれかがハングするようになりました。次に、サーバー側には次のようにタイムアウト例外があります。その場合にのみ、クライアント ユーザー コードは直ちにメソッド呼び出しを受け取り、値を返そうとします。それまでに、クライアントのソケットは中止され、WCF は失敗します。

クライアント上の何かが、ソケットがタイムアウトするまで処理からローカル WCF キューをぶら下げているようですが、ローカル メソッド呼び出しをキャンセルするのに十分早くはありません。しかし、以下の例外が信じられる場合、サーバーはhttp://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymousに操作を送信しようとしており(不適切です!)、タイムアウトしています。おそらく、その URI はリモート接続されたクライアントの "名前" に過ぎず、WCF はエラー メッセージの目的でそれを参照することを認識しており、URI の読み込みに失敗していることを意味しているように見えます。サーバーが最初に失敗したのか、クライアントが最初に失敗したのかわかりません。

WCF トレースを追加しようとしましたが、それ以上の情報が得られません。

同様のサンプル コードはこちらにありますが、多すぎて消化しきれませんでした。私はそのコードのバリエーションを試しました。

TimeoutException 'This request operation sent to http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous did not receive a reply within the configured timeout (00:00:00).  The time allotted to this operation may have been a portion of a longer timeout.  This may be because the service is still processing the operation or because the service was unable to send a reply message.  Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.'

Server stack trace: 
   at System.ServiceModel.Dispatcher.DuplexChannelBinder.SyncDuplexRequest.WaitForReply(TimeSpan timeout)
   at System.ServiceModel.Dispatcher.DuplexChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
4

4 に答える 4

27

まだ持っていない場合は、最初にProgramming WCF Servicesのコピーを入手してください。

クライアントが WinForm または WPF の場合は[CallbackBehavior(UseSynchronizationContext = false)]、UI スレッドがメッセージ ループに入るまでクライアントが受信メッセージを処理しないように使用する必要があります。

まず、WCF の "Duplex" チャネルは真の Duplex ではありません。 からのメッセージ

  • クライアントからサーバーへ
  • サーバーが待機しているクライアントからのメッセージをブロックできます
  • (またはその逆)

メッセージは単一の WCF チャネルでのみ順番にディスパッチされるためです。二重 WCF チャネルでは、2 つの着信メッセージ キューは提供されません。「TwoWay」呼び出しから返される結果は、WCF スタックのこのレベルの「呼び出し」とまったく同じです。 これを頭に入れてしまえば、多くの問題がより明確に理解できるようになります。

クライアントが WinForm または WPF の場合は[CallbackBehavior(UseSynchronizationContext = false)]、UI スレッドがメッセージ ループに入るまで、クライアントは受信メッセージを処理しないので、使用する必要がある場合があります。

デッドロックを回避するのに役立ついくつかのルールを見つけました。(私の WCF の質問を見て、私が苦しんでいたことを確認してください!)

サーバーは、同じクライアントからの呼び出しが処理中であるのと同じ接続で、クライアントに決して呼び出してはなりません。

および/または

クライアントは、コールバックの処理中に「コールバック」に使用されるのと同じ接続でサーバーにコールバックしてはなりません。

次回は、2 つのコントラクト (したがって TCP 接続) をコールバック用に使用し、もう 1 つはすべての client->server 要求用に使用すると思います。または、私自身のポーリングシステムを使用してください。これは私に非常に苦痛を与えました。

申し訳ありませんが、今日は例を書く時間がありません。とにかく、ほとんどの例は、例がやろうとしていることに対してはうまくいきますが、実際には何らかの理由であなたのアプリケーションと関係がありません。

WCF の例について私が知っている最良の Web サイトは、Juval Lowy の Web サイトです。

私はあなたと同じ種類の問題を抱えていたので、Stack Overflow で WCF について私が尋ねた質問も役に立つかもしれません。

また、Stack Overflow に関する WCF のすべての質問と回答を 1 日か 2 日かけて読むと、避けるべき問題がよくわかります。

于 2010-08-19T09:21:11.527 に答える
4

クライアントが WinForms アプリケーションであると仮定すると、Ian の提案を使用し、必要に応じて UI スレッドで実行される作業を委任することにより、コールバックの処理をアプリケーションの残りの部分から独立させる必要があります。たとえば、ラベルのテキストを変更するなど、サーバーがクライアントに何かを通知したい場合は、次のようにします。

[CallbackBehavior(UseSynchronizationContext = false)]
internal class ServiceCallback : IServiceCallback
{
    ChangeMainFormLabel(string text)
    {
        frmMain.Instance.BeginInvoke(new Action()(() => frmMain.Instance.lblSomething.Text = text));
    }
}

(Instanceは、単一のインスタンスを返す静的プロパティでfrmMainあり、サーバーが変更したいlblSomethingものLabelです。) このメソッドはすぐに返され、サーバーはクライアントの UI を待機することから解放され、解放されるとすぐに UI が更新されます。そうするために。そして何よりも、誰も待っていないのでデッドロックがありません。

于 2010-08-22T09:34:02.027 に答える
2

http://www.codeproject.com/KB/WCF/WCF_Duplex_UI_Threads.aspx

于 2010-09-16T14:57:38.573 に答える
0

すみません、例をすっかり忘れていました(:-$)。

サーバー用の私のコードは次のとおりです。
ISpotifyServer.cs

[ServiceContract(CallbackContract = typeof(ISpotifyCallback))]
public interface ISpotifyService
{
    [OperationContract(IsOneWay = true)]
    void Login(string username, string password);
}

ISpotifyCallback.cs

[ServiceContract]
public interface ISpotifyCallback
{
    [OperationContract(IsOneWay = true)]
    void OnLoginComplete();

    [OperationContract(IsOneWay = true)]
    void OnLoginError();
}

Program.cs

class Program
{
    static void Main(string[] args)
    {

        using (ServiceHost host = new ServiceHost(typeof(SpotifyService)))
        {
            host.Open();

            Console.WriteLine("Service running.");
            Console.WriteLine("Endpoints:");

            foreach (ServiceEndpoint se in host.Description.Endpoints)
                Console.WriteLine(se.Address.ToString());


            Console.ReadLine();

            host.Close();
        }
    }
}

AppData.xml

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MetadataEnabledBehavior">
          <serviceMetadata />
          <serviceDebug includeExceptionDetailInFaults="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="MetadataEnabledBehavior" name="SpotiServer.SpotifyService">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:9821" />
          </baseAddresses>
        </host>
        <clear />
        <endpoint address="spotiserver" binding="netTcpBinding"
            name="TcpEndpoint" contract="SpotiServer.ISpotifyService"
            listenUriMode="Explicit">
          <identity>
            <dns value="localhost"/>
            <certificateReference storeName="My" storeLocation="LocalMachine"
                x509FindType="FindBySubjectDistinguishedName" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

クライアントの場合:
Program.cs

class Program
{
    static void Main(string[] args)
    {
        InstanceContext context = new InstanceContext(new CallbackHandler());

        String username;
        String password;

        Console.Write("Username: ");
        username = Console.ReadLine();

        Console.WriteLine("Password: ");
        password = ReadPassword();

        SpotiService.SpotifyServiceClient client = new SpotiService.SpotifyServiceClient(context);
        client.Login(username, password);

        Console.ReadLine();
    }

    private static string ReadPassword()
    {
        Stack<string> passbits = new Stack<string>();
        //keep reading
        for (ConsoleKeyInfo cki = Console.ReadKey(true); cki.Key != ConsoleKey.Enter; cki = Console.ReadKey(true))
        {
            if (cki.Key == ConsoleKey.Backspace)
            {
                //rollback the cursor and write a space so it looks backspaced to the user
                Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
                Console.Write(" ");
                Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
                passbits.Pop();
            }
            else
            {
                Console.Write("*");
                passbits.Push(cki.KeyChar.ToString());
            }
        }
        string[] pass = passbits.ToArray();
        Array.Reverse(pass);
        return string.Join(string.Empty, pass);
    }
}

それがすべてだと思います。当然のことながら、(クライアント側で)結果をコンソールに出力し、サーバー側でユーザー名とパスワードが正しい場合は「OnLoginComplete」を実行し、そうでない場合は「OnLoginError」を実行するインターフェースの実装も持っています。うまくいかない場合、またはすべての設定にサポートが必要な場合はお知らせください。

于 2010-09-14T23:41:46.017 に答える