Openssl, C++. Зависает на SSL_read()...
При подключении всё проходит нормально, получаю ответ что Gimap is ready и бла бла бла.
Далее отправляю сообщение:
imap - это объект моего класса, который является обёрткой для функций openssl.
Ниже код метода sendMsg().
{
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;
}
И вот на строке:
прога почему-то зависает...чуство что ожидает ответа.
Причём, если отправить другую команду, типа . status inbox (messages) , или заведомо неправильно введённые данные авторизации - то ответ приходит. А если отправляю . login с правильными данными для авторизации - виснет. Пробовал авторизироваться через терминал - всё работает нормально.
В чём тут может быть дело?
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.
Причём этот-же код прекрасно работает при подключении и авторизации на pop сервер gmail.
[COLOR="Gray"]А буфер я бы сделал байт 16 хотя бы...[/COLOR]
Phodopus: у мня 10 байт стояло просто ради теста.. Я хотел посмотреть, можно-ли читать ответ от сервера в цикле (типа, while( SSL_read(...) )).
Щас увеличил до 64 байт - всё заработало. Авторизируюсь - всё окей.
Возможно какие-то внутренние ограничения. Вообще, какой смысл использовать столь малые буфера? Программа, что в микроконтроллере работает? Я бы как минимум 4КБ буфер выделил и через него работал.
Ну я уже писал что просто ради теста поставил такой маленький буфер.
Вы попали в точку насчёт кратности размера ответа размеру моего буффера.
Всё разрулил, немного модифицировав цикл чтения:
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;
}
таким образом я просто проверяю - есть-ли чё у сервера для меня или нет.
PS. Да я вообще снайпер-телепат. :)
PS. Да я вообще снайпер-телепат. :)
Ну так всё правильно. У меня-то всё упиралось в то, что я не знал сколько ещё осталось невычитанным из буфера =)) И когда из буффера вычитывались последние данные (в случае с кратностью длинной ответа размеру буффера) - я, не зная того что данные все прочитаны, вызывал SSL_read. В этом плане SSL_pending меня спасает.
Шлю серверу запрос на получение некоторых писем (надо отметить, что писем этих несколько, соответственно запрос происходит в цикле):
{
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, и во всех случаях мне выдавало что буфер ответа чист (т.е. никаких остатков с прошлого запроса там нет).
В данный момент пришлось поставить костыль, такого вида:
{
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;
}
}
Таким образом работает, однако хотелось-бы понять откуда в новом ответе берутся остатки старого...
SSL_pending() ничего не знает о том, сколько там ещё сервер ответил на Ваш запрос.