Подтвердить что ты не робот

Как узнать, мертв ли ​​соединение в python

Я хочу, чтобы приложение python могло сказать, когда сокет с другой стороны был удален. Есть ли способ для этого?

4b9b3361

Ответ 1

Это зависит от того, что вы подразумеваете под "отбрасыванием". Для сокетов TCP, если другой конец закрывает соединение либо через close() или завершение процесса, вы узнаете, прочитав конец файла или получив ошибку чтения, обычно для переменной errno установлено значение "connection reset by peer" в вашей операционной системе. Для python вы прочтете строку с нулевой длиной или сокет будет запущен при попытке чтения или записи из сокета.

Ответ 2

Короткий ответ:

используйте неблокирующий recv() или блокировку recv()/select() с очень короткий тайм-аут.

Длинный ответ:

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

TCP различает три формы "отбрасывания" соединения: тайм-аут, reset, закрыть.

Из-за этого таймаут не может быть обнаружен, TCP может только сказать, что время еще не истекло. Но даже если бы вы сказали, что время может истечь сразу же.

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

Итак, строго говоря, вы хотите проверить, закрыт ли поток чтения, или если поток записи закрыт, или оба они закрыты.

Даже если соединение было "сброшено", вы все равно сможете читать любые данные, которые все еще находятся в сетевом буфере. Только после того, как буфер пуст, вы получите сообщение об отключении от recv().

Проверка того, что соединение было удалено, похоже на запрос "что я получу после чтения всех данных, которые в настоящее время буферизованы?" Чтобы узнать это, вам просто нужно прочитать все данные, которые в настоящее время буферизованы.

Я вижу, как "чтение всех буферизованных данных", чтобы довести до конца, может быть проблемой для некоторых людей, которые все еще считают recv() как функцию блокировки. С блокировкой recv(), "проверка" для чтения, когда буфер уже пуст, блокирует, что побеждает цель "проверки".

По-моему, любая функция, которая документирована, чтобы потенциально заблокировать весь процесс на неопределенный срок, является недостатком дизайна, но я думаю, что она по-прежнему существует по историческим причинам, поскольку при использовании сокета, как обычный файловый дескриптор, это была крутая идея.

Что вы можете сделать:

  • установите сокет в неблокирующий режим, но вы получите зависящую от системы ошибку, чтобы указать, что буфер приема пуст, или буфер отправки заполнен.
  • придерживаться режима блокировки, но установить очень короткий тайм-аут сокета. Это позволит вам "ping" или "проверить" сокет с recv(), в значительной степени тем, что вы хотите сделать
  • используйте select() call или асинхронный модуль с очень коротким таймаутом. Отчеты об ошибках по-прежнему зависят от системы.

Для части записи проблемы сохранение пустых буферов чтения почти полностью закрывает ее. Вы обнаружите, что соединение "отброшено" после неблокирующей попытки чтения, и вы можете прекратить отправлять что-либо после того, как чтение вернет закрытый канал.

Я предполагаю, что единственный способ убедиться, что ваши отправленные данные достигли другого конца (и еще не находится в буфере отправки):

  • получить правильный ответ в том же сокете для отправленного точного сообщения. В основном вы используете протокол более высокого уровня для подтверждения.
  • выполнить успешные shutdow() и закрыть() в сокете

Соединитель python howto говорит, что send() вернет 0 байтов, если канал закрыт. Вы можете использовать неблокирующий или таймаут socket.send(), и если он вернет 0, вы больше не сможете отправлять данные в этот сокет. Но если он возвращает ненулевое значение, вы уже отправили что-то, удачи в этом:)

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

Ответ 3

Из ссылки Jweede опубликовано:

exception 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 docs

# 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

Ответ 4

Если я не ошибаюсь, это обычно обрабатывается с помощью timeout.

Ответ 5

Я перевел образец кода в этом сообщении блога в 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')