Использование сокетов SSL с select()
не так просто, как может показаться на первый взгляд.Хотя они работают нормально с ним в том смысле, что он не выдает ошибку, когда вы ее выдаете, но если вы просто используете их как обычные сокеты, рано или поздно вы столкнетесь с какой-то странностью.
Поскольку select()
нужен дескриптор файла, он получит необработанный сокет.Но даже если сырой сокет становится читаемым, это не значит, что вы получите данные из сокета SSL.Вам нужно будет использовать неблокирующие сокеты (что в любом случае является хорошей идеей при использовании select()
) и просто игнорировать его, если оно выдает SSL_ERROR_WANT_READ
(эквивалент SSL EWOULDBLOCK
).
Другойпроблема в том, что если вы напишите 2048 байт для соединения на другом конце, select()
на вашем конце вернется.Но если вы затем читаете только 1024 байта из сокета SSL, возможно, что сокет SSL внутренне считывает больше данных, и следующий select()
не вернется, даже если будет больше данных для чтения, возможно, заблокирует соединение.Это связано с тем, что необработанный сокет, которым пользуется select()
, не имеет никаких данных, поскольку он уже находится в буферах сокета SSL.
Первое решение, которое приходит на ум, - это читать больше данных.пока чтение не бросит SSL_ERROR_WANT_READ
, таким образом, опустошая буфер.Однако, если другой конец генерирует данные быстрее, чем вы можете их обработать, это приведет к тому, что все остальные соединения будут голодать, пока это соединение не завершит генерацию данных.
Вы можете видеть, сколько буферизованных данных хранится в сокете SSL.звонит sslsock.pending()
.Таким образом, лучшим подходом было бы сначала выполнить одно чтение для некоторого количества данных, проверить количество ожидающих данных и выполнить второе чтение для именно этого количества данных, таким образом, опустошив буфер, не вызывая больше чтений.
Справочная страница для SSL_pending()
(функция C за кулисами) также говорит следующее:
SSL_pending () учитывает только байты из записи TLS / SSL, которая в настоящее время используетсяобработано (если есть).Если установлен флаг read_ahead объекта SSL, возможно, были прочитаны дополнительные байты протокола, содержащие больше записей TLS / SSL;они игнорируются SSL_pending ()
Насколько я понимаю, это означает, что если установлено значение read_ahead
, вам нужно будет повторять второй шаг, пока SSL_pending()
не вернет 0. Япочти уверен, что python не устанавливает 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)