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

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

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

Очень ценю любую помощь.

4
Andreas 6 Июл 2010 в 19:17

2 ответа

Лучший ответ

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

-3
Andreas 14 Июл 2010 в 08:48

Использование сокетов 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)
15
Aleksi Torhamo 26 Авг 2012 в 20:50