2009-12-10 6 views
2

Il n'est pas possible de définir un délai d'attente de connexion pour un appel distant .NET. Documentation fait parfois référence à TcpChannel propriétés qui prétendument le faire, mais discussions et le plus récent docs que j'ai trouvé indiquent que ce n'est pas possible. On peut définir un délai d'expiration sur l'appel distant lui-même, mais pas sur la tentative de connexion initiale. Vous êtes bloqué avec le délai par défaut de 45 secondes.Vérification de la présence du serveur distant .NET - Mon approche est-elle correcte?

Pour diverses raisons, je ne peux pas utiliser WCF.

Cela provoque un problème lorsque le serveur distant disparaît. Si je tente de faire un appel à distance, je suis bloqué pour ces 45 secondes. Ce n'est pas bon. Je veux vérifier la présence du serveur distant. Ping il avec une PingTimeout est l'approche la plus simple, mais je veux vérifier spécifiquement pour le serveur distant, par opposition à seulement la machine en cours.

Après quelques essais, je suis venu avec cette approche:

  1. commencent une connexion Asynchrone socket TCP au port d'accès distant.
  2. Attendez la fin de la connexion, ou un délai d'attente pour expirer (en utilisant un ManualResetEvent).
  3. Si la connexion asynchrone a réussi, renvoie succès. Sinon, retournez l'échec.

Cela fonctionne, mais je ne suis pas sûr de mon utilisation de mon WaitHandle et socket. Je voudrais également assurer des vérifications concurrentes WRT thread-sécurité, que je pense avoir fait. Mon code est ci-dessous. Voyez-vous des problèmes avec mon approche?

private static bool IsChannelOpen(string ip, int port, int timeout) 
{ 
    IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(ip), port); 
    Socket client = new Socket(endpoint.AddressFamily, 
       SocketType.Stream, ProtocolType.Tcp); 
    SocketTestData data = new SocketTestData() 
       { Socket = client, ConnectDone = new ManualResetEvent(false) }; 
    IAsyncResult ar = client.BeginConnect 
       (endpoint, new AsyncCallback(TestConnectionCallback), data); 

    // wait for connection success as signaled from callback, or timeout 
    data.ConnectDone.WaitOne(timeout); 
    client.Close(); 
    return data.Connected; 
} 

private static void TestConnectionCallback(IAsyncResult ar) 
{ 
    SocketTestData data = (SocketTestData)ar.AsyncState; 
    data.Connected = data.Socket.Connected; 
    if (data.Socket.Connected) 
    { 
     data.Socket.EndConnect(ar); 
    } 
    data.ConnectDone.Set(); // signal completion 
} 

public class SocketTestData 
{ 
    public Socket Socket { get; set; } 
    public ManualResetEvent ConnectDone { get; set; } 
    public bool Connected { get; set; } 
} 
+1

J'ai toujours détesté ça à propos de Remoting. –

Répondre

4

Votre approche semble bien, mais serait plus enclin à envelopper le code de prise dans un utilisant pour assurer qu'aucune fuite de ressource en IsChannelOpen fonction, et l'enrouler autour du bloc try/catch aussi, comme si le la connexion a échoué, vous aurez une mauvaise surprise si une exception de socket s'est produite et votre code saute dans les bois pour ne jamais être revu.

Voici ce que je pense, devrait être fait pour solide-ifier le code:

private static bool IsChannelOpen(string ipAddress, int port, int timeout) 
    { 
     IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); 
     SocketTestData data; 
     try 
     { 
      using (Socket client = new Socket(endpoint.AddressFamily, 
         SocketType.Stream, ProtocolType.Tcp)) 
      { 

       data = new SocketTestData() { Socket = client, ConnectDone = new ManualResetEvent(false) }; 
       IAsyncResult ar = client.BeginConnect 
          (endpoint, new AsyncCallback(TestConnectionCallback), data); 

       // wait for connection success as signaled from callback, or timeout 
       data.ConnectDone.WaitOne(timeout); 
      } 
     } 
     catch (System.Net.Sockets.SocketException sockEx) 
     { 
     } 
     catch (System.Exception ex) 
     { 
     } 
     return data.Connected; 
    } 

    private static void TestConnectionCallback(IAsyncResult ar) 
    { 
     SocketTestData data = (SocketTestData)ar.AsyncState; 
     data.Connected = data.Socket.Connected; 
     data.Socket.EndConnect(ar); 
     data.ConnectDone.Set(); // Signal completion 
    } 

    public class SocketTestData 
    { 
     public Socket Socket { get; set; } 
     public ManualResetEvent ConnectDone { get; set; } 
     public bool Connected { get; set; } 
    } 
+0

@Tom: Excellentes suggestions, merci. J'ai ajouté le bloc 'using'. Je ne pense pas ** J'ai besoin du bloc 'try/catch', car cette exception est renvoyée d'un appel synchrone à' IsChannelOpen', et je souhaite peut-être propager l'exception. L'une de mes tentatives précédentes avait un appel inconditionnel à 'EndConnect', mais cela échouait en cas d'échec de la connexion, avec une exception SocketException (" Aucune connexion ne pouvait être établie car la machine cible la refusait activement "). C'est pourquoi j'ai ajouté le conditionnel, et une partie de pourquoi j'ai posé cette question. :-) –

+0

Hmmm ... 'System.Net.Sockets.Socket' n'implémente pas' Dispose'. essayer/attraper/enfin c'est ... –

+0

Intéressant ... dans VS 2008, je peux utiliser le socket dans une clause using (.NET Framework 3.5). Je ne l'aurais pas pensé dans VS 2005 (ie .NET Framework 2) – t0mm13b

2

Je ne pouvais pas croire .NET Remoting ne prend pas en charge délai d'attente. J'ai trouvé un conseil dans Stack Overflow question Remoting set timeout. Là, quelqu'un prétend que la propriété est "connectionTimeout", mais en fonction de mes tests, c'est la propriété "timeout". Cela fonctionne, mais pas exactement le temps que vous avez spécifié. En gros, si vous spécifiez 2000 millisec, il expire à 4000 millisec. Voici mon code:

IDictionary dic = new Hashtable(); 
    dic["timeout"] = 2000; 
    ChannelServices.RegisterChannel(new HttpChannel(dic, null, null), false); 
    ...