Comment tester une connexion cassée de TCPClient après avoir été connecté?

Je me bats avec un problème depuis 3 jours. Je ne trouve aucune solution, merci de m’aider 🙂
Je travaille avec Visual Studio 2010 et le langage C #.

J’ai un appareil qui fonctionne comme un serveur, qui envoie des données dans des délais très irréguliers (impossible de définir un délai de lecture).
J’ai écrit un client TCP pour me connecter à ce serveur et lire les données. Cela fonctionne bien, mais lorsque quelque chose ne va pas avec le réseau et que le serveur devient indisponible (par exemple, lorsque je détwig le câble réseau de mon ordinateur), il faut environ 10 secondes pour que l’application “remarque” une exception. (Je ne sais pas pourquoi exactement 10 secondes? Où est-il défini? Puis-je le changer?)
Je veux réagir plus rapidement – disons après une seconde après la rupture de la connexion.
Rechercher sur Google pour réponse ne me fournit cependant aucune solution de travail.

Le code de test est ci-dessous, j’essaye de le créer sur 2 threads: l’un lit les données, le second recherche l’état de la connexion et devrait m’alarmer lorsqu’il est cassé. Cela ne fonctionne ni pour TCPClient ni pour la classe Socket. J’ai essayé de lire / écrire des données avec tcpClient.SendTimeout = 100; et stream.WriteTimeout = 100; mais cela ne semble pas fonctionner.

 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Threading; namespace TCPClient { class Program { static volatile bool _continue = true; static TcpClient tcpClient; static NetworkStream stream; static void Main(ssortingng[] args) { try { //client thread - listen from server Thread tcpListenThread = new Thread(TcpListenThread); tcpListenThread.Start(); //connection checking thread Thread keepAliveThread = new Thread(KeepAliveThread); keepAliveThread.Start(); while (_continue) { if (Console.ReadLine() == "q") { _continue = false; } } keepAliveThread.Join(2000); if (keepAliveThread.IsAlive) { Console.WriteLine("Thread keepAlive has been aborted..."); keepAliveThread.Abort(); } tcpListenThread.Join(2000); if (tcpListenThread.IsAlive) { Console.WriteLine("Listen thread has been aborted..."); tcpListenThread.Abort(); } } catch (Exception ex) { Console.WriteLine("\n" + ex.Message); } Console.WriteLine("\nHit any key to quit..."); Console.Read(); } private static void TcpListenThread() { ssortingng server = "172.20.30.40"; int port = 3000; try { using (tcpClient = new TcpClient()) { tcpClient.Connect(server, port); if (tcpClient.Connected) Console.WriteLine("Successfully connected to server"); using (stream = tcpClient.GetStream()) { while (_continue) { Byte[] data = new Byte[1024]; Int32 bytes = stream.Read(data, 0, data.Length); ssortingng responseData = System.Text.Encoding.ASCII.GetSsortingng(data, 0, bytes); Console.WriteLine("Received: {0}, responseData); } } } } catch (Exception ex) { Console.WriteLine("Listen thread exception! " + ex.Message); } } private static void KeepAliveThread() { while (_continue) { if (tcpClient != null) { try { //...what to put here to check or throw exception when server is not available??... } catch (Exception ex) { Console.WriteLine("Disconnected..."); } } Thread.Sleep(1000); //test for connection every 1000ms } } } } 

Modifier:
La réponse de @ carsten: bien que cela semble prometteur, cette solution ne fonctionne pas …
J’ai fait l’application de test la plus simple pour cela:

 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Threading; namespace TCPClientTest { class Program { static void Main(ssortingng[] args) { try { ssortingng server = "172.20.30.40"; int port = 3000; using (TcpClient tcpClient = new TcpClient()) { tcpClient.Connect(server, port); int i = 0; while (true) { // This is how you can determine whether a socket is still connected. bool blockingState = tcpClient.Client.Blocking; try { byte[] tmp = new byte[1]; tcpClient.Client.Blocking = false; tcpClient.Client.Send(tmp, 0, 0); Console.WriteLine("Connected!"); } catch (SocketException e) { // 10035 == WSAEWOULDBLOCK if (e.NativeErrorCode.Equals(10035)) Console.WriteLine("Still Connected, but the Send would block"); else { Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode); } } finally { tcpClient.Client.Blocking = blockingState; } Console.WriteLine("Connected: {0} ({1})", tcpClient.Client.Connected, i++); Thread.Sleep(1000); } } } catch (Exception ex) { Console.WriteLine("Global exception: {0}", ex.Message); } } } } 

Les résultats suivent, il affiche:

Connecté!
Connecté: vrai

plus mon numéro de commande toutes les secondes. Lorsque je déconnecte le câble réseau, il faut 8 secondes pour lancer l’impression:

Déconnecté: code d’erreur 10054!
Connecté: Faux

donc par 8 secondes je ne suis pas au courant que la connexion est perdue. Il semble que le ping soit la meilleure option ici, mais je vais tester une autre solution.

Réponse simple. Tu ne peux pas. Tcp est fabriqué d’une manière qui ne le permet pas. Cependant, le moyen habituel d’y parvenir est d’envoyer un ping avec un intervalle plus court que les messages. Donc, disons que chaque fois que vous recevez un message du serveur, vous démarrez une horloge qui compte à rebours pendant 1 minute, puis vous envoyez une commande “ping” (si vous n’avez pas reçu de nouveau message entre les deux). Si vous ne recevez pas de réponse à votre “ping” dans les 30 secondes, vous concluez que la connexion est rompue.

Théoriquement, vous devriez pouvoir faire cela au niveau du paquet (tcp envoie un ACK que vous devriez pouvoir vérifier), mais je ne sais pas si cela est possible en C #, ni dans aucun autre langage de programmation, ou si vous devez le faire dans le microprogramme / matériel.

Je pense que c’est une question qui revient souvent. C’est peut-être pour cette raison que les documents MSDN apportent une bonne réponse à cette question – voir Socket.Connected.

Citation de là:

La propriété Connected obtient l’état de la connexion du socket à partir de la dernière opération d’E / S. Lorsqu’il renvoie false, le socket n’a jamais été connecté ou n’est plus connecté.

La valeur de la propriété Connected reflète l’état de la connexion à partir de l’opération la plus récente. Si vous devez déterminer l’état actuel de la connexion, effectuez un appel d’envoi non bloquant, de zéro octet. Si l’appel aboutit ou génère un code d’erreur WAEWOULDBLOCK (10035), le socket est toujours connecté; sinon, la prise n’est plus connectée.

avec cet exemple de code:

 // .Connect throws an exception if unsuccessful client.Connect(anEndPoint); // This is how you can determine whether a socket is still connected. bool blockingState = client.Blocking; try { byte [] tmp = new byte[1]; client.Blocking = false; client.Send(tmp, 0, 0); Console.WriteLine("Connected!"); } catch (SocketException e) { // 10035 == WSAEWOULDBLOCK if (e.NativeErrorCode.Equals(10035)) Console.WriteLine("Still Connected, but the Send would block"); else { Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode); } } finally { client.Blocking = blockingState; } Console.WriteLine("Connected: {0}", client.Connected); 

et la motivation simple pour une méthode d’extension:

 public static bool IsConnected(this Socket client) { // This is how you can determine whether a socket is still connected. bool blockingState = client.Blocking; try { byte [] tmp = new byte[1]; client.Blocking = false; client.Send(tmp, 0, 0); return true; } catch (SocketException e) { // 10035 == WSAEWOULDBLOCK if (e.NativeErrorCode.Equals(10035)) return true; else { return false; } } finally { client.Blocking = blockingState; } } 

C’est un très vieux sujet, mais c’est le premier article SO qui est apparu lorsque j’ai cherché cette question et que j’ai trouvé une solution plus utile ailleurs. J’ai donc pensé poster un lien pour aider les autres à l’avenir:

https://social.msdn.microsoft.com/Forums/en-US/c857cad5-2eb6-4b6c-b0b5-7f4ce320c5cd/c-how-to-determine-if-a-tcpclient-has-been-disconnected?forum== netfxnetcom & prof = requirejs

ElFenix ​​a posté cette réponse qui a fonctionné pour moi:

 // Detect if client disconnected if (tcp.Client.Poll(0, SelectMode.SelectRead)) { byte[] buff = new byte[1]; if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0) { // Client disconnected bClosed = true; } } 

Juste au cas où quelqu’un aurait besoin de quelque chose de simple et efficace.

C’est le code que je suis venu avec

  while (true) { ssortingng s = null; DateTime readLag = DateTime.Now; try { s = streamIn.ReadLine(); } catch (Exception ex) { SocketException sockEx = ex.InnerException as SocketException; if (sockEx != null) { OnDebug("(" + sockEx.NativeErrorCode + ") Exception = " + ex); } else { OnDebug("Not SockEx :" + ex); } } if (enableLog) OnDebug(s); if (s == null || s == "") { if (readLag.AddSeconds(.9) > DateTime.Now) { break; } } else { CommandParser(s); } } 

Ce que j’ai eu était l’erreur native 10035 à chaque fois qu’une lecture échouait mais bloquait.

Lorsque la connexion a été mise à la corbeille, j’ai instantanément obtenu un retour sans données.

Régler readLag.AddSeconds () juste en dessous de mon délai de lecture me donnerait une bonne idée que le temps ne s’est jamais écoulé et que je n’ai aucune donnée. Ce qui ne devrait pas arriver.

Dès que ce critère est apparu, je me suis éloigné de la boucle et le fil a pris fin.

J’espère que cela aide quelqu’un d’autre là-bas.

Il existe une option de socket appelée SO_KEEPALIVE provenant de l’implémentation unix oginale qui permet au protocole TCP d’envoyer des sondes occasionnelles pour s’assurer que l’autre extrémité du tuyau est toujours à l’écoute. Il peut être réglé avec la fréquence de configuration des sondes via Socket.IOControl

il sera correct et fonctionnera si vous tapez ce qui suit:

 byte[] tmp = new byte[] {0}; ... client.Send(tmp, 1, 0); ...