С Новым годом! Форум программистов, компьютерный форум, киберфорум
C++: Сети
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.94/18: Рейтинг темы: голосов - 18, средняя оценка - 4.94
32 / 30 / 4
Регистрация: 01.02.2014
Сообщений: 878
1

Как правильно использовать SSL_read при условии неблокируемого сокета

12.01.2020, 16:42. Показов 3594. Ответов 8
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
В мануале написано
https://www.openssl.org/docs/m... _read.html

C++ (Qt)
1
If the underlying BIO is non-blocking, SSL_read() will also return when the underlying BIO could not satisfy the needs of SSL_read() to continue the operation. In this case a call to SSL_get_error(3) with the return value of SSL_read() will yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. As at any time a re-negotiation is possible, a call to SSL_read() can also cause write operations!
что при неблокируемом сокете будут возникать ошибки, мол он не смог считать так как не успели придти данные или они пришли но в недостаточном количестве для расшифровки. Если такая ошибка выявлена то функцию SSL_read() нужно вызывать заново.

И в конечном итоге картина выглядит примерно следующим образом

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
while(true){
    if(select()){
        if( SSL_read() ){
        break;
        }else if( ошибка == SSL_ERROR_WANT_READ){
            continue;
        }else{
            return 0;
        }
    }
}
Но при таком варианте есть 2 проблемы:

1) select в данном случае абсолютно бесполезен, так как даже если данные для считывания есть, то SSL_read() все равно может возвращать ошибку SSL_ERROR_WANT_READ, так как имеющихся данных ему может не хватить.
2) При условии медленного интернета или медленного сайта, на открытие одной страницы количество срабатываний continue; может доходить до 1000 и более итераций, что весьма неплохо грузит процессор.

Единственный выход который я нашел это сделать перед continue; слип на 10 миллисекунд, но уверен что такая реализация равносильна идиотизму, подскажите как реализовать чтение правильно? нигде в гугле нормального примера не нашел.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
12.01.2020, 16:42
Ответы с готовыми решениями:

