Actuellement, je travaille sur un simple programme de chat de serveur client (voulait entrer dans les choses de communication du serveur client en C#). Cela fonctionne jusqu'à maintenant, sauf pour se déconnecter correctement du serveur.Problèmes avec la fermeture de TcpClient et NetworkStream
Sur le client j'utilise ce code ici pour fermer la connexion:
client.Client.Disconnect(false); // client is the TcpClient
client.Close();
Sur le serveur il y a une boucle filetée en attente pour les messages du client:
private void StartChat()
{
int requestCount = 0;
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
string rCount = null;
while (true)
{
try
{
requestCount++;
NetworkStream stream = tcpClient.GetStream();
int bufferSize = (int)tcpClient.ReceiveBufferSize;
if (bufferSize > bytesFrom.Length)
{
bufferSize = bytesFrom.Length;
}
stream.Read(bytesFrom, 0, bufferSize);
dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
rCount = Convert.ToString(requestCount);
string message = client.Name + " says: " + dataFromClient;
program.Broadcast(message);
}
catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
{
program.UserDisconnected(client);
break;
}
catch(ArgumentOutOfRangeException ex)
{
Debug.WriteLine(ex.ToString());
break;
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
break;
}
}
Si le client se déconnecte avec le code indiqué ci-dessus, la fonction récupère le flux tout le temps et produit une telle sortie:
\0\0\0\0\0\0\0 [and so on]
Dans ce cas, le ArgumentOutOfRangeException
sera lancé car il n'y a pas d'index de $
. J'ai ajouté un break
pour éviter et l'exécution sans fin de la boucle.
Étonnamment, le ObjectDisposedException
ne sera pas lancé. De plus, le System.IO.IOException
ne sera pas lancé mais il devrait l'être parce que le flux a été fermé, donc la connexion a été refusée.
Si je ferme simplement l'application client qui est connectée au serveur, le serveur n'arrête pas la boucle, il attend juste un flux qui ne viendra jamais parce que le client s'est déconnecté. Comment puis-je détecter si le client est déconnecté ou n'est plus joignable?
Et la façon dont je ferme la connexion client est-elle un bon moyen de fermer une connexion?
Merci pour votre aide!
Mise à jour:
private void StartChat()
{
int requestCount = 0;
byte[] bytesFrom = new byte[10025];
string dataFromClient = null;
string rCount = null;
while (true)
{
try
{
requestCount++;
NetworkStream stream = tcpClient.GetStream();
stream.ReadTimeout = 4000;
int bufferSize = (int)tcpClient.ReceiveBufferSize;
if (bufferSize > bytesFrom.Length)
{
bufferSize = bytesFrom.Length;
}
// Wait for a client message. If no message is recieved within the ReadTimeout a IOException will be thrown
try
{
int bytesRead = stream.Read(bytesFrom, 0, bufferSize);
stream.Flush();
if (bytesRead == 0)
{
throw new System.IO.IOException("Connection seems to be refused or closed.");
}
}
catch (System.IO.IOException)
{
byte[] ping = System.Text.Encoding.UTF8.GetBytes("%");
stream.WriteTimeout = 1;
stream.Write(ping, 0, ping.Length);
continue;
}
dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
rCount = Convert.ToString(requestCount);
string message = client.Name + " says: " + dataFromClient;
program.Broadcast(message);
}
catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException)
{
Debug.WriteLine(ex.ToString());
program.UserDisconnected(client);
break;
}
catch(ArgumentOutOfRangeException ex)
{
Debug.WriteLine(ex.ToString());
}
catch(Exception ex)
{
Debug.WriteLine(ex.ToString());
break;
}
}
}
Est-ce que cela signifie que vous ne pouvez pas détecter si le client est parti? En regardant le docu, il devrait lancer une exception ObjectDispoedException ou au moins une exception IOException car il ne peut plus en lire – chris579
Une déconnexion _graceful_ n'est pas un cas exceptionnel, et est détectée par 'stream.Read()' renvoyant 0. Vous devriez en effet avoir un gestionnaire d'exception, mais je doute que 'ObjectDisposedException' soit levé à moins que vous ne le disposiez vous-même, puis essayez de l'utiliser. –
Vous avez raison, DisposedException ne sera pas levée, sauf si cet objet est toujours là et n'est pas supprimé par garbage collector ou manuellement éliminé. Cependant, merci beaucoup, cela a fonctionné! Je vais accepter votre réponse dès que possible. Éditer: Si je ferme le client, la boucle ne se terminera pas et restera jusqu'à ce que l'application serveur soit fermée. Comment pourrais-je résoudre ce problème? – chris579