program story

파이썬에서 연결이 끊어 졌는지 확인하는 방법

inputbox 2020. 12. 24. 23:40
반응형

파이썬에서 연결이 끊어 졌는지 확인하는 방법


내 파이썬 응용 프로그램이 다른 쪽 소켓이 떨어졌을 때 알 수 있기를 바랍니다. 이것에 대한 방법이 있습니까?


"dropped"가 의미하는 바에 따라 다릅니다. TCP 소켓의 경우, 다른 쪽 끝이 close () 또는 프로세스 종료를 통해 연결을 닫으면 파일 끝을 읽거나 읽기 오류가 발생하는 것을 알 수 있습니다. 일반적으로 errno는 'connection reset by 피어 '는 운영 체제에 따라 다릅니다. 파이썬의 경우 길이가 0 인 문자열을 읽거나 소켓에서 읽거나 쓰려고 할 때 socket.error가 발생합니다.


짧은 대답:

비 블로킹 recv () 또는 매우 짧은 시간 제한이있는 차단 recv () / select ()를 사용하십시오.

긴 대답 :

소켓 연결을 처리하는 방법은 필요에 따라 읽거나 쓰고 연결 오류를 처리 할 준비를하는 것입니다.

TCP는 연결을 "삭제"하는 3 가지 형태 (시간 초과, 재설정, 닫기)를 구분합니다.

이 중 시간 초과는 실제로 감지 할 수 없으며 TCP는 시간이 아직 만료되지 않았 음을 알려줄 수 있습니다. 그러나 그것이 당신에게 말했더라도 시간은 여전히 ​​곧 만료 될 수 있습니다.

또한 사용자 또는 피어 (연결의 다른 쪽 끝)가 shutdown ()을 사용하면 들어오는 바이트 스트림 만 닫고 나가는 바이트 스트림을 계속 실행하거나 나가는 스트림을 닫고 들어오는 스트림을 계속 실행할 수 있습니다.

엄밀히 말하면 읽기 스트림이 닫혀 있는지, 쓰기 스트림이 닫혀 있는지 또는 둘 다 닫혀 있는지 확인하고 싶습니다.

연결이 "삭제"된 경우에도 여전히 네트워크 버퍼에있는 데이터를 읽을 수 있어야합니다. 버퍼가 비워진 후에 만 ​​recv ()에서 연결이 끊어집니다.

연결이 끊어 졌는지 확인하는 것은 "현재 버퍼링 된 모든 데이터를 읽은 후 무엇을 받게됩니까?"라고 묻는 것과 같습니다. 이를 확인하려면 현재 버퍼링 된 모든 데이터를 읽어야합니다.

"버퍼링 된 모든 데이터를 읽는 것"이 ​​어떤 사람들에게는 여전히 문제가 될 수 있다는 것을 알 수 있습니다. 여전히 recv ()를 블로킹 함수로 생각합니다. recv ()를 차단하면 버퍼가 이미 비어있을 때 읽기를 "검사"하면 차단되어 "검사"의 목적을 무효화합니다.

내 생각에 전체 프로세스를 무기한으로 차단하도록 문서화 된 모든 기능은 설계상의 결함이지만, 일반 파일 설명자처럼 소켓을 사용하는 것이 멋진 아이디어 였기 때문에 역사적인 이유로 여전히 존재한다고 생각합니다.

할 수있는 일은 다음과 같습니다.

  • 소켓을 비 차단 모드로 설정하지만 수신 버퍼가 비어 있거나 송신 버퍼가 꽉 찼음을 나타내는 시스템 종속 오류가 발생합니다.
  • 차단 모드를 고수하지만 소켓 시간 제한을 매우 짧게 설정하십시오. 이렇게하면 recv ()로 소켓을 "ping"또는 "확인"할 수 있습니다.
  • 매우 짧은 시간 제한으로 select () 호출 또는 asyncore 모듈을 사용하십시오. 오류보고는 여전히 시스템에 따라 다릅니다.

문제의 쓰기 부분의 경우 읽기 버퍼를 비워두면 거의 문제가 해결됩니다. non-blocking 읽기 시도 후에 "dropped"된 연결을 발견 할 것이며, 읽기가 닫힌 채널을 반환 한 후에는 어떤 것도 전송을 중지하도록 선택할 수 있습니다.

