Сервер python SimpleHTTPRequestHandler оставляет сокет в состоянии TIME_WAIT после выхода

У меня написан простой сервер, расширяющий SimpleHTTPRequestHandler.

Если я запускаю и останавливаю его, не делая никаких запросов к серверу, я могу без проблем запустить резервную копию на том же порту.

При запуске netstat выглядит так:

sam@hersheezy:server$ sudo netstat -na --program | grep 8001
tcp        0      0 0.0.0.0:8001            0.0.0.0:*               LISTEN      23392/python

После выполнения запроса netstat выглядит так (даже после завершения запроса):

sam@hersheezy:server$ sudo netstat -na --program | grep 8001
tcp        0      0 0.0.0.0:8001            0.0.0.0:*               LISTEN      23392/python    
tcp        0      0 127.0.0.1:8001          127.0.0.1:48659         TIME_WAIT   -

Затем я убиваю сервер с помощью C-c, и netstat выглядит так (на данный момент я не могу перезапустить сервер, потому что порт уже используется):

 sudo netstat -na --program | grep 8001
tcp        0      0 127.0.0.1:8001          127.0.0.1:48674         TIME_WAIT   - 

Я явно что-то не так закрываю. Мой код, который отправляет ответ, выглядит следующим образом:

"""
reply is an object that can be json encoded that is written with a response code 200
"""
def send_provider_reply(self, replyobj):
    try:
        str_reply = json.dumps(replyobj)
        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        #do we need to send a newline??
        self.wfile.write(str_reply)
    except:
        traceback.print_exc()
        self.send_err(500, 'failed after provider creation')

person Hersheezy    schedule 05.03.2012    source источник


Ответы (1)


Параметр сокета SO_LINGER не позволяет сокету перейти в TIME_WAIT. Но TIME_WAIT существует не просто так: он должен защитить вас от зависших пакетов от старых соединений. Таким образом, продолжительность TIME_WAIT по умолчанию в два раза превышает время прохождения по сети туда и обратно. Таким образом, можно найти старые соединения в течение TIME_WAIT.

Чтобы дать некоторый контекст: на стороне сервера, с прослушивающими сокетами, есть опция сокета SO_REUSEADDR. Это позволяет прослушивающему сокету выполнить привязку до окончания TIME_WAIT. Для серверных процессов, которые всегда должны слушать один и тот же порт (например, веб-сервер на порту 80, 443), это обязательно.

типичный код Python для сервера может содержать что-то вроде этого:

...
listener = socket(AF_INET, SOCK_STREAM)
listener.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
listener.bind((HOST, PORT))
listener.listen(32)
...
person Jörg Beyer    schedule 05.03.2012
comment
Я думаю, что понимаю вас в части защиты от зависших пакетов, но я не совсем понимаю, почему это хорошо: это позволяет прослушивающему сокету успешно выполнить привязку до окончания TIME_WAIT. Я знаю другие серверы, которые можно быстро остановить/запустить (например, nginx), что они делают, чтобы избежать этого? Спасибо! - person Hersheezy; 06.03.2012
comment
Для людей, которые используют SimpleHTTPRequestHandler с SocketServer.TCPServer (я предполагаю, что все), простой способ установить SO_REUSEADDR — сделать это перед инициализацией TCPServer: SocketServer.TCPServer.allow_reuse_address = True - person Guss; 05.09.2019