Development Tip

TCP 클라이언트 연결 끊김 감지

yourdevel 2020. 11. 11. 20:45
반응형

TCP 클라이언트 연결 끊김 감지


간단한 서버를 실행 중이고 accept()클라이언트에서 연결을 했다고 가정 해 보겠습니다 .

클라이언트 연결이 끊겼을 때 알 수있는 가장 좋은 방법은 무엇입니까? 일반적으로 클라이언트는 닫기 명령을 보내야하지만 수동으로 연결이 끊어 지거나 네트워크 연결이 완전히 끊어지면 어떻게됩니까? 서버는이를 어떻게 감지하거나 처리 할 수 ​​있습니까?


select (읽기 마스크 세트 포함)는 신호가 지정된 핸들과 함께 반환되지만 ioctl *을 사용하여 읽기 대기중인 바이트 수를 확인하면 0이됩니다. 이것은 소켓이 분리되었다는 신호입니다.

이것은 클라이언트가 연결 해제되었는지 확인하는 다양한 방법에 대한 훌륭한 토론입니다 : Stephen Cleary, Detection of Half-Open (Dropped) Connections .

* Windows의 경우 ioctlsocket을 사용하십시오.


TCP에서는 순서대로 연결 해제를 감지하는 한 가지 방법이 있습니다. 즉, read()/recv()/recvXXX()읽을 때 반환 값으로 0을 가져 오는 것 입니다.

끊어진 연결을 감지하는 신뢰할 수있는 방법은 쓰기를 통해 확인하는 것입니다. 끊어진 연결에 충분한 쓰기를 수행 한 후 TCP는 끊어 졌음을 알기에 충분한 재시도 및 시간 초과를 수행하고 결국 '연결 시간 초과' 값으로 write()/send()/sendXXX()-1을 반환하게 됩니다. 후자는 연결 단계에서 발생할 수있는 '연결 시간 초과'와 다릅니다.errno/WSAGetLastError()ECONNRESET,

또한 적절한 읽기 시간 제한을 설정하고 실패한 연결을 삭제해야합니다.

여기에 대한 대답 ioctl()과는 FIONREAD경쟁의 넌센스입니다. 하는 일은 소켓 수신 버퍼에 현재 얼마나 많은 바이트가 블록킹없이 읽을 수 있는지 알려주는 것입니다. 클라이언트가 연결 해제를 구성하지 않는 5 분 동안 아무것도 보내지 않으면 FIONREAD0이됩니다. 똑같은 것이 아닙니다. 가까이에 있지도 않습니다.


이것을 조금 더 확장하려면 :

서버를 실행하는 경우 TCP_KEEPALIVE를 사용하여 클라이언트 연결을 모니터링하거나 직접 비슷한 작업을 수행하거나 연결을 통해 실행중인 데이터 / 프로토콜에 대한 지식이 있어야합니다.

기본적으로 연결이 끊어지면 (즉, 제대로 닫히지 않은 경우) 서버는 클라이언트에 무언가를 쓰려고 할 때까지 알아 차리지 못합니다. 이것이 keepalive가 당신을 위해 달성하는 것입니다. 또는 프로토콜을 더 잘 알고있는 경우 어쨌든 비활성 시간 초과시 연결을 끊을 수 있습니다.


완료 루틴 또는 완료 포트와 함께 중첩 된 (즉, 비동기식) I / O를 사용하는 경우 클라이언트 측이 연결을 닫을 때 즉시 알림을받습니다 (미해결 읽기가 있다고 가정).


TCP에는 프로토콜에 "열기"및 "닫기"절차가 있습니다. 일단 "개방"되면 연결은 "닫힘"까지 유지됩니다. 그러나 데이터 흐름을 비정상적으로 멈출 수있는 많은 것들이 있습니다. 즉, 링크를 사용할 수 있는지 여부를 결정하는 기술은 프로토콜과 응용 프로그램 사이의 소프트웨어 계층에 크게 의존합니다. 위에서 언급 한 것들은 비 침습적 인 방식 (0 바이트 읽기 또는 쓰기)으로 소켓을 사용하려는 프로그래머에게 초점을 맞추는 것이 아마도 가장 일반적 일 것입니다. 라이브러리의 일부 레이어는 프로그래머에게 "폴링"을 제공합니다. 예를 들어, Win32 asych (지연된) 호출은 오류없이 반환되는 읽기를 시작하고 더 이상 읽을 수없는 소켓 (아마도 TCP FIN 프로 시저)을 신호하기 위해 0 바이트를 반환 할 수 있습니다. 다른 환경에서는 "이벤트"를 사용할 수 있습니다. 래핑 레이어에 정의 된대로. 이 질문에 대한 답은 하나도 없습니다. 소켓을 사용할 수없고 닫아야하는 경우를 감지하는 메커니즘은 라이브러리에 제공된 래퍼에 따라 다릅니다. 또한 소켓 자체는 애플리케이션 라이브러리 아래의 계층에서 재사용 할 수 있으므로 환경이 Berkley Sockets 인터페이스를 처리하는 방식을 파악하는 것이 좋습니다.


"""
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()

Try looking for EPOLLHUP or EPOLLERR. How do I check client connection is still alive

Reading and looking for 0 will work in some cases, but not all.


The return value of receive will be -1 if connection is lost else it will be size of buffer.

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();
    }
}

apr library from apache project is a good reference for this problem. It use poll with a timeout value to check if the other side connection is broken or not.


I toyed with a few solutions but this one seems to work best for detecting host and/or client disconnection in Windows. It is for non-blocking sockets, and derived from IBM's example.

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;
}

It's really easy to do: reliable and not messy:

        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

We run in similar issue when detecting the cable removal on the PC was the issue. After googling we hit on the SuperCom for TCP library that offered this feature and a very reliable data communication library that could also handle reporting events when a connection was closed.

참고URL : https://stackoverflow.com/questions/283375/detecting-tcp-client-disconnect

반응형