Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   С/С++, C#, Delphi, .NET, Asm (https://forum.antichat.xyz/forumdisplay.php?f=24)
-   -   C++ функция recv зависает, сокеты, проблема (https://forum.antichat.xyz/showthread.php?t=125744)

awdrg 17.06.2009 19:06

C++ функция recv зависает, сокеты, проблема
 
Пишу в обучающих целях простой граббер web-страниц. Открываю сокет, отсылаю заголовок функцией send. Далее начинается чтение ответа и самого кода странички функцией recv. Тут начинаются проблемы - после чтения предпоследней порции информации функция "переходит в режим ожидания" :( Флаг MSG_PEEK не помогает. Вопрос: как избежать этого виса, возможно ли поставить recv на таймаут? Приведите пожалуйста простой пример кода с решением.
Пока решаю это проблему так: в каждой порции ответа сервера ищу "\r\n\r\n", если нахожу более 2 раз - выход из цикла приема.

d_x 17.06.2009 19:33

Когда я с помощью send-recv работал с вебом, таких проблем не было. Просто я проверял, сколько байт считано (это и возвращает recv), и если это число равно 0, то цикл чтения закончен. Простой пример кода только на асме могу привести, на нем и писал)
Лучше бы показал свой код.

razb 17.06.2009 19:55

Цитата:

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

awdrg 17.06.2009 20:01

Я так и пытался делать. Проблема в том, что если размер буфера чуть больше кол-ва оставшихся байт то он пытается что то читать (около минуты функция висит)
вот кусок кода
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;



d_x 17.06.2009 21:20

Как вариант - использовать не 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;   



awdrg 17.06.2009 21:58

Цитата:

Сообщение от d_x
Как вариант - использовать не keep-alive соединение, а close. Протокол надо использовать HTTP/1.0, а не 1.1

Хм, проблема так просто решилась, большое спасибо!

slesh 18.06.2009 00:18

а на счет таймаута для recv в моей статейке почитай

_Great_ 18.06.2009 09:30

Content-Length!

slesh 18.06.2009 09:52

2 _Great_ а если сервак еб*н*тый и непосылает Content-Length - тогда облом. )

_Great_ 18.06.2009 15:35

При кипелайв соединении обязан. Иначе браузер никогда не узнает когда посылать запрос очередной.
А при клоуз - оно и не нужно.


Время: 08:44