Пишу в обучающих целях простой граббер web-страниц. Открываю сокет, отсылаю заголовок функцией send. Далее начинается чтение ответа и самого кода странички функцией recv. Тут начинаются проблемы - после чтения предпоследней порции информации функция "переходит в режим ожидания" Флаг MSG_PEEK не помогает. Вопрос: как избежать этого виса, возможно ли поставить recv на таймаут? Приведите пожалуйста простой пример кода с решением. Пока решаю это проблему так: в каждой порции ответа сервера ищу "\r\n\r\n", если нахожу более 2 раз - выход из цикла приема.
Когда я с помощью send-recv работал с вебом, таких проблем не было. Просто я проверял, сколько байт считано (это и возвращает recv), и если это число равно 0, то цикл чтения закончен. Простой пример кода только на асме могу привести, на нем и писал) Лучше бы показал свой код.
Я так и пытался делать. Проблема в том, что если размер буфера чуть больше кол-ва оставшихся байт то он пытается что то читать (около минуты функция висит) вот кусок кода 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_INET, SOCK_STREAM, 0 ); if ( sock < 0 ) { 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 { closesocket( sock ) ; WSACleanup(); return "[Bad address]"; } if(connect( sock, (sockaddr *)&dest_addr, sizeof(dest_addr) )) { return "[Connection error]"; } send( sock, buff2, strlen(buff2), 0); int x = 1; FILE * ff = fopen("log.txt","w"); while (x>0) { x = recv( sock, buff, 256, 0 ); // 256 - buffer size strcat(text,buff); fputs(buff,ff); } fclose(ff); return text; }
Как вариант - использовать не 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_INET, SOCK_STREAM, 0 ); if ( sock < 0 ) { 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 { closesocket( sock ) ; WSACleanup(); return 0; } if(connect( sock, (sockaddr *)&dest_addr, sizeof(dest_addr) )) { return 0; } send( sock, buff2, strlen(buff2)-1, 0); int x = 255; FILE * ff = fopen("log.txt","w"); while (x>0) { x = recv( sock, buff, 255, 0 ); // 256 - buffer size printf("Got %u bytes\n",x); fwrite(buff,1,x,ff); } printf("finished\n",x); closesocket( sock ) ; WSACleanup(); fclose(ff); return 1; } int main() { myconnect(); return 0; }
При кипелайв соединении обязан. Иначе браузер никогда не узнает когда посылать запрос очередной. А при клоуз - оно и не нужно.
Хм может еще кто нибудь знает: есть в С++ аналог функции urlencode()? Для преобразования русских символов в url вид?
например 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; }
awdrg Если знать, почему может, а почему не может быть, будешь заодно знать и есть ли. krypt3r Это что еще за пи##ец? Откуда sizeof(char*) ? Почему заполняется нулями только треть буффера - len+1 ? Зачем вообще заполнение нулями, если вызывается calloc? Почему код вообще на Си, когда просили на С++? В чем сакральный смысл приведения char* к тиму char*? Садись, два