Question Impossible d'arrêter le service Windows TCP asynchrone


J'ai un service Windows du serveur TCP développé en .net 4.0 avec des sockets de serveurs asynchrones.

Cela fonctionne, mais environ 90% du temps, je ne peux tout simplement pas l'arrêter: après avoir appuyé sur le bouton stop dans la console du service Windows, il se bloque et s'arrête après environ une minute, mais le processus continue. Il se bloque à _listener.Close (). La seule chose que je puisse faire pour fermer la communication est de redémarrer Windows. Il peut y avoir quelque chose avec des prises de fermeture. J'ai essayé de comprendre, mais je ne peux tout simplement pas trouver la racine du problème.

Le côté client n’est pas sous mon contrôle, ce sont quelques 100 gadgets qui envoient des données via TCP.

Voici mon code (mis à jour).

Merci beaucoup, toutes les suggestions sont très appréciées!

    public class DeviceTCPServer
{
    public readonly IPAddress IPAddress;
    public readonly int Port;
    public readonly int InputBufferSize;
    public readonly ConcurrentDictionary<Guid, StateObject> Connections;

    public event EventHandler OnStarted;
    public event EventHandler OnStopped;
    public event ServerEventHandler OnConnected;
    public event ServerEventHandler OnDisconnected;
    public event RecievedEventHandler OnRecieved;
    public event DroppedEventHandler OnDropped;
    public event ExceptionEventHandler OnException;
    public event ServerLogEventHandler ServerLog;

    private volatile bool _iAmListening;
    private Socket _listener;
    private Thread _listenerThread;
    private readonly ManualResetEvent _allDone = new ManualResetEvent(false);

    public bool Listening
    {
        get { return _iAmListening; }
    }

    public DeviceTCPServer(IPAddress ipAddress,
                         int port,
                         int inputBufferSize)
    {
        IPAddress = ipAddress;
        Port = port;
        InputBufferSize = inputBufferSize;

        Connections = new ConcurrentDictionary<Guid, StateObject>();
    }

    public void ThreadedStart()
    {
        _listenerThread = new Thread(Start)
        {
            CurrentUICulture = Thread.CurrentThread.CurrentUICulture,
            IsBackground = true
        };

        _listenerThread.Start();
    }

    private void Start()
    {
        try
        {
            var localEP = new IPEndPoint(IPAddress, Port);
            _listener = new Socket(localEP.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            _listener.Bind(localEP);
            _listener.Listen(10000);

            if (OnStarted != null)
                OnStarted(this, new EventArgs());

            _iAmListening = true;

            var listenerWithCultureInfo = new Tuple<Socket, CultureInfo>(_listener,
                                                                         Thread.CurrentThread.CurrentUICulture);

            while (_iAmListening)
            {
                _allDone.Reset();
                _listener.BeginAccept(AcceptCallback, listenerWithCultureInfo);
                _allDone.WaitOne();
            }
        }
        catch (Exception exc)
        {
            Debug.Assert(false, exc.Message);
            if (OnException != null)
                OnException(this, new ExceptionEventArgs(exc, "Start"));
        }
    }

    public void StopListening()
    {
        try
        {
            _iAmListening = false;
            _allDone.Set();
        }
        catch(Exception exc)
        {
            Debug.Assert(false, exc.Message);
            if (OnException != null)
                OnException(this, new ExceptionEventArgs(exc, "StopListening"));
        }
    }

    public void Stop()
    {
        try
        {
            _listener.Close(0);
            CloseAllConnections();
            _listenerThread.Abort();
        }
        catch (Exception exc)
        {
            Debug.Assert(false, exc.Message);
            if (OnException != null)
                OnException(this, new ExceptionEventArgs(exc, "Stop"));
        }
    }

    private void AcceptCallback(IAsyncResult ar)
    {
        var arTuple = (Tuple<Socket, CultureInfo>)ar.AsyncState;
        var state = new StateObject(arTuple.Item2, InputBufferSize);

        try
        {
            Connections.AddOrUpdate(state.Guid,
                                    state,
                                    (k, v) => v);

            Thread.CurrentThread.CurrentUICulture = state.CurrentUICulture;

            var listener = arTuple.Item1;
            var handler = listener.EndAccept(ar);

            _allDone.Set();
            if (!_iAmListening)
                return;

            state.WorkSocket = handler;
            handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
                                 RecieveCallBack, state);

            if (OnConnected != null)
                OnConnected(this, new ServerEventArgs(state));
        }
        catch(ObjectDisposedException)
        {
            _allDone.Set();
            return;
        }
        catch (Exception exc)
        {
            Debug.Assert(false, exc.Message);
            if (OnException != null)
                OnException(this, new ExceptionEventArgs(exc, state, "AcceptCallback"));
        }
    }

