Instantly detect client disconnection from server

2018-12-31 15:37发布

How can I detect that a client has disconnected from my server?

I have the following code in my AcceptCallBack method

static Socket handler = null;
public static void AcceptCallback(IAsyncResult ar)
{
  //Accept incoming connection
  Socket listener = (Socket)ar.AsyncState;
  handler = listener.EndAccept(ar);
}

I need to find a way to discover as soon as possible that the client has disconnected from the handler Socket.

I've tried:

  1. handler.Available;
  2. handler.Send(new byte[1], 0, SocketFlags.None);
  3. handler.Receive(new byte[1], 0, SocketFlags.None);

The above approaches work when you are connecting to a server and want to detect when the server disconnects but they do not work when you are the server and want to detect client disconnection.

Any help will be appreciated.

13条回答
梦该遗忘
2楼-- · 2018-12-31 16:13

The example code here http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx shows how to determine whether the Socket is still connected without sending any data.

If you called Socket.BeginReceive() on the server program and then the client closed the connection "gracefully", your receive callback will be called and EndReceive() will return 0 bytes. These 0 bytes mean that the client "may" have disconnected. You can then use the technique shown in the MSDN example code to determine for sure whether the connection was closed.

查看更多
还给你的自由
3楼-- · 2018-12-31 16:15

Using the method SetSocketOption, you will be able to set KeepAlive that will let you know whenever a Socket gets disconnected

Socket _connectedSocket = this._sSocketEscucha.EndAccept(asyn);
                _connectedSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);

http://msdn.microsoft.com/en-us/library/1011kecd(v=VS.90).aspx

Hope it helps! Ramiro Rinaldi

查看更多
步步皆殇っ
4楼-- · 2018-12-31 16:16

i had same problem , try this :

void client_handler(Socket client) // set 'KeepAlive' true
{
    while (true)
    {
        try
        {
            if (client.Connected)
            {

            }
            else
            { // client disconnected
                break;
            }
        }
        catch (Exception)
        {
            client.Poll(4000, SelectMode.SelectRead);// try to get state
        }
    }
}
查看更多
宁负流年不负卿
5楼-- · 2018-12-31 16:17

Someone mentioned keepAlive capability of TCP Socket. Here it is nicely described:

http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html

I'm using it this way: after the socket is connected, I'm calling this function, which sets keepAlive on. The keepAliveTime parameter specifies the timeout, in milliseconds, with no activity until the first keep-alive packet is sent. The keepAliveInterval parameter specifies the interval, in milliseconds, between when successive keep-alive packets are sent if no acknowledgement is received.

    void SetKeepAlive(bool on, uint keepAliveTime , uint keepAliveInterval )
    {
        int size = Marshal.SizeOf(new uint());

        var inOptionValues = new byte[size * 3];

        BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0);
        BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, size);
        BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, size * 2);

        socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    }

I'm also using synchronous reading:

socket.BeginReceive(packet.dataBuffer, 0, 128,
                    SocketFlags.None, new AsyncCallback(OnDataReceived), packet);

And in callback, here is caught timeout SocketException, which raises when socket doesn't get ACK signal after keep-alive packet.

public void OnDataReceived(IAsyncResult asyn)
{
    try
    {
        SocketPacket theSockId = (SocketPacket)asyn.AsyncState;

        int iRx = socket.EndReceive(asyn);
    catch (SocketException ex)
    {
        SocketExceptionCaught(ex);
    }
}

This way, I'm able to safely detect disconnection between TCP client and server.

查看更多
伤终究还是伤i
6楼-- · 2018-12-31 16:18

I've found quite useful, another workaround for that!

If you use asynchronous methods for reading data from the network socket (I mean, use BeginReceive - EndReceive methods), whenever a connection is terminated; one of these situations appear: Either a message is sent with no data (you can see it with Socket.Available - even though BeginReceive is triggered, its value will be zero) or Socket.Connected value becomes false in this call (don't try to use EndReceive then).

I'm posting the function I used, I think you can see what I meant from it better:


private void OnRecieve(IAsyncResult parameter) 
{
    Socket sock = (Socket)parameter.AsyncState;
    if(!sock.Connected || sock.Available == 0)
    {
        // Connection is terminated, either by force or willingly
        return;
    }

    sock.EndReceive(parameter);
    sock.BeginReceive(..., ... , ... , ..., new AsyncCallback(OnRecieve), sock);

    // To handle further commands sent by client.
    // "..." zones might change in your code.
}
查看更多
爱死公子算了
7楼-- · 2018-12-31 16:22

This is simply not possible. There is no physical connection between you and the server (except in the extremely rare case where you are connecting between two compuers with a loopback cable).

When the connection is closed gracefully, the other side is notified. But if the connection is disconnected some other way (say the users connection is dropped) then the server won't know until it times out (or tries to write to the connection and the ack times out). That's just the way TCP works and you have to live with it.

Therefore, "instantly" is unrealistic. The best you can do is within the timeout period, which depends on the platform the code is running on.

EDIT: If you are only looking for graceful connections, then why not just send a "DISCONNECT" command to the server from your client?

查看更多
登录 后发表回答