2010-07-06 4 views
4

У меня есть серверное приложение, использующее select.select(), и теперь я пытаюсь добавить к нему SSL, однако при прослушивании «сырых» сокетов я получаю следующую ошибку:select and ssl in python

ValueError: file descriptor cannot be a negative integer (-1) 

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

Действительно оцените любую помощь.

+3

Это поможет, если вы вывесили пример кода (уменьшенная его сущностное ядро). Документация модуля 'ssl' Python 2.6 указывает, что передача сокета, возвращаемого функцией wrap_socket(), должна быть выбрана(). –

+0

Да, я действительно работал, я не совсем уверен, что я сделал не так, но я думаю, что это было что-то. Спасибо за ответ, хотя. – Andreas

ответ

-3

Как указал Мариус, select.select() работает с SSL-сокетами, я до сих пор не знаю, что вызвало мою молчаливую ошибку, но я прыгнул с пистолета, думая, что это SSL + select(). Таким образом, на этот вопрос дан ответ.

10

Использование SSL-сокетов с select() не так просто, как может показаться на первый взгляд. Хотя они работают хорошо с ним в том смысле, что он не бросает ошибку, когда вы ее даете, если вы просто используете их, как обычные сокеты, вы рано или поздно столкнетесь с какой-то странностью.

С select() нужен файловый дескриптор, он получит исходный сокет. Но даже если сырой сокет становится читаемым, это не означает, что вы получите данные из сокета SSL. Вам нужно будет использовать неблокирующие сокеты (это хорошая идея в любом случае при использовании select()) и просто игнорировать его, если он выдает SSL_ERROR_WANT_READ (эквивалент SSL EWOULDBLOCK).

Другая проблема заключается в том, что если вы написали 2048 байт для подключения на другом конце, возвращается select() на вашем конце. Но если вы только читаете 1024 байта из сокета SSL, возможно, что сокет SSL внутренне считывает больше данных, а следующий select() не вернется, хотя будет больше данных для чтения, возможно, для блокировки соединения. Это связано с тем, что raw-сокет, который используется select(), не имеет данных, поскольку он уже находится в буферах SSL-сокета.

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

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

Человек-страница SSL_pending() (функция C негласно) также говорит, что это:

SSL_pending() учитывает только байты из/SSL записи TLS, который в настоящее время обрабатываются (если таковые имеются). Если установлен флаг read_ahead объекта SSL, могут быть прочитаны дополнительные байты протокола, содержащие больше записей TLS/SSL; они игнорируются SSL_pending()

Из того, что я понимаю, это означает, что если read_ahead установлен, вам нужно повторить процедуру второго шага до SSL_pending() возвращает 0. Я уверен, что питон не установлен read_ahead, но лучше быть в безопасности, чем извиняться, поэтому я включил цикл в код примера.

Я не знаком с этим, но что-то, как это должно работать:

# Put the SSL socket to non-blocking mode 
sslsock.setblocking(0) 

while True: 
    r, w, e = select.select([sslsock], [], []) 
    if sslsock in r: 
     try: 
      data = sslsock.recv(1024) 
     except ssl.SSLError as e: 
      # Ignore the SSL equivalent of EWOULDBLOCK, but re-raise other errors 
      if e.errno != ssl.SSL_ERROR_WANT_READ: 
       raise 
      continue 
     # No data means end of file 
     if not data: 
      break 
     # Drain the SSL socket's internal buffer. 
     # If you want to remove the loop, make sure you don't call recv() 
     # with a 0 length, since that could cause a read to the raw socket. 
     data_left = sslsock.pending() 
     while data_left: 
      data += sslsock.recv(data_left) 
      data_left = sslsock.pending() 
     # Process the data 
     process(data)