    public void RecieveCallBack(IAsyncResult ar)
    {
        var state = (StateObject)ar.AsyncState;

        try
        {
            Thread.CurrentThread.CurrentUICulture = state.CurrentUICulture;

            var handler = state.WorkSocket;
            var read = handler.EndReceive(ar);

            var pBinayDataPocketCodecStore = new BinayDataPocketCodecStore();

            if (read > 0)
            {
                state.LastDataReceive = DateTime.Now;

                var data = new byte[read];
                Array.Copy(state.Buffer, 0, data, 0, read);
                state.AddBytesToInputDataCollector(data);

                //check, if pocket is complete
                var allData = state.InputDataCollector.ToArray();
                var codecInitRes = pBinayDataPocketCodecStore.Check(allData);

                if (codecInitRes.Generic.Complete)
                {
                    if (!codecInitRes.Generic.Drop)
                    {
                        if (OnRecieved != null)
                            OnRecieved(this, new RecievedEventArgs(state, allData));
                    }
                    else
                    {
                        if (OnDropped != null)
                            OnDropped(this, new DroppedEventArgs(state, codecInitRes.Generic));

                        //get new data
                        state.ResetInputDataCollector();

                        handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
                                             RecieveCallBack, state);
                    }
                }
                else
                {
                    //get more data
                    handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
                                         RecieveCallBack, state);
                }
            }
            else
            {
                if ((handler.Connected == false) || (handler.Available == 0))
                {
                    Close(state);
                }
            }
        }
        catch (Exception exc)
        {
            Debug.Assert(false, exc.Message);
            if (OnException != null)
                OnException(this, new ExceptionEventArgs(exc, state, "RecieveCallBack"));
        }
    }

    public void Send(StateObject state, byte[] data)
    {
        try
        {
            var handler = state.WorkSocket;

            handler.BeginSend(data, 0, data.Length, 0,
                              SendCallback, state);
        }
        catch (Exception exc)
        {
            Debug.Assert(false, exc.Message);
            if (OnException != null)
                OnException(this, new ExceptionEventArgs(exc, state, "Send"));
        }
    }

    private void SendCallback(IAsyncResult ar)
    {
        var state = (StateObject)ar.AsyncState;

        try
        {
            Thread.CurrentThread.CurrentUICulture = state.CurrentUICulture;
            var handler = state.WorkSocket;

            handler.EndSend(ar);
            handler.BeginReceive(state.Buffer, 0, state.InputBufferSize, 0,
                                 RecieveCallBack, state);
        }
        catch (Exception exc)
        {
            Debug.Assert(false, exc.Message);
            if (OnException != null)
                OnException(this, new ExceptionEventArgs(exc, state, "SendCallback"));
        }
    }

    public void Close(StateObject state)
    {
        try
        {
            if (state == null)
                return;

            var handler = state.WorkSocket;

            if (handler == null)
                return;

            if (!handler.Connected)
                return;

            if (handler.Available > 0)
            {
                var data = new byte[handler.Available];
                handler.Receive(data);
            }

            handler.Shutdown(SocketShutdown.Both);
            handler.Close(0);

            if (OnDisconnected != null)
                OnDisconnected(this, new ServerEventArgs(state));

            StateObject removed;
            var removeResult = Connections.TryRemove(state.Guid, out removed);
        }
        catch (Exception exc)
        {
            Debug.Assert(false, exc.Message);
            if (OnException != null)
                OnException(this, new ExceptionEventArgs(exc, "Close"));
        }
    }

    private void CloseAllConnections()
    {
        try
        {
            var connections = Connections.Select(c => c.Value);

            foreach(var connection in connections)
            {
                Close(connection);
            }
        }
        catch(Exception exc)
        {
            Debug.Assert(false, exc.Message);
            if (OnException != null)
                OnException(this, new ExceptionEventArgs(exc, "CloseAllConnections"));
        }
    }

    public override string ToString()
    {
        return string.Format("{0}:{1}", IPAddress, Port);
    }
}

12
2017-07-27 14:19


origine


Réponses:


Comme il s'agit d'un service, la règle habituelle "Attendre les threads autres que l'arrière-plan pour quitter" ne s'applique pas, et il vous incombe de tuer les travailleurs (ce qui peut inclure des opérations asynchrones en attente). À présent; en ce moment, vous tuez le auditeur, mais cela empêche seulement Nouveau prises de fixation. Vous devriez idéalement garder une trace de vos clients quelque part, de sorte que vous pouvez également tuer tous les client prises de courant. Quand tu as fini cette, ça devrait aller. N'oubliez pas de synchroniser l'accès à toute collection de clients ou d'utiliser un type de collecte sécurisé pour les threads.


4
2017-07-27 14:26



Je pense que vous devriez utiliser le Socket.Shutdown méthode avant de fermer le socket. Comme il est indiqué dans la documentation:

Lorsque vous utilisez un socket orienté connexion, appelez toujours la méthode Shutdown avant de fermer le socket. Cela garantit que toutes les données sont envoyées et reçues sur le socket connecté avant sa fermeture.

De plus, je me trompe peut-être, mais je ne vois aucun appel à la méthode StopListening (). Depuis lors, le champ _iAmListening n'est pas défini sur false et le rappel (AcceptCallback (IAsyncResult ar)) répond toujours aux appels entrants.


0
2017-08-03 15:03



À un bref aperçu de votre code et commentaire J'ai remarqué deux choses.

Je n'ai pas pu le résoudre. Tout ce que j'essaie, il se bloque à _listener.Close () ;. Il ne ferme tout simplement pas la prise de l'auditeur, se bloque pour toujours. Même il n'y a pas de connexions (en vérifiant également la commande netstat ...)

La première chose à faire est d'appeler StopListening(), dans Close(StateObject state) ou ailleurs dans le programme, avant de fermer le socket. Nettoyez méthodiquement toutes les ressources en démantelant les appels effectués pour cet objet.

En outre (sans connaître votre flux de travail), si le temps est compté lors de la fermeture du socket, ne continuez pas à accepter plus de données dans Close(StateObject state). Bien sûr, cela ne sera peut-être pas possible si les données doivent être récupérées dans cette analyse.

if (handler.Available > 0)
{
    var data = new byte[handler.Available];
    handler.Receive(data);
}

0
2017-08-05 14:50



Donc, enfin, le problème est résolu, il n'y avait pas de problème avec mon service Windows!

Cela a été causé par une mise à jour de Microsoft Windows:

Microsoft KB4338815, qui a provoqué le blocage définitif du support de fermeture sur les processeurs Intel Xeon: https://forum.filezilla-project.org/viewtopic.php?t=49308

Merci à tous vos efforts pour essayer de m'aider!


0
2017-08-15 12:59