Search code examples
c#socketsvisual-studio-2013.net-4.5asyncsocket

C# TcpSockets Do I disconnect the clean/proper way?


So i know there are a lot of articles out there on this topic and i did read a lot of them i would say but for some reason im sure my code is not doing what it is supposed to do.

I want to close a connection between my Server and my Client. Now on the serverside i start the disconnect with this code

public void shutdown()
    {
        _socket.Shutdown(SocketShutdown.Both);
        _socket.Close();
    }

And on the Client side i have some troubles understanding how i get to the disconnect what i believe is happening is the following: in my async receive callback i should get an error since the server started a shutdown sequence and i have to handle that (right???) so my code for the client looks like this: ReceiveCallback:

private void ReceiveCallback(IAsyncResult result)
    {
        int bytesRecieved = 0;
        byte[] tempBuff;

        //Try to receive But if a Socket error occures disconnect otherwise start Receiving again
        try
        {
            bytesRecieved = _socket.EndReceive(result);
        }
        catch (SocketException sockEx)
        {
            Disconnect(sockEx);
            return;
        }
        catch (ObjectDisposedException disposeEx)
        {
            Disconnect(disposeEx);
            return;
        }
        catch (Exception ex)
        {
            StartReceive();
            return;
        }

        if (bytesRecieved == 0)
        {
            StartReceive();
            return;
        }

        tempBuff = new byte[bytesRecieved];
        Buffer.BlockCopy(_buffer, 0, tempBuff, 0, bytesRecieved);
        StartReceive();
        _packHandler.handlePacket(tempBuff);

    }

Disconnect:

public void Disconnect()
    {
        if (!_socket.Connected)
        {
            return;
        }
        _socket.BeginDisconnect(false, DisconnectCallback, null);
    }

DisconnectCallback

private void DisconnectCallback(IAsyncResult result)
    {
        _socket.EndDisconnect(result);
        _socket.Close();
    }

(The Disconnect Method is overloaded so if i get an exception it puts up a messageBox and then also calls Disconnect. Just so i know what happened.)

Where am i wrong and what can i improve uppon ???

I tried the code and it seemed to work but i then looked with netstat if all sockets are closed and the client socket was not. It was in FIN_WAIT_2 which means that it (or the server???) did not yet send the FIN packet right ? Oh and then i tried it again with this line changed:

if (bytesRecieved == 0)
        {
            StartReceive();
            return;
        }

TO

if (bytesRecieved == 0)
        {
            Disconnect;
            return;
        }

which then threw an exception on the serverside and on the clientside the client said that the connection was closed by the server ???

EDIT: Even when i have closed both Programs Netstat still shows the port in a WAITING status. what does that tell me ?


Solution

  • Important is:

    • If a server starts a shutdown sequence you DO have to handle it
    • Both sides have to call shutdown on their socket
    • You need a way to notice the disconnect (it does not give you an error, or least it didnt for me)

    Therefor I created my own class customSocket which inherits from Socket

    public class customSocket : Socket
    {
        #region Properties
        private readonly Timer _timer;
        private const int _interval = 1000;
    
        private bool Connected
        {
            get
            {
                bool part1 = Poll(1000, SelectMode.SelectRead);
                bool part2 = (Available == 0);
                if (part1 && part2)
                    return false;
                else 
                    return true;
            }
        }
    
        public bool EventsEnabled
        {
            set
            {
                if (value)
                {
                    _timer.Start();
                }
                else
                    _timer.Stop();
            }
        }
    
        #endregion
    
        #region Constructors
    
        public customSocket(AddressFamily addressFamily, SocketType sockType, ProtocolType protocolType)
            : base(addressFamily, sockType, protocolType)
        {
            _timer = new Timer { Interval = _interval };
            _timer.Elapsed += TimerTick;
        }
    
        public customSocket(SocketInformation sockInfo)
            : base(sockInfo)
        {
            _timer = new Timer { Interval = _interval };
            _timer.Elapsed += TimerTick;
        }
    
        #endregion
    
        #region Events
    
        public event EventHandler<EventArgs> Socket_disconected;
        public void Raise_Socket_disconnected()
        {
            EventHandler<EventArgs> handler = Socket_disconected;
            if (handler != null)
            {
                handler(this,new EventArgs());
            }
        }
    
        private void TimerTick(object sender, EventArgs e)
        {
            if (!Connected)
            {
                Raise_Socket_disconnected();
            }
        }
    
        #endregion
    }
    

    This version of a socket has an Event for a disconnect. Now if you create an instance of your socket class you have to connect the handler and set the EventsEnabled true.

    This handler then calls the shutdown and your socket does not stay in FIN_WAIT_2