Как правильно использовать логические операции в условии оператора if?
Как мне правильно применить && ??? )) using namespace std; enum lamp{on,off}; int main(int...

Как найти для этих условии 2 парных чисел а и b при котором выполняется все условии?
Мой пример кода был таким данный момент но не работал. В экране пустота. Ничего не выводится. Где у...

как объявить правильно tr и как его использовать при вызове функции?
main.cpp case 8: cout << "Firs trapec # is: "; cin >> k; k = k - 1; cout <<...

Как правильно создать объект сокета?
QTcpSocket* socket=new QTcpSocket(); На эту строчку ругается компилятор так: error C2512:...

8
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
12.01.2020, 19:30 2
Цитата Сообщение от mh-coder Посмотреть сообщение
картина выглядит примерно следующим образом
В таком виде - это и есть правильное чтение. В общих чертах.

А вот не работать нормально это может у вас по разным причинам. Нюансов много, если не работает, значит ошибка в этих нюансах.

Чтобы конкретно что-то сказать, надо видеть как именно у вас организован процесс.
0
32 / 30 / 4
Регистрация: 01.02.2014
Сообщений: 878
15.01.2020, 14:18  [ТС] 3
Цитата Сообщение от DrOffset Посмотреть сообщение
В таком виде - это и есть правильное чтение. В общих чертах.

А вот не работать нормально это может у вас по разным причинам. Нюансов много, если не работает, значит ошибка в этих нюансах.

Чтобы конкретно что-то сказать, надо видеть как именно у вас организован процесс.
Организован также как и в примере выше, вот более конкретный код
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
bool waitForReadyRead(){
    if(lp_current_conection == nullptr){
        return 0;
    }
    ResetEvent(hEventRead);
    if(WSAEventSelect(lp_current_conection->handleSocket,  hEventRead,  FD_READ )){
        return 0;
    }
 
    tempDRORD = WSAWaitForMultipleEvents(2, hEvents, false, DWORD(ReadWriteTimeout*1000), true );
 
    if(WSAEventSelect(lp_current_conection->handleSocket,  hEventRead,  0 )){
        return 0;
    }
    if ((tempDRORD == WSA_WAIT_FAILED) || (tempDRORD == WSA_WAIT_TIMEOUT)){
        return 0;
    }
    if(tempDRORD == 0){
        return 1;
    }
    if(tempDRORD == 1){
        ResetEvent(hEventStop);
    }
    return 0;
 
}
bool RecvDataHttp(QByteArray& data){
 
 for(int z = 0;z<1000;z++){
        ZeroMemory(mRecvData,SizeRequest);
 
        if (!waitForReadyRead()) // select
        {
          lp_current_conection->changeSocketState(BLOCKED_SOCKET);
          return 0;
        }
 
            size_recv = SSL_read(lp_current_conection->lp_ssl,  mRecvData, SizeRequest-1);
            if(size_recv <= 0){
                size_recv = SSL_get_error(lp_current_conection->lp_ssl, size_recv);
                if(size_recv == SSL_ERROR_WANT_READ){
 
 
                    count_cycle++;
                    qDebug()<<"count_cycle = "<<count_cycle;
                    ResetEvent(hEventStop);
                    WaitForSingleObject(hEventStop,DWORD(10*count_cycle));
                    ResetEvent(hEventStop);
                    continue;
                    
                }else if(size_recv == SSL_ERROR_NONE){
                    break;
                }else{
                    if(!lp_current_conection->changeSocketState(BLOCKED_SOCKET)){
                        return 0;
                    }
                    return 0;
                }
            }
 
            data.append(mRecvData,size_recv);
            
        // тут кусок кода который согласно http протоколу определяет все ли считано и выходит из функции.
 
}
0
174 / 170 / 19
Регистрация: 31.08.2010
Сообщений: 574
19.01.2020, 19:37 4
Рекомендую посмотреть этот код:
https://metacpan.org/source/ML... e.pm#L2083

код на Perl, но думаю должно быть понятно
0
32 / 30 / 4
Регистрация: 01.02.2014
Сообщений: 878
21.01.2020, 19:45  [ТС] 5
Сказать честно, вообще ничего не понятно, но судя по количеству кода, врятли там есть что-то рабочее

Добавлено через 15 минут
Тут проблема в следующем, WSAEventSelect ждет когда в сокет придет хоть что-то, и этого что-то может быть мало для расшифровки пакета, и SSL_read возвращает ошибку, тоесть мне нужно ждать пока в сокет не прийдет данных которых достаточно для расшифровки.
Отсюда два вопроса, сколько нужно байт для расшифровки, и можно ли как-то сделать ожидание определенного количества байт?
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
21.01.2020, 21:52 6
Цитата Сообщение от mh-coder Посмотреть сообщение
WSAEventSelect ждет когда в сокет придет хоть что-то
WSAEventSelect ничего не ждет. Он связывает Event с сокетом для ожидания конкретных указанных событий на нем.

Само по себе ожидание выполняется функцией WSAWaitForMultipleEvents.

Почему оно ожидает два события? Что это за второе событие из представленного кода не понятно.

Также не вижу у вас в коде вызова WSAEnumNetworkEvents, для определения какое именно произошло событие на сокете.

Еще не очень понятно зачем каждый раз устанавливать и сбрасывать событие через WSAEventSelect. В типичной ситуации его достаточно сделать один раз.

Цитата Сообщение от mh-coder Посмотреть сообщение
сколько нужно байт
https://www.openssl.org/docs/m... nding.html

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

Попробуйте переписать ваш код, руководствуясь этим примером: https://github.com/Andersbakke... ad_write.c
0
32 / 30 / 4
Регистрация: 01.02.2014
Сообщений: 878
22.01.2020, 11:33  [ТС] 7
Цитата Сообщение от DrOffset Посмотреть сообщение
Почему оно ожидает два события? Что это за второе событие из представленного кода не понятно.
[/url]
Второе событие прерывает ожидание, например тайм аут 60 сек, сайт / прокси / интернет завис или очень медленно работает, а я захотел остановить поток, я нажимаю кнопку стоп, и посылается сигнал stopEvent чтобы прервать ожидание и работа моментально и корректно прерывается.


Цитата Сообщение от DrOffset Посмотреть сообщение
Также не вижу у вас в коде вызова WSAEnumNetworkEvents, для определения какое именно произошло событие на сокете.
не вижу в нем смысла, всего 2 события, в массиве их по очередность не меняется, WSAWaitForMultipleEvents возвращает либо ошибку, либо номер события которое произошло, в данном случае это 0 и 1.


Цитата Сообщение от DrOffset Посмотреть сообщение
Еще не очень понятно зачем каждый раз устанавливать и сбрасывать событие через WSAEventSelect. В типичной ситуации его достаточно сделать один раз.
WSAEventSelect захватывает сокет и принудительно переводит его в неблокирующий режим, тоесть если я его не освобожу
WSAEventSelect(lp_current_conection->handleSocket, hEventRead, 0 )
то дальнейшие операции (например закрыть хендл, перевести в блокирующий режим и прочее) я с ним провести не смогу.

===========

За полный пример спасибо, пойду изучу
0
19409 / 10028 / 2443
Регистрация: 30.01.2014
Сообщений: 17,678
22.01.2020, 12:51 8
Цитата Сообщение от mh-coder Посмотреть сообщение
WSAWaitForMultipleEvents возвращает либо ошибку, либо номер события которое произошло, в данном случае это 0 и 1.
Событий на сокете больше одного. Помимо READ, может быть еще CLOSE, также так может быть возвращена ошибка, непосредственно произошедшая в момент чтения.
Короче, я бы оставил WSAEnumNetworkEvents на месте.

Не совсем понимаю, зачем вам намеренно упрощать пример, лишая себя дифференциальной диагностики того, что происходит.
0
32 / 30 / 4
Регистрация: 01.02.2014
Сообщений: 878
27.01.2020, 13:36  [ТС] 9
Цитата Сообщение от DrOffset Посмотреть сообщение
Событий на сокете больше одного. Помимо READ, может быть еще CLOSE, также так может быть возвращена ошибка, непосредственно произошедшая в момент чтения.
Короче, я бы оставил WSAEnumNetworkEvents на месте.

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

Потестил я ваш пример, все работает но все равно циклов слишком много, если открывать одну страницу, то не заметно, если зациклить это дело, то имеется постоянная и немалая нагрузка на процессор, в случае с таймером нагрузки нет, и страница загружается примерно с той-же скоростью. Наверно нужно искать решение где-то в другом месте, например самому считывать из сокета, проверять длину и самому отправлять данные на расшифровку, нет у вас такого примера? я искал - ненашел
0
27.01.2020, 13:36
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
27.01.2020, 13:36
Помогаю со студенческими работами здесь

Read(). Как правильно прочитать из сокета?
День добрый! Не могу понять почему read() пролетает не дождавшись каких либо данных .Код ниже ...

Как использовать ASCII value в условии
Программу надо остановить при вводу CTRL + D он же (^D) он же EOT (end of transmission) он же ASCII...

Как правильно использовать operator= при наследовании в полиморфных и неполиморфных классах
Доброго времени суток! Изучаю книгу С. Прата &quot;Язык программирования C++. Лекции и упражнения.&quot;...

Общие подходы при создании игры: как правильно использовать графику?
При создании игры я не стремился сделать что-то очень смешное или увлекательное, просто увидев в...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru