Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Openssl, C++. Зависает на SSL_read()...

444
09 июля 2010 года
patison
323 / / 15.03.2007
Есть небольшая программка, которая через openssl коннетится на imap.gmail.com и пытается получать оттуда данные.
При подключении всё проходит нормально, получаю ответ что Gimap is ready и бла бла бла.

Далее отправляю сообщение:
 
Код:
imap->sendMsg(". login my_login my_pass\r\n");


imap - это объект моего класса, который является обёрткой для функций openssl.
Ниже код метода sendMsg().
Код:
string IMAP_interractor::sendMsg(char *Cmd)
{
    char Data[10];
    string sResponse;

    int w = SSL_write(this->Ssl, Cmd, strlen(Cmd));

    if (w > 0)
    {
        int r = SSL_read(this->Ssl, Data, sizeof(Data));
        sResponse = Data;

        while( r == sizeof(Data) )
        {
            r = SSL_read(this->Ssl, Data, sizeof(Data));
            sResponse += Data;
        }
    }
    else printf("SSL_write failed.\n");

    return sResponse;
}

И вот на строке:
 
Код:
r = SSL_read(this->Ssl, Data, sizeof(Data));

прога почему-то зависает...чуство что ожидает ответа.
Причём, если отправить другую команду, типа . status inbox (messages) , или заведомо неправильно введённые данные авторизации - то ответ приходит. А если отправляю . login с правильными данными для авторизации - виснет. Пробовал авторизироваться через терминал - всё работает нормально.

В чём тут может быть дело?
43K
09 июля 2010 года
loki231
76 / / 27.09.2009
Что-то там из ввода-вывода ниже уровнем работает в блокирующем режиме.
444
09 июля 2010 года
patison
323 / / 15.03.2007
И что-же с этим делать?
43K
09 июля 2010 года
loki231
76 / / 27.09.2009
Перевести в неблокирующий режим или использовать SSL_pending, как я понимаю.

http://www.openssl.org/docs/ssl/SSL_read.html

If the underlying BIO is blocking, SSL_read() will only return, once the read operation has been finished or an error occurred, except when a renegotiation take place, in which case a SSL_ERROR_WANT_READ may occur. This behaviour can be controlled with the SSL_MODE_AUTO_RETRY flag of the SSL_CTX_set_mode(3) call.

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! The calling process then must repeat the call after taking appropriate action to satisfy the needs of SSL_read(). The action depends on the underlying BIO. When using a non-blocking socket, nothing is to be done, but select() can be used to check for the required condition. When using a buffering BIO, like a BIO pair, data must be written into or retrieved out of the BIO before being able to continue.

SSL_pending(3) can be used to find out whether there are buffered bytes available for immediate retrieval. In this case SSL_read() can be called without blocking or actually receiving new data from the underlying socket.
444
09 июля 2010 года
patison
323 / / 15.03.2007
Да, это я читал. Но ведь дело не столько в том что блокируется (в конце концов можно запихнуть в отдельнйы поток), сколько в том что сервер не отвечает.

Причём этот-же код прекрасно работает при подключении и авторизации на pop сервер gmail.
14
09 июля 2010 года
Phodopus
3.3K / / 19.06.2008
Все это звучит странно. Нет возможности попробовать на другом сервисе?
[COLOR="Gray"]А буфер я бы сделал байт 16 хотя бы...[/COLOR]
43K
09 июля 2010 года
loki231
76 / / 27.09.2009
Могу лишь предположить, что в одном случае сервер закрывает соединение и SSL_read() завершается, а в другом сервер не закрывает соединение, а ждёт продолжения сессии, в этом случае SSL_read() тоже висит и тоже ждёт.
444
09 июля 2010 года
patison
323 / / 15.03.2007
Чудеса блин =))

Phodopus: у мня 10 байт стояло просто ради теста.. Я хотел посмотреть, можно-ли читать ответ от сервера в цикле (типа, while( SSL_read(...) )).
Щас увеличил до 64 байт - всё заработало. Авторизируюсь - всё окей.
43K
09 июля 2010 года
loki231
76 / / 27.09.2009
Мда. С таким подходом через некоторое время найдется сервер, с которым работать опять не будет. Ну да дело хозяйское..
444
09 июля 2010 года
patison
323 / / 15.03.2007
Ну почему-же, вопрос "почему с буфером в 10 байт ответ зависал" остался для меня актуальным. В данный момент пытаюсь найти на него ответ.
5
09 июля 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: patison
Ну почему-же, вопрос "почему с буфером в 10 байт ответ зависал" остался для меня актуальным. В данный момент пытаюсь найти на него ответ.

Возможно какие-то внутренние ограничения. Вообще, какой смысл использовать столь малые буфера? Программа, что в микроконтроллере работает? Я бы как минимум 4КБ буфер выделил и через него работал.

444
09 июля 2010 года
patison
323 / / 15.03.2007
Цитата: hardcase
Возможно какие-то внутренние ограничения. Вообще, какой смысл использовать столь малые буфера? Программа, что в микроконтроллере работает? Я бы как минимум 4КБ буфер выделил и через него работал.



Ну я уже писал что просто ради теста поставил такой маленький буфер.

43K
09 июля 2010 года
loki231
76 / / 27.09.2009
Скорее всего, ответ сервера кратен 10 байтам (на уровне SSL_read()). Поэтому, когда Вы вычитываете по 10 байт, то в конце концов возникает ситауация, когда SSL_read() читать вообще нечего и она блокируется. Когда Вы увеличили размер читаемых блоков, то длина ответа сервера стала не кратна им и SSL_read() в конце концов читает не полный блок и больше Вы её уже не вызываете. В данном случае это работает, но так делать нельзя. Потому что, другой ответ сервера может быть кратен новому блоку и все повторится. Нужно либо разбирать ответ сервера, и когда, согласно прикладному протоколу, получен конец ответа сервера, ну например '\n', то больше SSL_read() не вызывать, или читать в неблокируемом режиме.
444
09 июля 2010 года
patison
323 / / 15.03.2007
Спасибо Вам, товарищь!
Вы попали в точку насчёт кратности размера ответа размеру моего буффера.
Всё разрулил, немного модифицировав цикл чтения:
 
Код:
int r = SSL_read(this->Ssl, Data, sizeof(Data));
        sResponse = Data;

        //cout << "SSL_pending: " << SSL_pending(this->Ssl) << endl;

        while( SSL_pending(this->Ssl) > 0 )
        {
            r = SSL_read(this->Ssl, Data, sizeof(Data));
            sResponse += Data;
        }

таким образом я просто проверяю - есть-ли чё у сервера для меня или нет.
43K
09 июля 2010 года
loki231
76 / / 27.09.2009
В общем случае, такой подход не работает. SSL_pending() показывает, сколько байт осталось невычитанными во внутреннем буфере SSL_read(). Это не имеет никакого отношения к длине ответа сервера на Ваш запрос. Просто для коротких ответов, которые передаются в одном блоке SSL, получается, что то, что возвращает SSL_pending() совпадает с размером ответа.

PS. Да я вообще снайпер-телепат. :)
444
09 июля 2010 года
patison
323 / / 15.03.2007
Цитата: loki231
В общем случае, такой подход не работает. SSL_pending() показывает, сколько байт осталось невычитанными во внутреннем буфере SSL_read(). Это не имеет никакого отношения к длине ответа сервера на Ваш запрос. Просто для коротких ответов, которые передаются в одном блоке SSL, получается, что то, что возвращает SSL_pending() совпадает с размером ответа.

PS. Да я вообще снайпер-телепат. :)



Ну так всё правильно. У меня-то всё упиралось в то, что я не знал сколько ещё осталось невычитанным из буфера =)) И когда из буффера вычитывались последние данные (в случае с кратностью длинной ответа размеру буффера) - я, не зная того что данные все прочитаны, вызывал SSL_read. В этом плане SSL_pending меня спасает.

444
10 июля 2010 года
patison
323 / / 15.03.2007
В продолжении темы, дабы не плодить новых топиков...

Шлю серверу запрос на получение некоторых писем (надо отметить, что писем этих несколько, соответственно запрос происходит в цикле):
Код:
for(int iter = 0; iter<myMails.size(); iter++)
    {
        string subj, from;

        imap->sendMsg( (". fetch " + myMails[iter] + " (body[header.fields (subject from)])\r\n").c_str() );

        subj = imapParser.getSubject( imap->getLastResponse() );
        from = imapParser.getFrom( imap->getLastResponse() );

        cout << "=====" << endl << "from: " << from << endl << "subj: " << subj << endl << endl;
    }

Всплыла проблемка следующего характера. Сервер гмыла, на запрос fetch, возвращает нечто следующее:
Цитата:

. fetch 2054 (body[header.fields (from subject)]) --- Тут непосредственно вызов команды, а дальее идёт ответ сервера
* 2054 FETCH (BODY[HEADER.FIELDS (from to date subject)] {159}
Date: Sat, 10 Jul 2010 13:48:18 +0300
Subject: this is a simple message, man
From: George C <mymail@gmail.com>

)
. OK Success


И в ходе чтения этого ответа через SSL_read , вычитывается всё до(!) закрывающей скобки перед .OK.
А вот при запрашивании следующего письма (в цикле), SSL_read отдаёт нам остатки прошлого ответа, то бишь ".OK Success". Причём! Перед тем как запрашивать каждое письмо, я делал проверку SSL_pending, и во всех случаях мне выдавало что буфер ответа чист (т.е. никаких остатков с прошлого запроса там нет).

В данный момент пришлось поставить костыль, такого вида:

Код:
for(int iter = 0; iter<myMails.size(); iter++)
    {
        string subj, from;

        imap->sendMsg( (". fetch " + myMails[iter] + " (body[header.fields (subject from)])\r\n").c_str() );

        if( imap->getLastResponse()== ". OK Success\r\n")
            iter--;
        else
        {
            subj = imapParser.getSubject( imap->getLastResponse() );
            from = imapParser.getFrom( imap->getLastResponse() );

            cout << "=====" << endl << "from: " << from << endl << "subj: " << subj << endl << endl;
        }
    }

Таким образом работает, однако хотелось-бы понять откуда в новом ответе берутся остатки старого...
43K
11 июля 2010 года
loki231
76 / / 27.09.2009
Это то, о чём я говорил. Ответ сервера передается в двух блоках SSL. В первом - "* 2054 FETCH (BODY[HEADER.FIELDS (from to date subject)] {159}.....". Во втором ". OK Success\n". А SSL_pending() говорит о количестве данных в читаемом блоке, в данном случае -первом. и это НИКАК не связано с количеством данных, содержащихся в ответе сервера. Надо разбирать читаемые данные согласно прикладному протоколу. В данном случае IMAP.
SSL_pending() ничего не знает о том, сколько там ещё сервер ответил на Ваш запрос.
444
12 июля 2010 года
patison
323 / / 15.03.2007
Ога, вот теперь на примере (читай "стукнувшись свои лбом") понял :) Иду курить маны о том - как правильно читать ответ сервера :)

Знаете кого-то, кто может ответить? Поделитесь с ним ссылкой.

Ваш ответ

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог