C++ функция recv зависает, сокеты, проблема

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by awdrg, 17 Jun 2009.

  1. awdrg

    awdrg Member

    Joined:
    30 Jan 2009
    Messages:
    195
    Likes Received:
    31
    Reputations:
    1
    Пишу в обучающих целях простой граббер web-страниц. Открываю сокет, отсылаю заголовок функцией send. Далее начинается чтение ответа и самого кода странички функцией recv. Тут начинаются проблемы - после чтения предпоследней порции информации функция "переходит в режим ожидания" :( Флаг MSG_PEEK не помогает. Вопрос: как избежать этого виса, возможно ли поставить recv на таймаут? Приведите пожалуйста простой пример кода с решением.
    Пока решаю это проблему так: в каждой порции ответа сервера ищу "\r\n\r\n", если нахожу более 2 раз - выход из цикла приема.
     
  2. d_x

    d_x Banned

    Joined:
    25 Mar 2008
    Messages:
    558
    Likes Received:
    650
    Reputations:
    210
    Когда я с помощью send-recv работал с вебом, таких проблем не было. Просто я проверял, сколько байт считано (это и возвращает recv), и если это число равно 0, то цикл чтения закончен. Простой пример кода только на асме могу привести, на нем и писал)
    Лучше бы показал свой код.
     
  3. razb

    razb Active Member

    Joined:
    24 Mar 2009
    Messages:
    658
    Likes Received:
    133
    Reputations:
    18
    Покажи свой код, тут не телепаты...
     
  4. awdrg

    awdrg Member

    Joined:
    30 Jan 2009
    Messages:
    195
    Likes Received:
    31
    Reputations:
    1
    Я так и пытался делать. Проблема в том, что если размер буфера чуть больше кол-ва оставшихся байт то он пытается что то читать (около минуты функция висит)
    вот кусок кода
    PHP:
    #define PORT 80
    #define SERVERADDR "forum.antichat.ru" 
    char connect()
    {
       
    WSADATA wd;
       
    char buff[256] = "\0";
       
    char text[100000] = "\0";
       
    char buff2[10000]="GET / HTTP/1.1\r\nHost: forum.antichat.ru\r\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11\r\nAccept-Language: ru,en-us;q=0.7,en;q=0.3\r\nKeep-Alive: 300\r\nConnection: keep-alive\r\n\r\n";
       
    printf "TCP Client \n" );
       if ( 
    WSAStartup 0x202, &wd ) )
       {
          return 
    "[Connection error]";
       }

       
    SOCKET sock ;
       
    sock=socket AF_INETSOCK_STREAM);
       if  ( 
    sock )
       {
           return  
    "[Socket error]";
       }

       
    sockaddr_in  dest_addr;
       
    dest_addr.sin_family=AF_INET;
       
    dest_addr.sin_port=htons PORT );
       
    HOSTENT    *hst;
       if( 
    inet_addr(SERVERADDR) != INADDR_NONE )
           
    dest_addr.sin_addr.S_un.S_addr inet_addr(SERVERADDR);
       else if( 
    hst=gethostbyname SERVERADDR ) )
           ( ( 
    unsigned long   * ) &dest_addr.sin_addr )[0]=((unsigned long** )hst->h_addr_list)[0][0];
       else
       {
          
    closesocketsock ) ;
          
    WSACleanup();
          return  
    "[Bad address]";
       }

       if(
    connectsock, (sockaddr *)&dest_addrsizeof(dest_addr) ))
       {
          return 
    "[Connection error]";
       }

       
    sendsockbuff2strlen(buff2), 0);
       
    int x 1;
       
    FILE ff fopen("log.txt","w");
       while (
    x>0)
       {
          
    recvsockbuff256); // 256 - buffer size
          
    strcat(text,buff);
          
    fputs(buff,ff);
       }
       
    fclose(ff);
       return 
    text;
    }
     
    #4 awdrg, 17 Jun 2009
    Last edited: 17 Jun 2009
  5. d_x

    d_x Banned

    Joined:
    25 Mar 2008
    Messages:
    558
    Likes Received:
    650
    Reputations:
    210
    Как вариант - использовать не keep-alive соединение, а close. Протокол надо использовать HTTP/1.0, а не 1.1, иначе придется парсить содержимое, так как сервер может отсылать его в этом случае кусками с Transfer-Encoding: chunked. Кстати, в файл надо писать не просто строку buff с помощью fputs, так как последний байт у нее необязательно нулевой, а следует использовать fwrite и явно указывать размер записываемого буфера.
    Примерно так:
    PHP:
     #define PORT 80 
    #define SERVERADDR "forum.antichat.ru" 


    #include "stdio.h"
    #include <winsock2.h>

    int myconnect() 

       
    WSADATA wd
       
    char buff[256] = {0}; 
       
    char *buff2="GET / HTTP/1.0\r\nHost: forum.antichat.ru\r\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.0.11) Gecko/2009060308 Ubuntu/9.04 (jaunty) Firefox/3.0.11\r\nAccept-Language: ru,en-us;q=0.7,en;q=0.3\r\nConnection: close\r\nContent-length: 0\r\n\r\n\n"
       
    printf "TCP Client \n" ); 
       if ( 
    WSAStartup 0x0101, &wd ) ) 
       { 
          return 
    0
       } 

       
    SOCKET sock 
       
    sock=socket AF_INETSOCK_STREAM); 
       if  ( 
    sock 
       { 
           return  
    0
       } 

       
    sockaddr_in  dest_addr
       
    dest_addr.sin_family=AF_INET
       
    dest_addr.sin_port=htons PORT ); 
       
    HOSTENT    *hst
       if( 
    inet_addr(SERVERADDR) != INADDR_NONE 
           
    dest_addr.sin_addr.S_un.S_addr inet_addr(SERVERADDR); 
       else if( 
    hst=gethostbyname SERVERADDR ) ) 
           ( ( 
    unsigned long   * ) &dest_addr.sin_addr )[0]=((unsigned long** )hst->h_addr_list)[0][0]; 
       else 
       { 
          
    closesocketsock ) ; 
          
    WSACleanup(); 
          return  
    0
       } 

       if(
    connectsock, (sockaddr *)&dest_addrsizeof(dest_addr) )) 
       { 
          return 
    0
       } 

       
    sendsockbuff2strlen(buff2)-10); 
       
    int x 255
       
    FILE ff fopen("log.txt","w"); 
       while (
    x>0
       { 
          
    recvsockbuff255); // 256 - buffer size

          
    printf("Got %u bytes\n",x);
          
    fwrite(buff,1,x,ff); 
       } 
       
    printf("finished\n",x);
       
    closesocketsock ) ; 
       
    WSACleanup(); 
       
    fclose(ff); 
       return 
    1
    }  

    int main()
    {
     
    myconnect();
     return 
    0;   
    }
     
    #5 d_x, 17 Jun 2009
    Last edited: 17 Jun 2009
    1 person likes this.
  6. awdrg

    awdrg Member

    Joined:
    30 Jan 2009
    Messages:
    195
    Likes Received:
    31
    Reputations:
    1
    Хм, проблема так просто решилась, большое спасибо!
     
  7. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,223
    Reputations:
    455
    а на счет таймаута для recv в моей статейке почитай
     
    1 person likes this.
  8. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,118
    Reputations:
    1,139
    Content-Length!
     
  9. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,223
    Reputations:
    455
    2 _Great_ а если сервак еб*н*тый и непосылает Content-Length - тогда облом. )
     
  10. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,118
    Reputations:
    1,139
    При кипелайв соединении обязан. Иначе браузер никогда не узнает когда посылать запрос очередной.
    А при клоуз - оно и не нужно.
     
  11. awdrg

    awdrg Member

    Joined:
    30 Jan 2009
    Messages:
    195
    Likes Received:
    31
    Reputations:
    1
    Хм может еще кто нибудь знает: есть в С++ аналог функции urlencode()? Для преобразования русских символов в url вид?
     
  12. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,223
    Reputations:
    455
    а самому написать?? банальный конверт в hex с добавлением с переди символа %
     
  13. krypt3r

    krypt3r Elder - Старейшина

    Joined:
    27 Apr 2007
    Messages:
    1,507
    Likes Received:
    389
    Reputations:
    101
    например
    Code:
    char *urlencode (char *str) {
      char *ret, *ptr;
      int len = strlen (str), i;
    
      if ((ret = calloc (3 * len + 1, sizeof (char *))) == NULL)
        return NULL;
      memset (ret, '\0', len + 1);
      for (i = 0, ptr = ret; i < len; i++) {
        sprintf ((char *) ptr, "%%%x", str[i]);
        ptr += 3;
      }
      return ret;
    }
    
     
  14. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,118
    Reputations:
    1,139
    А с какого ... должен быть?
     
  15. awdrg

    awdrg Member

    Joined:
    30 Jan 2009
    Messages:
    195
    Likes Received:
    31
    Reputations:
    1
    Я спросил не "почему нету" а "есть ли". Это разные вещи.
     
  16. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,118
    Reputations:
    1,139
    awdrg
    Если знать, почему может, а почему не может быть, будешь заодно знать и есть ли.

    krypt3r
    Это что еще за пи##ец?
    Откуда sizeof(char*) ?
    Почему заполняется нулями только треть буффера - len+1 ?
    Зачем вообще заполнение нулями, если вызывается calloc?
    Почему код вообще на Си, когда просили на С++?
    В чем сакральный смысл приведения char* к тиму char*?
    Садись, два
     
    2 people like this.
Loading...