전송 된 데이터가 다른 쪽 끝에 도달했는지 확인하는 유일한 방법은 다음 중 하나입니다 (아직 전송 버퍼에 있지 않음).

  • 보낸 정확한 메시지에 대해 동일한 소켓에서 적절한 응답을받습니다. 기본적으로 더 높은 수준의 프로토콜을 사용하여 확인을 제공합니다.
  • 소켓에서 성공적인 shutdow () 및 close () 수행

파이썬 소켓 하우투는 send ()가 채널이 닫히면 쓰여진 0 바이트를 반환한다고 말합니다. non-blocking 또는 timeout socket.send ()를 사용할 수 있으며 0을 반환하면 해당 소켓에서 더 이상 데이터를 보낼 수 없습니다. 그러나 0이 아닌 값을 반환하면 이미 무언가를 보낸 것입니다. 행운을 빕니다 :)

또한 여기에서는 OOB (대역 외) 소켓 데이터를 문제에 접근하는 수단으로 고려하지 않았지만 OOB가 의미하는 바가 아니라고 생각합니다.


Jweede가 게시 한 링크에서 :

예외 socket.timeout :

This exception is raised when a timeout occurs on a socket
which has had timeouts enabled via a prior call to settimeout().
The accompanying value is a string whose value is currently
always “timed out”.

다음은 Python 문서 의 소켓 모듈에 대한 데모 서버 및 클라이언트 프로그램입니다.

# Echo server program
import socket

HOST = ''                 # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
    data = conn.recv(1024)
    if not data: break
    conn.send(data)
conn.close()

그리고 클라이언트 :

# Echo client program
import socket

HOST = 'daring.cwi.nl'    # The remote host
PORT = 50007              # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send('Hello, world')
data = s.recv(1024)
s.close()
print 'Received', repr(data)

내가 가져온 문서 예제 페이지에는이 아이디어를 사용하는 더 복잡한 예제가 있지만 여기에 간단한 대답이 있습니다.

클라이언트 프로그램을 작성한다고 가정하면 소켓이 떨어질 위험이있을 때 소켓을 사용하는 모든 코드를 try 블록 안에 넣으십시오.

try:
    s.connect((HOST, PORT))
    s.send("Hello, World!")
    ...
except socket.timeout:
    # whatever you need to do when the connection is dropped

이 블로그 게시물의 코드 샘플을 Python : 클라이언트가 연결을 닫을 때 감지하는 방법? , 그리고 그것은 나를 위해 잘 작동합니다.

from ctypes import (
    CDLL, c_int, POINTER, Structure, c_void_p, c_size_t,
    c_short, c_ssize_t, c_char, ARRAY
)


__all__ = 'is_remote_alive',


class pollfd(Structure):
    _fields_ = (
        ('fd', c_int),
        ('events', c_short),
        ('revents', c_short),
    )


MSG_DONTWAIT = 0x40
MSG_PEEK = 0x02

EPOLLIN = 0x001
EPOLLPRI = 0x002
EPOLLRDNORM = 0x040

libc = CDLL(None)

recv = libc.recv
recv.restype = c_ssize_t
recv.argtypes = c_int, c_void_p, c_size_t, c_int

poll = libc.poll
poll.restype = c_int
poll.argtypes = POINTER(pollfd), c_int, c_int


class IsRemoteAlive:  # not needed, only for debugging
    def __init__(self, alive, msg):
        self.alive = alive
        self.msg = msg

    def __str__(self):
        return self.msg

    def __repr__(self):
        return 'IsRemoteClosed(%r,%r)' % (self.alive, self.msg)

    def __bool__(self):
        return self.alive


def is_remote_alive(fd):
    fileno = getattr(fd, 'fileno', None)
    if fileno is not None:
        if hasattr(fileno, '__call__'):
            fd = fileno()
        else:
            fd = fileno

    p = pollfd(fd=fd, events=EPOLLIN|EPOLLPRI|EPOLLRDNORM, revents=0)
    result = poll(p, 1, 0)
    if not result:
        return IsRemoteAlive(True, 'empty')

    buf = ARRAY(c_char, 1)()
    result = recv(fd, buf, len(buf), MSG_DONTWAIT|MSG_PEEK)
    if result > 0:
        return IsRemoteAlive(True, 'readable')
    elif result == 0:
        return IsRemoteAlive(False, 'closed')
    else:
        return IsRemoteAlive(False, 'errored')

내가 착각하지 않는다면 이것은 일반적으로 시간 제한을 통해 처리됩니다 .

참조 URL : https://stackoverflow.com/questions/667640/how-to-tell-if-a-connection-is-dead-in-python

반응형