2014-06-30 1 views
2

dans l'un de mes projets J'ai implémenté un petit serveur HTTP pour diffuser les données vidéo d'une webcam connectée. Pour cette tâche, j'utilise le System.Net.Sockets.TcpListener de .NET Framework 4.5, qui écoute un point de terminaison préconfiguré et utilise le mtehod AcceptSocketAsync() pour attendre les requêtes entrantes. Vous pouvez voir les parties pertinentes du Code ci-dessous:C#: System.Net.Sockets.TcpListener ne ferme parfois pas Socket correctement

this.server = new TcpListener(endpoint); 
this.server.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(true, 0)); 
this.server.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
this.server.Start(); 
... 
this.listenTask = Task.Factory.StartNew(this.Listen); 

... 

private async void Listen() 
{ 
    try 
    { 
     while (this.server.Server.IsBound) 
     { 
      Socket socket = await this.server.AcceptSocketAsync(); 
      if (socket == null) 
      { 
       break; 
      } 

      Task.Factory.StartNew(() => this.ClientThread(socket)); 
     } 
    } 
    catch (ObjectDisposedException) 
    { 
     Log.Debug("Media HttpServer closed."); 
    } 
} 

Cela fonctionne très bien, quand je démarre l'application et le serveur HTTP est démarré pour la première fois. Cependant, lorsque j'arrête le serveur HTTP (via CheckBox dans les paramètres de l'application), le socket d'écoute non-retourné n'est parfois pas fermé. Quand je vérifie l'état des sockets via la console (netstat -ano) je peux voir que le socket est toujours dans l'état LISTENING. Le problème qui en résulte est, lorsque je redémarre le serveur HTTP à nouveau, je reçois une System.Net.Sockets.SocketException avec le message "Une seule utilisation de chaque adresse de socket est normalement autorisée", ce qui n'est pas surprenant.

La partie de code correspondant pour arrêter le serveur HTTP est la suivante:

... 
if (this.server != null) 
{ 
    if (this.server.Server != null) 
    { 
     if (this.server.Server.Connected) 
     { 
      this.server.Server.Shutdown(SocketShutdown.Both); 
      this.server.Server.Disconnect(true); 
     } 

     this.server.Server.Close(); 
    } 

    this.server.Stop(); 
} 
... 

Je garde aussi la trace de mes connexions ouvertes et les fermer après avoir terminé la transmission de données et lors de l'arrêt du serveur. Aucune des sockets de connexion reste ouverte, donc je crois que seul le socket d'écoute devrait être pertinent pour ce problème. J'ai déjà essayé diverses combinaisons/commandes des méthodes Shutdown(), Disconnect(), Close() et Stop() lors de l'arrêt du serveur, ainsi que la définition/le désarmement de plusieurs options lors du démarrage du serveur comme Linger et ReuseAddress , qui semblait parfois résoudre le problème au début, mais quelques jours plus tard, le problème se reproduisait.

J'ai essayé aussi de « forcer » la prise d'écoute de fermer lors de l'arrêt du serveur à l'aide GetTcpTable (...) et SetTcpEntry (...) à partir iphlpapi.dll, comme décrit dans cette question: How to close a TCP connection by port? Malheureusement, l'approche n'a pas fonctionné pour moi (cela change tout à propos de l'état de la prise d'écoute du tout). Comme je suis un peu désemparé de ce que je pourrais faire d'autre, je demande ici si quelqu'un a une idée de ce qui pourrait causer le problème décrit ou comment le résoudre. Toute aide serait grandement appréciée.

Cordialement, Chris

+1

Vous avez été victime du modèle de disposition superstitieuse. Vous disposez de beaucoup de façons et vérifiez toutes sortes de conditions. 'server.Dispose();' aurait été suffisant et n'aurait pas produit ce problème. – usr

Répondre

-1

Je disposerais de vos connexions socket une fois que vous les avez fermé. La documentation dit qu'ils devraient être éliminés après la fermeture, mais personnellement, j'aime dire quand se débarrasser de tout ce qui utilise l'interface IDisposable.

1

Si vous n'avez aucune exigence particulière, il est suggéré d'utiliser la classe TcpListener et ses méthodes uniquement ou si vous avez une telle exigence, n'utilisez pas TcpListener et commencez par le socket Raw.

TcpListener classe est auto suffisante pour fournir la méthode comme

Start(), AcceptTcpClient() et Stop().

Vous pouvez créer un List<TcpClient> et passer en boucle sur chaque client, puis appeler le client.close() après avoir appelé Stop() sur l'instance TcpListener.

Un très bon exemple de communication client-serveur est sur MSDN: http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener(v=vs.110).aspx

Cordialement

2

Vous devriez presque toujours laisser TcpListener.Server seul. Il est là pour vous permettre de définir les options de socket, ne pas l'utiliser à des fins de contrôle.

Ainsi, votre Listen devrait être:

private async void Listen() 
{ 
    try 
    { 
     while (true) 
     { 
      Socket socket = await this.server.AcceptSocketAsync(); 
      if (socket == null) 
      { 
       break; 
      } 

      Task.Run(() => this.ClientThread(socket)); 
     } 
    } 
    catch (ObjectDisposedException) 
    { 
     Log.Debug("Media HttpServer closed."); 
    } 
} 

(en supposant que vous voulez en fait un thread par client, une architecture que je ne recommande pas en général).

Lorsque vous êtes prêt à arrêter, arrêtez:

if (this.server != null) 
{ 
    this.server.Stop(); 
} 
-1

sur socket serveur, arrêt & déconnexion est pas nécessaire pour les sockets d'écoute. Ces appels sont nécessaires uniquement pour les sockets connectés. Remplacer l'arrêt de douille avec le code ci-dessous:

if (this.server != null) 
{ 
    if (this.server.Server != null) 
    { 
     this.server.Server.Close(); 
     this.server.Server = NULL; 
    } 

} 
Questions connexes