Использование 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)
Это поможет, если вы вывесили пример кода (уменьшенная его сущностное ядро). Документация модуля 'ssl' Python 2.6 указывает, что передача сокета, возвращаемого функцией wrap_socket(), должна быть выбрана(). –
Да, я действительно работал, я не совсем уверен, что я сделал не так, но я думаю, что это было что-то. Спасибо за ответ, хотя. – Andreas