Модераторы: feodorv
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Признак окончания приема данных, HTTP 
V
    Опции темы
MANAB
  Дата 4.10.2010, 13:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 29
Регистрация: 17.5.2009
Где: Беларусь, Минск

Репутация: нет
Всего: нет



Моя программка через Winsock отправляет HTTP-запрос на сервер, мол хочу получить такую-то страницу. Сервер отвечает - нате. Все приходит. Вопрос, а как узнать, что оно пришло полностью? Особенно, когда не указан Content-Length:

Код

string Answer = "";
    vector<in_addr> AddrList;
    hostent *remoteHost = NULL;
    DWORD dwError = 0;
    int i = 0;

    WORD wVersionRequested;
    WSADATA wsaData;
    int err = 0;
    wVersionRequested = MAKEWORD( 2, 0 );
    err = WSAStartup( wVersionRequested, &wsaData );
    if ( err != 0 )
    {
        return string( "Err" );
    }

    remoteHost = gethostbyname( strHost.c_str() );

    if ( remoteHost == NULL )
    {
        dwError = WSAGetLastError();
        if ( dwError != 0 ) 
        {
            if ( dwError == WSAHOST_NOT_FOUND ) 
            {
                Answer = "Host not found";
                return Answer;
            } 
            else 
            {
                if (dwError == WSANO_DATA) 
                {
                    Answer = "No data record found";
                    return Answer;
                } 
                else 
                {
                    char chErr[1024];
                    sprintf( chErr, "Function failed with error: %ld\0", dwError );
                    Answer.append( chErr );
                    return Answer;
                }
            }
        }
    } 
    else 
    {
        i = 0;
        if ( remoteHost->h_addrtype == AF_INET )
        {
            in_addr adr;
            while ( remoteHost->h_addr_list[i] != 0 ) 
            {
                adr.s_addr = *(u_long *) remoteHost->h_addr_list[i++];
                AddrList.push_back( adr );
            }
        }
        else if ( remoteHost->h_addrtype == AF_INET6 )
        {   
            Answer = "IPv6 address was returned";
            return Answer;
        }  
    }

    if ( AddrList.size() == 0 )
    {
        Answer = "Host does not exist";
        return Answer;
    }

    SOCKET s = socket( AF_INET, SOCK_STREAM, IPPROTO_IP );
    if ( m_iTime != 0 )
    {
        int sz = sizeof( m_iTime );
        if ( setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, (char *)&m_iTime, sz ) != 0 )
        {
            return Answer;
        }

        if ( setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, (char *)&m_iTime, sz ) != 0 )
        {
            return Answer;
        }
    }

    sockaddr_in dest_addr;
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons( Port );
    dest_addr.sin_addr.s_addr = /*inet_addr( strHost.c_str() );*/AddrList[0].s_addr;
    if ( connect( s, (sockaddr *)&dest_addr, sizeof( dest_addr ) ) == 0 )
    {
        char* buf = new char[BufSize];

        int iRes = send( s, strRequest.c_str(), strRequest.length(), 0 );
        if ( iRes != SOCKET_ERROR )
        {
            int BytesRec = 0;
            while ( true )
            {
                int bytes = recv( s, buf, BufSize-1, 0 );

                if ( bytes <= 0 )
                    break;
                else
                {
                    BytesRec += bytes;
                    buf[bytes] = '\0';
                    Answer.append( buf, bytes );

                    size_t pos1 = Answer.find( "Content-Length: ", 0 );
                    if ( pos1 != string::npos )
                    {
                        size_t pos2 = Answer.find( "\r\n", pos1 + 10 );
                        string strCL = Answer.substr( pos1 + 16, pos2 - pos1 - 16 );
                        int AnswerContentLength = atoi( Answer.substr( pos1 + 16, pos2 - pos1 - 16 ).c_str() );

                        pos2 = Answer.find( "\r\n\r\n", 0 );
                        if ( pos2 != string::npos )
                        {
                            if ( Answer.size() - pos2 - 4 >= AnswerContentLength )
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                                                // PROBLEM HERE
                        /*if ( bytes < BufSize-1 )
                        {
                            break;
                        }*/
                    }
                    //if ( /*( buf[bytes-1] == '\n' ) || (( buf[bytes-1] == '\0' ))*/ )
                        //break;
                }
            }
        }
    }
    else
    {
        //Answer = "Could not connect";
        shutdown( s, 2 );

        closesocket( s );

        WSACleanup();

        return Answer;
    }
    
    shutdown( s, 2 );

    closesocket( s );

    WSACleanup();


Ответ не всегда заканчивается какими-то специальными символами, а даже если и заканчивается, нет гарантии, что блок из середины пакета не заканчивается им, и тогда получится что я приму не весь ответ.
Так же признаком не является факт, что функция recv выдала нам байт меньше, чем у нас буфер - за время цикла буфер мог просто не заполниться полностью.
0 байт как признак окончания выдается только когда истечет таймаут, что меня абсолютно не устраивает.  smile 


Это сообщение отредактировал(а) MANAB - 4.10.2010, 13:30
PM MAIL ICQ   Вверх
GrayCardinal
Дата 4.10.2010, 14:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Фигасе
****


Профиль
Группа: Завсегдатай
Сообщений: 3039
Регистрация: 9.11.2003

Репутация: 3
Всего: 58



В ответе либо указана длина, либо данные передаются "кусочками" (Transfer-Encoding: chunked, ежели память не изменяет). Третьего не дано. Суть кусочков - сначала размер кусочка (в виде строки-числа с \r\n на конце), затем данные кусочка. Ежели всё - тогда размер кусочка == 0.


--------------------
PM MAIL WWW   Вверх
MANAB
Дата 4.10.2010, 15:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 29
Регистрация: 17.5.2009
Где: Беларусь, Минск

Репутация: нет
Всего: нет



Т.е. получается, надо каждый раз парсить при получении куска буфера.
Просто не хотелось бы нагружать функцию этим. Вот если бы сразу по окончании передачи при вызове recv приходил 0, было бы удобно...
PM MAIL ICQ   Вверх
GrayCardinal
Дата 4.10.2010, 15:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Фигасе
****


Профиль
Группа: Завсегдатай
Сообщений: 3039
Регистрация: 9.11.2003

Репутация: 3
Всего: 58



Цитата

Т.е. получается, надо каждый раз парсить при получении куска буфера.
Просто не хотелось бы нагружать функцию этим. Вот если бы сразу по окончании передачи при вызове recv приходил 0, было бы удобно...

Парсить всё равно придется. Другое дело, что можно сначала скачать, затем всё распарсить. Сервер EOF выдает после завершения передачи, если включить в запросе "Connection: close" Правда для следующего запроса придется открывать соединение по-новой smile

Это сообщение отредактировал(а) GrayCardinal - 4.10.2010, 15:12


--------------------
PM MAIL WWW   Вверх
MANAB
Дата 4.10.2010, 17:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 29
Регистрация: 17.5.2009
Где: Беларусь, Минск

Репутация: нет
Всего: нет



Если кому-нибудь понадобится, то следующий код работает:
Код

string CMyWinSock::Send( std::string strHost, short Port, std::string strRequest, unsigned int BufSize )
{
    string Answer = "";
    vector<in_addr> AddrList;
    hostent *remoteHost = NULL;
    DWORD dwError = 0;
    int i = 0;

    WORD wVersionRequested;
    WSADATA wsaData;
    int err = 0;
    wVersionRequested = MAKEWORD( 2, 0 );
    err = WSAStartup( wVersionRequested, &wsaData );
    if ( err != 0 )
    {
        return string( "Err" );
    }

    remoteHost = gethostbyname( strHost.c_str() );

    if ( remoteHost == NULL )
    {
        dwError = WSAGetLastError();
        if ( dwError != 0 ) 
        {
            if ( dwError == WSAHOST_NOT_FOUND ) 
            {
                Answer = "Host not found";
                return Answer;
            } 
            else 
            {
                if (dwError == WSANO_DATA) 
                {
                    Answer = "No data record found";
                    return Answer;
                } 
                else 
                {
                    char chErr[1024];
                    sprintf( chErr, "Function failed with error: %ld\0", dwError );
                    Answer.append( chErr );
                    return Answer;
                }
            }
        }
    } 
    else 
    {
        i = 0;
        if ( remoteHost->h_addrtype == AF_INET )
        {
            in_addr adr;
            while ( remoteHost->h_addr_list[i] != 0 ) 
            {
                adr.s_addr = *(u_long *) remoteHost->h_addr_list[i++];
                AddrList.push_back( adr );
            }
        }
        else if ( remoteHost->h_addrtype == AF_INET6 )
        {   
            Answer = "IPv6 address was returned";
            return Answer;
        }  
    }

    if ( AddrList.size() == 0 )
    {
        Answer = "Host does not exist";
        return Answer;
    }

    SOCKET s = socket( AF_INET, SOCK_STREAM, IPPROTO_IP );
    if ( m_iTime != 0 )
    {
        int sz = sizeof( m_iTime );
        if ( setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, (char *)&m_iTime, sz ) != 0 )
        {
            return Answer;
        }

        if ( setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, (char *)&m_iTime, sz ) != 0 )
        {
            return Answer;
        }
    }

    sockaddr_in dest_addr;
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons( Port );
    dest_addr.sin_addr.s_addr = /*inet_addr( strHost.c_str() );*/AddrList[0].s_addr;
    if ( connect( s, (sockaddr *)&dest_addr, sizeof( dest_addr ) ) == 0 )
    {
        char* buf = new char[BufSize];

        int iRes = send( s, strRequest.c_str(), strRequest.length(), 0 );
        if ( iRes != SOCKET_ERROR )
        {
            int BytesRec = 0;
            while ( true )
            {
                int bytes = recv( s, buf, BufSize-1, 0 );

                if ( bytes <= 0 )
                    break;
                else
                {
                    BytesRec += bytes;
                    buf[bytes] = '\0';
                    Answer.append( buf, bytes );

                    size_t pos1 = Answer.find( "Content-Length: ", 0 );
                    if ( pos1 != string::npos )
                    {
                        size_t pos2 = Answer.find( "\r\n", pos1 + 10 );
                        string strCL = Answer.substr( pos1 + 16, pos2 - pos1 - 16 );
                        int AnswerContentLength = atoi( Answer.substr( pos1 + 16, pos2 - pos1 - 16 ).c_str() );

                        pos2 = Answer.find( "\r\n\r\n", 0 );
                        if ( pos2 != string::npos )
                        {
                            if ( Answer.size() - pos2 - 4 >= AnswerContentLength )
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        pos1 = Answer.find( "Transfer-Encoding: chunked", 0 );
                        if ( pos1 != string::npos )
                        {
                            size_t HeaderSize = Answer.find( "\r\n\r\n", 0 );
                            if ( HeaderSize != string::npos )
                            {
                                HeaderSize += 4;
                                bool bBreak = false;
                                pos1 = HeaderSize;
                                while ( true )
                                {
                                    size_t pos2 = Answer.find( '\r\n', pos1 );
                                    if ( pos2 != string::npos )
                                    {
                                        char* ch = new char[pos2-pos1+1];
                                        Answer.substr( pos1, pos2-pos1 ).copy( ch, pos2-pos1 );
                                        ch[pos2-pos1] = '\0';
                                        int chunk = parseHex( ch );
                                        if ( chunk == 0 )
                                        {
                                            bBreak = true;
                                            break;
                                        }

                                        pos1 = pos2 + 2 + chunk + 2;
                                        if ( pos1 >= Answer.size() )
                                            break;
                                    }
                                }

                                if ( bBreak )
                                    break;
                            }
                        }
                    }
                }
            }
        }
    }
    else
    {
        //Answer = "Could not connect";
        shutdown( s, 2 );

        closesocket( s );

        WSACleanup();

        return Answer;
    }
    
    shutdown( s, 2 );

    closesocket( s );

    WSACleanup();

    return Answer;
}

Тему можно закрывать

Это сообщение отредактировал(а) MANAB - 4.10.2010, 17:26
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0490 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.