Обнаружение отключения клиента TCP

Скажем, я запускаю простой сервер и accept() выдает соединение с клиентом.

Каков наилучший способ сказать, когда клиент отключился? Обычно клиент должен отправить команду закрытия, но что, если он отключается вручную или вообще теряет сетевое соединение? Как сервер может обнаружить или обработать это?

Ответ 1

select (с набором маски чтения) вернется с указателем, но если вы используете ioctl * для проверки количества байтов, ожидающих чтения, оно будет равно нулю. Это признак отключения разъема.

Это отличная дискуссия по различным методам проверки того, что клиент отключился: Стивен Клири, обнаружение полуоткрытых (выпадающих) соединений.

* для Windows используется ioctlsocket.

Ответ 2

В TCP существует только один способ обнаружения упорядоченного разъединения, а это значит, что при чтении он получает нуль в качестве возвращаемого значения из read()/recv()/recvXXX().

Существует также один надежный способ обнаружения сломанного соединения: путем записи на него. После достаточного количества записей в сломанном соединении TCP выполнит достаточно повторений и тайм-аутов, чтобы знать, что он сломан, и в конечном итоге приведет к тому, что write()/send()/sendXXX() вернет -1 с errno/WSAGetLastError() значением ECONNRESET, или в некоторых случаях время ожидания соединения ". Обратите внимание, что последний отличается от" таймаута соединения", который может возникать в фазе подключения.

Вы также должны установить разумный тайм-аут чтения и сбросить соединения, которые не удались.

Ответ здесь о ioctl() и FIONREAD - состязательный вздор. Все, что делает, это указать, сколько байтов в настоящее время находится в буфере приема сокета, доступное для чтения без блокировки. Если клиент не отправляет вам что-либо в течение пяти минут, которое не является отключением, но оно вызывает FIONREAD равным нулю. Не то же самое: даже не закрывай.

Ответ 3

Чтобы развернуть это немного больше:

Если вы используете сервер, вам нужно либо использовать TCP_KEEPALIVE для мониторинга клиентских подключений, либо сделать что-то подобное себе, либо получить информацию о том, какие данные/протокол вы используете для соединения.

В принципе, если соединение будет убито (т.е. неправильно закрыто), сервер не заметит, пока он не попытается что-то написать клиенту, и это то, что для вас выполняет keepalive. В качестве альтернативы, если вы знаете протокол лучше, вы можете просто отключиться от таймаута бездействия.

Ответ 4

Если вы используете перекрывающиеся (то есть асинхронные) операции ввода-вывода с процедурами завершения или портами завершения, вы будете немедленно уведомлены (если у вас есть замечательное чтение), когда клиентская сторона закрывает соединение.

Ответ 5

"""
tcp_disconnect.py
Echo network data test program in python. This easily translates to C & Java.

A server program might want to confirm that a tcp client is still connected 
before it sends a data. That is, detect if its connected without reading from socket.
This will demonstrate how to detect a TCP client disconnect without reading data.

The method to do this:
1) select on socket as poll (no wait)
2) if no recv data waiting, then client still connected
3) if recv data waiting, the read one char using PEEK flag 
4) if PEEK data len=0, then client has disconnected, otherwise its connected.
Note, the peek flag will read data without removing it from tcp queue.

To see it in action: 0) run this program on one computer 1) from another computer, 
connect via telnet port 12345, 2) type a line of data 3) wait to see it echo, 
4) type another line, 5) disconnect quickly, 6) watch the program will detect the 
disconnect and exit.

John Masinter, 17-Dec-2008
"""

import socket
import time
import select

HOST = ''       # all local interfaces
PORT = 12345    # port to listen

# listen for new TCP connections
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)
# accept new conneciton
conn, addr = s.accept()
print 'Connected by', addr
# loop reading/echoing, until client disconnects
try:
    conn.send("Send me data, and I will echo it back after a short delay.\n")
    while 1:
        data = conn.recv(1024)                          # recv all data queued
        if not data: break                              # client disconnected
        time.sleep(3)                                   # simulate time consuming work
        # below will detect if client disconnects during sleep
        r, w, e = select.select([conn], [], [], 0)      # more data waiting?
        print "select: r=%s w=%s e=%s" % (r,w,e)        # debug output to command line
        if r:                                           # yes, data avail to read.
            t = conn.recv(1024, socket.MSG_PEEK)        # read without remove from queue
            print "peek: len=%d, data=%s" % (len(t),t)  # debug output
            if len(t)==0:                               # length of data peeked 0?
                print "Client disconnected."            # client disconnected
                break                                   # quit program
        conn.send("-->"+data)                           # echo only if still connected
finally:
    conn.close()

Ответ 7

В протоколе TCP есть "открытые" и "закрытые" процедуры. После "открытия" соединение удерживается до "закрытого". Но есть много вещей, которые могут остановить поток данных ненормально. При этом методы определения того, можно ли использовать ссылку, сильно зависят от уровней программного обеспечения между протоколом и прикладной программой. Те, о которых упоминалось выше, сосредоточены на программиста, пытающегося использовать сокет неинвазивным способом (чтение или запись 0 байтов), возможно, являются наиболее распространенными. Некоторые слои в библиотеках будут предоставлять "опрос" для программиста. Например, вызовы Win32 asych (delayed) могут начать чтение, которое будет возвращаться без ошибок и 0 байтов, чтобы сигнализировать сокет, который больше не может быть прочитан (предположительно, процедура TCP FIN). В других средах могут использоваться "события", как определено в их обертывающих слоях. На этот вопрос нет единого ответа. Механизм обнаружения, когда сокет не может использоваться и должен быть закрыт, зависит от оберток, поставляемых в библиотеках. Также стоит отметить, что сами сокеты могут быть повторно использованы слоями под библиотекой приложений, поэтому разумно разобраться, как ваша среда работает с интерфейсом Berkley Sockets.

Ответ 8

Возвращаемое значение приема будет равным -1, если соединение потеряно иначе, это будет размер буфера.

void ReceiveStream(void *threadid)
{
    while(true)
    {
        while(ch==0)
        {
            char buffer[1024];
            int newData;
            newData = recv(thisSocket, buffer, sizeof(buffer), 0);
            if(newData>=0)
            {
                std::cout << buffer << std::endl;
            }
            else
            {
                std::cout << "Client disconnected" << std::endl;
                if (thisSocket)
                {
                    #ifdef WIN32
                        closesocket(thisSocket);
                        WSACleanup();
                    #endif
                    #ifdef LINUX
                        close(thisSocket);
                    #endif
                }
                break;
            }
        }
        ch = 1;
        StartSocket();
    }
}

Ответ 9

apr-библиотека из проекта apache является хорошей ссылкой для этой проблемы. Он использует опрос с тайм-аутом, чтобы проверить, нарушено ли другое боковое соединение или нет.

Ответ 10

Я поиграл с несколькими решениями, но, похоже, он работает лучше всего для обнаружения отключения хоста и/или клиента в Windows. Он предназначен для неблокирующих сокетов и получен из примера IBM.

char buf;
int length=recv(socket, &buf, 0, 0);
int nError=WSAGetLastError();
if(nError!=WSAEWOULDBLOCK&&nError!=0){
    return 0;
}   
if (nError==0){
    if (length==0) return 0;
}

Ответ 11

Это очень легко сделать: надежный и не беспорядочный:

        Try
            Clients.Client.Send(BufferByte)
        Catch verror As Exception
            BufferString = verror.ToString
        End Try
        If BufferString <> "" Then
            EventLog.Text &= "User disconnected: " + vbNewLine
            Clients.Close()
        End If

Ответ 12

Мы сталкиваемся с подобной проблемой при обнаружении удаления кабеля на ПК. После googling мы попали в SuperCom для библиотеки TCP, которая предложила эту функцию, и очень надежную библиотеку передачи данных, которая также могла обрабатывать события отчетности, когда соединение было закрыто.

Ответ 13

Этот вопрос основан на различных заблуждениях о сетевом стеке.

Прежде всего, нет понятия "соединение" Tcp или иначе, и чем раньше вы сделаете это различие, тем скорее вы поймете, что это не отличается от udp или любого другого протокола.

Когда вы получаете время от времени, у вас либо есть данные для отправки, либо нет.

Если вы не хотите передавать, чем, необязательно увеличивайте время ожидания и получаете снова.

Если клиент отключил компьютер или иным образом отключился, то как и почему это имеет значение?

Разве это сломает ваше "соединение"? Может быть, но, возможно, нет, кто знает, как настроен ваш компьютер или LSP такого.

Обычный способ сделать это аналогичен любым другим протоколам, и я напоминаю вам, что сокет уже "подключен" в случаях, когда происходит регенерация.

При попытке отправить этому клиенту что-то, что вы узнаете, если клиент "подключен" или более правильно подтвердил ваши данные под Tcp. (Под udp также зависит от сети через icmp)

Почему вы проголосовали за этот ответ из-за своего собственного недоразумения.

Короче говоря, если у вас есть данные для отправки, отправьте его и определите, какие действия необходимы после отправки успешной или неудачной.

Если время срабатывания вашей операции одинаково, и, возможно, вы можете использовать отправку размера 0, чтобы подтвердить, что другая сторона все еще принимает ваши данные.