Сокеты, косяк с получением страници.

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by transserg, 24 Aug 2010.

  1. transserg

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

    Joined:
    2 Jul 2008
    Messages:
    147
    Likes Received:
    25
    Reputations:
    2
    привет все. пытаюсь получить страницу ya.ru.
    все бы ничего запрс ушел ответ приходит. но в нем куча мусора который постоянно повторяется.
    вот кусок кода
    Code:
    int get_page (char *buf)
    {
        SOCKET sock;
        char buff[8024];
        int len;
    
        if (!(sock = init_tcp_client("ya.ru", 80))) // инициазизация сокета
            return 0;
    
        len = sprintf(buff,  "GET / HTTP/1.1\r\n"
                                        "Host: ya.ru\r\n"
                                        "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8\r\n"
                                        "Accept: text/html,q=0.9,*/*;q=0.8\r\n"
                                        "Accept-Language: ru,en-us;q=0.7,en;q=0.3\r\n"                                    
                                        "Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7\r\n"
                                        "Keep-Alive: 115\r\n"
                                        "Connection: keep-alive\r\n"
                                        "\r\n");
      
        len=send(sock, buff, len,0);  // передача запроса
        len=recv(sock, buff, len,0);
        
        while (len>0)
        {
            printf(buff);
            len=recv(sock, buff, len,0);        
        }    
        
        //memcpy(buf,buff,sizeof(buff));
        close(sock);
        return len;
    }
    
     
  2. GRRRL Power

    GRRRL Power Elder - Старейшина

    Joined:
    13 Jul 2010
    Messages:
    823
    Likes Received:
    185
    Reputations:
    84
    Так делать нельзя, потому что printf выводит buff как строку, а recv принимает как бинарные данные. Соттветственно, строка будет не законченной нуллбайтом и при вызове printf вывалится еще куча мусора до первого попавшегося нуллбайта. Нужно принимать сразу все данные в буфер, а потом уже выводить их, не забыв дописать в конец нулевой байт.
     
  3. transserg

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

    Joined:
    2 Jul 2008
    Messages:
    147
    Likes Received:
    25
    Reputations:
    2
    GRRRL Power проблема в том что я незнаю заранее размер страници чтоб под нее выделить буфер. да и не догоняю как например к уже выделенной паменить добавть еще тогда было бы проще
     
  4. transserg

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

    Joined:
    2 Jul 2008
    Messages:
    147
    Likes Received:
    25
    Reputations:
    2
    сделал так
    Code:
        tmp=malloc(1024*32);
        t_tmp=tmp;
      
        len=send(sock, buff, len,0);  // передача запроса
        len=recv(sock, buff, len,0);
        
        while (len>0)
        {
            //printf(buff);
            memcpy(t_tmp,buff,len);
            t_tmp=t_tmp+len;
            len=recv(sock, buff, len,0);
    
        }    
        printf(tmp);
    
    
     
  5. GRRRL Power

    GRRRL Power Elder - Старейшина

    Joined:
    13 Jul 2010
    Messages:
    823
    Likes Received:
    185
    Reputations:
    84
    Как вариант:
    PHP:
    size_t received_lenlen 0;

    //...


    do
    {
      
    len recv(sock, (char*)&buff[0] + received_lenlen0);
      
    received_len += len;
    }
    while(
    len 0);

    buff[received_len] = 0;

    printf(buff);
    Размер буфера узнается из заголовков, в http-заголовке Content-length это пишется, поэтому нужно сначала принять его, спарсить длину, создать буфер и читать. Либо же париться с выделениями памяти. За нас это уже сделали, поэтому можно написать:

    PHP:
    #include <string>
    using std::string;

    //...

    size_t len 0;
    char buff[1024];
    string str "";

    //...


    do
    {
      
    len recv(sockbuff10230);
      
    buff[len] = 0;
      
    str.append(buf);
    }
    while(
    len 0);

    printf("%s"str.c_str()); //опасно так выводить
    Коды не проверял, но должно работать.
     
    1 person likes this.
  6. transserg

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

    Joined:
    2 Jul 2008
    Messages:
    147
    Likes Received:
    25
    Reputations:
    2
    спасибо за помошь. ну я почти так же сделал =) ну да можно парсить а можно выделить сразу здоровый кусок кода.
     
  7. GRRRL Power

    GRRRL Power Elder - Старейшина

    Joined:
    13 Jul 2010
    Messages:
    823
    Likes Received:
    185
    Reputations:
    84
    Во втором примере мы не работаем с памятью, все делает класс string. Если много контента будет получено, то несколько раз строка будет копироваться из одного буфера памяти в другой, большего размера, но мы этого не видим, это всё внутри string происходит.
     
  8. slesh

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

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    Code:
    do
    {
      len = recv(sock, buff, 1023, 0);
      buff[len] = 0;
      str.append(buf);
    }
    while(len > 0); 
    
    Так нельзя. Потому что если вдруг коннект разорвется и recv вернет -1 то затрется переменная перед массивом.

    Code:
    do
    {
      len = recv(sock, (char*)&buff[0] + received_len, len, 0);
      received_len += len;
    }
    while(len > 0); 
    
    Так тож незя, потому что по темже причинами, тока тут ты уменьшиш размер данных котоыре считал.

    А вот если ты заранее выделил большой буфер, то можно вот так вот сделать:
    Code:
    int full_len = 0; 
    for (;;)
    {
        len = recv (sock, buf + full_len, buf_len - full_len - 1, 0);
        if (len < 1) break;
        full_len += len;
    }
    
    buf[full_len] = 0;
    
    В итоге у тебя не будет переполнения буфера и нормально обработается дисконнект или ошибка. И в конце при любом условии ты поставишь нулбайт где надо.
     
    1 person likes this.
  9. transserg

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

    Joined:
    2 Jul 2008
    Messages:
    147
    Likes Received:
    25
    Reputations:
    2
    slesh спасибо за более подробные разъяснения!
     
  10. GRRRL Power

    GRRRL Power Elder - Старейшина

    Joined:
    13 Jul 2010
    Messages:
    823
    Likes Received:
    185
    Reputations:
    84
    Просто добавить условие выхода из цикла при len == -1.

    Содержимое всегда может быть больше по объему, чем выделенный буфер, и часть останется непринятой) Не выделять же мегабайтами заранее.
     
  11. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    2GRRRL Power
    угу а еще если в buff встретиться % то вообще красота начнется я бы так написал
    printf("%s\n", buff); или так
    printf("%.*s\n", len, buff); если известна длинна строки но она без '\0' в конце
    2slesh
    Code:
    int full_len = 0; 
    for (;;)
    {
        len = recv (sock, buf + full_len, buf_len - full_len - 1, 0);
        if (len < 1) break;
        full_len += len;
    }
    
    buf[full_len] = 0;
    
    красивый код
    2transserg
    Code:
    typedef char page;
    
    page * alloc_page(unsigned sock)
    {
    	char buff[4096], *tmp;
    	page *p = 0;
    	int rec, all = 0;
    
    	do
    	{
    		rec = recv(sock, buff, sizeof(buff), 0);
    		if (rec == -1)
    		{
    			free(p);
    			return NULL;
    		}
    		if (rec > 0)
    		{
    			all += rec;
    			tmp = realloc(p, all + 1);
    			if (!tmp)
    			{
    				free(p);
    				return NULL;
    			}
    			p = tmp;
    			memmove(p + all - rec, buff, rec);
    			p[all] = '\0';
    		}
    	} while (rec > 0);
    
    	return p;
    }
    
    void free_page(page *p)
    {
    	free(p);
    }
    
     
    #11 greki_hoy, 24 Aug 2010
    Last edited: 24 Aug 2010
    2 people like this.
  12. cheater_man

    cheater_man Member

    Joined:
    13 Nov 2009
    Messages:
    651
    Likes Received:
    44
    Reputations:
    7
    Code:
    typedef char page;
    
    page * alloc_page(unsigned sock)
    {
    	char buff[4096], *tmp;
    	page *p = 0;
    	int rec, all = 0;
    
    	do
    	{
    		rec = recv(sock, buff, sizeof(buff), 0);
    		if (rec == -1)
    		{
    			free(p);
    			return NULL;
    		}
    		if (rec > 0)
    		{
    			all += rec;
    			[SIZE=4][COLOR=DarkRed]tmp = realloc(p, all + 1);[/COLOR][/SIZE]
    			if (!tmp)
    			{
    				free(p);
    				return NULL;
    			}
    			p = tmp;
    			memmove(p + all - rec, buff, rec);
    			p[all] = '\0';
    		}
    	} while (rec > 0);
    
    	return p;
    }
    
    void free_page(page *p)
    {
    	free(p);
    }
    
    error C2440: '=' : cannot convert from 'void *' to 'char *'
     
  13. Irdis

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

    Joined:
    6 Feb 2006
    Messages:
    248
    Likes Received:
    52
    Reputations:
    3
    tmp = (char*)realloc(p, all + 1);
    (смотрю только на ошибку =) )
     
  14. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    2cheater_man
    2Irdis
    просто это C код а не C++ надо включить опцию /TC в компиляторе
    C/C++->Advanced->Compile As->Compile As C Code(/TC)
    что значит что собирать как ANSI C в нем неявное приведение из void*
    в любой другой тип абсолютно корректно в отличии от C++
    для C++ можно просто закастить
     
    #14 greki_hoy, 24 Aug 2010
    Last edited: 24 Aug 2010
  15. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,309
    Reputations:
    327
    else if (rec == 0) хде?
     
  16. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    проследи цепочку выполнения и скажи нужно ли оно там
     
  17. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,309
    Reputations:
    327
    и что? и где у тя free будет если по мсдну "If the connection has been gracefully closed, the return value is zero"


    а бред пардон надо трезветь
     
    1 person likes this.
  18. cheater_man

    cheater_man Member

    Joined:
    13 Nov 2009
    Messages:
    651
    Likes Received:
    44
    Reputations:
    7
    не мог бы проект целиком куда-нибудь залить, а то если как С компилю то:
    Error 1 error LNK2019: unresolved external symbol _recv@16 referenced in function _alloc_page 123456.obj
    Error 2 fatal error LNK1120: 1 unresolved externals
     
  19. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    проекта нет и небыло никогда это писалось еще когда slesh писал утилитку для slil.ru just for fun :)
    просто можно сделать так #pragma comment(lib, "ws2_32.lib") или Linker->Input->Additional Dependencies->ws2_32.lib