Сервер/клиентский протокол сокета python с нестабильным подключением к клиенту

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

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

Теперь, потому что клиент будет на мобильном телефоне, таким образом, нестабильное соединение я оставлю с открытыми потоками, которые больше не связаны, и поскольку цикл начинается с recv, довольно сложно сломать потерянное соединение таким образом.

Я думал о добавлении отправки перед recv, чтобы проверить, является ли соединение еще живым, но это может вообще не помогло, если клиент отключится после моей отказоустойчивой отправки, поскольку клиент отправляет поток данных каждые 5 секунд.

Я заметил, что recv будет ломаться иногда, но не всегда, и в тех случаях я остаюсь с зомби-потоками, используя ресурсы.

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

Может ли кто-нибудь указать мне в правильном направлении на хороший способ решить эту проблему?

Примеры кода:

СЛУШАТЕЛЬ:

serversocket = socket(AF_INET, SOCK_STREAM)
serversocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
serversocket.bind(addr)
serversocket.listen(2)
logg("Binded to port: " + str(port))

# Listening Loop
while 1:
  clientsocket, clientaddr = serversocket.accept()
  threading.Thread(target=handler, args=(clientsocket, clientaddr,port,)).start()

# This is useless as it will never get here
serversocket.close()

Handler:

  # Socket connection handler (Threaded)
  def handler(clientsocket, clientaddr, port):
    clientsocket.settimeout(15)

    # Loop till client closes connection or connection drops
    while 1:
      stream = ''
      while 1:
        ending = stream[-6:] # get stream ending
        if ending == '.$$$$.':
          break

        try:
          data = clientsocket.recv(1)
        except:
          sys.exit()

        if not data:
          sys.exit()
          # this is the usual point where thread is closed when a client closes connection normally

        stream += data

      # Clear the line ending
      stream = base64.b64encode(stream[:-6])

      # Send data to be processed
      re = getreply(stream)

      # Send response to client
      try:
        clientsocket.send(re + str('.$$$$.'))
      except:
        sys.exit()

Как вы можете видеть, есть три условия, по крайней мере, один должен запускать exit, если соединение терпит неудачу, но иногда они этого не делают.

Ответ 1

Извините, но я думаю, что идея threaded в этом случае не очень хорошая. Поскольку вам не нужно обрабатывать/делать много вещей в этих потоках (рабочие?), И большую часть времени эти потоки ждут сокета (это операция блокировки, не так ли?) Я бы посоветовал прочитать о управляемое событиями программирование. Согласно сокетам этот шаблон чрезвычайно полезен, потому что вы можете делать все в одном потоке. Вы общаетесь с одним сокетом за раз, но остальные соединения просто ждут данных, поэтому почти нет потерь. Когда вы отправляете несколько байтов, вы просто проверяете, что, возможно, другое соединение требует переноса. Вы можете прочитать о select и epoll.

В python есть несколько библиотек, которые можно сыграть с этим:

В некоторых проектах я использовал торнадо, и эта задача очень хороша. Libev тоже хорош, но является c-оберткой, поэтому немного low-level (но очень приятно для некоторых задач).

Ответ 2

Таким образом, вы должны использовать socket.settimeout(float) с clientsocket как один из предложенных комментариев.

Причина, по которой вы не видите разницы, заключается в том, что при вызове socket.recv(bufsize[, flags]) и истечении времени ожидания генерируется исключение socket.timeout, и вы получаете это исключение и выходите.

try:
      data = clientsocket.recv(1)
except:
      sys.exit()

должно быть что-то вроде:

try:
      data = clientsocket.recv(1)
except timeout:
      #timeout occurred
      #handle it
      clientsocket.close()
      sys.exit()