Как обработать сломанную трубу (SIGPIPE) в Python?

StackOverflow https://stackoverflow.com/questions/180095

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

PS: Этот вопрос / ответ связан с проблема в общем виде; как конкретно это решить?

Это было полезно?

Решение

Прочитайте инструкцию try:

try:
    # do something
except socket.error, e:
    # A socket error
except IOError, e:
    if e.errno == errno.EPIPE:
        # EPIPE error
    else:
        # Other error

Другие советы

Предполагая, что вы используете стандартный модуль сокетов, вы должны перехватить исключение socket.error: (32, 'Broken pipe') (не IOError, как предлагали другие). Это будет затронуто в случае, который вы описали, то есть отправка / запись в сокет, для которого удаленная сторона отключилась.

import socket, errno, time

# setup socket to listen for incoming connections
s = socket.socket()
s.bind(('localhost', 1234))
s.listen(1)
remote, address = s.accept()

print "Got connection from: ", address

while 1:
    try:
        remote.send("message to peer\n")
        time.sleep(1)
    except socket.error, e:
        if isinstance(e.args, tuple):
            print "errno is %d" % e[0]
            if e[0] == errno.EPIPE:
               # remote peer disconnected
               print "Detected remote disconnect"
            else:
               # determine and handle different error
               pass
        else:
            print "socket error ", e
        remote.close()
        break
    except IOError, e:
        # Hmmm, Can IOError actually be raised by the socket module?
        print "Got IOError: ", e
        break

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

Вы можете уменьшить (но не полностью исключить) это, используя select.select () (или poll ). Проверьте, готовы ли данные для чтения с однорангового узла, прежде чем пытаться выполнить запись. Если select сообщает, что есть данные, доступные для чтения из однорангового сокета, прочитайте их, используя socket.recv () . Если это возвращает пустую строку, удаленный узел закрыл соединение. Поскольку здесь все еще есть условие гонки, вам все равно нужно поймать и обработать исключение.

Twisted отлично подходит для такого рода вещей, однако, похоже, вы уже написали немало кода.

SIGPIPE (хотя я думаю, что вы имеете в виду EPIPE ?) возникает в сокетах, когда вы выключаете сокет и затем отправляете в него данные. Простое решение - не закрывать сокет перед тем, как отправлять данные. Это также может происходить на каналах, но это не похоже на то, что вы испытываете, поскольку это сетевой сервер.

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

Конечно, если вы использовали Twisted вместо того, чтобы создавать новый поток для каждого клиентского подключения, вы, вероятно, не захотите У меня нет этой проблемы. Действительно трудно (возможно, невозможно, в зависимости от вашего приложения) правильно упорядочить операции закрытия и записи, если несколько потоков имеют дело с одним и тем же каналом ввода / вывода.

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

$ packet_write_wait: Connection to 10.. port 22: Broken pipe

Второй раз это работает:

[1]   Done                    nohup python -u add_asc_dec.py > add2.log 2>&1

Я думаю, причина может быть в текущей серверной среде.

Мой ответ очень близок к ответу С. Лотта, за исключением того, что я бы сказал более конкретно:

try:
    # do something
except IOError, e:
    # ooops, check the attributes of e to see precisely what happened.
    if e.errno != 23:
        # I don't know how to handle this
        raise

где " 23 " это номер ошибки, который вы получаете из EPIPE. Таким образом, вы не будете пытаться обработать ошибку прав доступа или что-то еще, к чему вы не готовы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top