Простой HTTP-клиент - туплю :(

Тема в разделе "WASM.NETWORKS", создана пользователем iamlamer, 6 окт 2010.

  1. iamlamer

    iamlamer New Member

    Публикаций:
    0
    Регистрация:
    20 июн 2005
    Сообщения:
    273
    Адрес:
    Russia
    Задача: по шурому накатать скачивалку html-странички вместе с картинками. Проблема: чтение первой странички OK, все последующие - обломы. Вот проблемный код без всего лишнего.

    Код (Text):
    1. #include <windows.h>
    2. #include <alloc.h>
    3. #include <string.h>
    4. #include <stdio.h>
    5.  
    6. #define MAXMES 0x200
    7. #define MAXBUF 0xffff
    8.  
    9. char               ip[]={"64.142.28.160"}; // Строка IP
    10. char               get[MAXMES];            // Строка запроса GET
    11. char               sBuf[MAXBUF];           // Читаемый буфер
    12. int                iRec, iEnd, i, e;
    13. SOCKET             SendSocket;
    14. WSADATA            WSAData;
    15. SOCKADDR_IN        remoteAddr;
    16.  
    17. void getpage(char *pagename) { // GET + скачка + отображение
    18.  get[0] = 0;
    19.  strcat(get, "GET ");
    20.  strcat(get, pagename);   // Урл странички
    21.  strcat(get,  " HTTP/1.1\r\n" );  // При 1.0 не работает keep-alive !!!
    22.  strcat(get, "Accept: */*\r\n" );
    23.  strcat(get, "Accept-Encoding: gzip,deflate\r\n");
    24.  strcat(get, "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.1.4322)\r\n");
    25.  strcat(get, "Host: www.john.org\r\n");
    26.  strcat(get, "Connection: Keep-Alive\r\n");
    27.  strcat(get, "\r\n");
    28.  e = send( SendSocket, get, strlen(get), 0);
    29.  if (e==-1) printf("\nsend error %u\n", WSAGetLastError());
    30.  memset( sBuf, 0, MAXBUF); iEnd = 0; // Обнуляем
    31.  while (1) {
    32.    iRec = recv(SendSocket, (LPSTR) sBuf+iEnd, sizeof(sBuf)-iEnd, 0);
    33.    if (iRec==-1) printf("\nrecv error %u\n", WSAGetLastError());
    34.    if (!iRec) break;                        // Конец данных
    35.    iEnd += iRec;
    36.  }
    37.  printf("\nreceived %u", iEnd);
    38. }
    39.  
    40. main() {
    41.  
    42.  WSAStartup( MAKEWORD(1,1), &WSAData );
    43.  /* Создать и настроить сокет */
    44.  SendSocket = socket(AF_INET, SOCK_STREAM, 0);  // IPPROTO_TCP ?
    45.  /* Законнектитться */
    46.  memset(&remoteAddr, 0, sizeof(SOCKADDR_IN)); // Обнуляем
    47.  remoteAddr.sin_family = AF_INET;
    48.  remoteAddr.sin_port = htons(80);               // Порт
    49.  remoteAddr.sin_addr.s_addr = inet_addr(ip);    // Числовой IP
    50.  e = connect (SendSocket, (PSOCKADDR) &remoteAddr, sizeof (remoteAddr));
    51.  if (e) { printf("\nconnect error %u", WSAGetLastError()); exit(0); }
    52. /* Читаем несколько */
    53.  getpage("http://www.john.org/index.html");
    54.  getpage("http://www.john.org/BlowedUpRealGood.gif");
    55.  getpage("http://www.john.org/FrankenMac.gif");
    56.  getpage("http://www.john.org/Wireless.gif");
    57.  getpage("http://www.john.org/JohnS.gif");
    58.  
    59. // getpage("http://www.john.org/index.html", 0);
    60. // getpage("/BlowedUpRealGood.gif", 1);
    61. // getpage("/FrankenMac.gif", 1);
    62. // getpage("/Wireless.gif", 1);
    63. // getpage("/JohnS.gif", 1);
    64.  
    65.  closesocket(SendSocket);
    66.  WSACleanup();
    67. }
    Результаты выглядят примерно так:
    Код (Text):
    1. received 2860
    2. received 0
    3. received 0
    4. received 0
    5. received 0
    Имена файлов правильные, по отдельности читаются ОК. И на других ip тоже самое.

    Самое обидное, что лет несколько назад я уже делал что-то подобное, и у меня все получалось. Но исходники пропали. Пните меня - в каком месте туплю?
     
  2. Atlantic

    Atlantic Member

    Публикаций:
    0
    Регистрация:
    22 июн 2005
    Сообщения:
    322
    Адрес:
    Швеция
    iamlamer
    Судя по всему, сервак закрыл соединение после первого ответа. Нужно парсить хидеры, которые он посылает. Скорее всего, там было "Connection: close".
     
  3. iamlamer

    iamlamer New Member

    Публикаций:
    0
    Регистрация:
    20 июн 2005
    Сообщения:
    273
    Адрес:
    Russia
    Увы. :dntknw: Вот начало первой странички (правда, это не с www.john.org, а с другого сервака, на котором то же самое).

    Код (Text):
    1. HTTP/1.1 200 OK
    2. Date: Wed, 06 Oct 2010 20:05:02 GMT
    3. Server: Apache/1.3.34 (Unix) mod_throttle/3.1.2 rus/PL30.22
    4. Last-Modified: Thu, 02 Sep 2010 12:52:42 GMT
    5. Keep-Alive: timeout=5, max=100
    6. Connection: Keep-Alive
    7. Transfer-Encoding: chunked
    8. Content-Type: text/html; charset=windows-1251
    9. Vary: accept-charset, user-agent
    UPD: а вот с www.john.org:

    Код (Text):
    1. HTTP/1.1 200 OK
    2. Date: Wed, 06 Oct 2010 20:11:47 GMT
    3. Server: Apache/1.3.19 (Unix) DAV/1.0.3 PHP/4.3.1
    4. Last-Modified: Wed, 09 Jun 1999 20:51:45 GMT
    5. ETag: "2f0597-9f9-375ed3e1"
    6. Accept-Ranges: bytes
    7. Content-Length: 2553
    8. Keep-Alive: timeout=15, max=100
    9. Connection: Keep-Alive
    10. Content-Type: text/html
    Везде keep-alive.
     
  4. iamlamer

    iamlamer New Member

    Публикаций:
    0
    Регистрация:
    20 июн 2005
    Сообщения:
    273
    Адрес:
    Russia
    Забыл отписаться. Победил, разумеется, еще вчера: создал сокет - обменялся - прибил. Если кому пригодится (в Интернете одни ляля-тополя, а рабочего издохника - чтобы с одного сервера читались несколько страничек сразу - не нашел), то вот.

    Код (Text):
    1. #include <windows.h>
    2. #include <alloc.h>
    3. #include <string.h>
    4. #include <stdio.h>
    5.  
    6. #define MAXMES 0x200
    7. #define MAXBUF 0xffff
    8.  
    9. char               get[MAXMES];            // Строка запроса GET
    10. char               sBuf[MAXBUF];           // Читаемый буфер
    11. int                iRec, iEnd, i, e;
    12. SOCKET             SendSocket;
    13. WSADATA            WSAData;
    14. SOCKADDR_IN        remoteAddr;
    15.  
    16. ////////////////////////////////// GET + скачка + отображение
    17. void getpage(char *pagename, int mode) {
    18.  
    19.  /* Создать и настроить сокет */
    20.  SendSocket = socket(AF_INET, SOCK_STREAM, 0);  // IPPROTO_TCP ?
    21.  e = connect (SendSocket, (PSOCKADDR) &remoteAddr, sizeof (remoteAddr));
    22.  if (e) { printf("\nconnect error %u", WSAGetLastError()); return; }
    23.  
    24.  get[0] = 0;
    25.  strcat(get, "GET ");
    26.  strcat(get, pagename);
    27.  strcat(get,  " HTTP/1.1\r\n" );  // При 1.0 не работает keep-alive !!!
    28.  strcat(get, "Accept: */*\r\n" );
    29.  strcat(get, "Accept-Encoding: gzip,deflate\r\n");
    30.  strcat(get, "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.1.4322)\r\n");
    31.  strcat(get, "Host: www.john.org\r\n");
    32.  strcat(get, "Connection: Close\r\n");
    33.  strcat(get, "\r\n");
    34.  strcat(get, "");
    35.  e = send( SendSocket, get, strlen(get), 0);
    36.  if (e==-1) printf("\nsend error %u\n", WSAGetLastError()); else printf("\nsent %u ", e);
    37.  
    38.  memset( sBuf, 0, MAXBUF); iEnd = 0; // Обнуляем
    39.  while (1) {
    40.   iRec = recv(SendSocket, (LPSTR) sBuf+iEnd, sizeof(sBuf)-iEnd, 0);
    41.   if (iRec==-1) printf("\nrecv error %u\n", WSAGetLastError());
    42.   if (!iRec) break;                        // Конец данных
    43.   iEnd += iRec;
    44.  }
    45.  
    46.  printf("\nreceived %u", iEnd);
    47.  closesocket(SendSocket);
    48. }
    49.  
    50. main() {
    51.  
    52.  /* Данные для коннекта */
    53.  memset(&remoteAddr, 0, sizeof(SOCKADDR_IN)); // Обнуляем
    54.  remoteAddr.sin_family = AF_INET;
    55.  remoteAddr.sin_port = htons(80);               // Порт
    56.  remoteAddr.sin_addr.s_addr = inet_addr("64.142.28.160");    // Числовой IP
    57.  
    58.  WSAStartup( MAKEWORD(1,1), &WSAData );
    59.  getpage("http://www.john.org/index.html", 0);
    60.  getpage("http://www.john.org/BlowedUpRealGood.gif", 1);
    61.  getpage("http://www.john.org/FrankenMac.gif", 1);
    62.  getpage("http://www.john.org/Wireless.gif", 1);
    63.  getpage("http://www.john.org/JohnS.gif", 1);
    64.  WSACleanup();
    65.  
    66. }
    и
    Код (Text):
    1. sent 213
    2. received 2822
    3. sent 223
    4. received 18799
    5. sent 217
    6. received 19784
    7. sent 215
    8. received 12929
    9. sent 212
    10. received 3032
    Для моей задачи скорости обмена вполне хватает и избыточный трафик не напрягает. Но. Это, конечно, моветон и галимая индокитайщина. По хорошему, надо держать сокет живым и коннект открытым, как в начале топика. Пока вроде работает, и буду потихоньку разбираться дальше. Чего же ей, @#$%^& такой, не хватает? :dntknw:
     
  5. xanxy

    xanxy New Member

    Публикаций:
    0
    Регистрация:
    18 май 2010
    Сообщения:
    18
    Возможно проблемы были из за этого кода:
    Код (Text):
    1.  e = send( SendSocket, get, strlen(get), 0);
    2.  if (e==-1) printf("\nsend error %u\n", WSAGetLastError()); else printf("\nsent %u ", e);
    3.  
    4.  memset( sBuf, 0, MAXBUF); iEnd = 0; // Обнуляем
    5.  while (1) {
    6.   iRec = recv(SendSocket, (LPSTR) sBuf+iEnd, sizeof(sBuf)-iEnd, 0);
    7.   if (iRec==-1) printf("\nrecv error %u\n", WSAGetLastError());
    8.   if (!iRec) break;                        // Конец данных
    9.   iEnd += iRec;
    10.  }
    тк вы делаете send и практически сразу recv, возможно сервер не успевал ответить или еще чего..
    Вообщем нужно юзать select или евенты.
    Ну и в написании сетевых приложений, как правило пользуются WireShark'ом что бы смотреть, отправился ли ваш пакет, ответил ли сервер..
     
  6. xanxy

    xanxy New Member

    Публикаций:
    0
    Регистрация:
    18 май 2010
    Сообщения:
    18
    вот небольшой пример с select:
    Код (Text):
    1.     fd_set  read_s;
    2.     timeval time_out;
    3.     time_out.tv_sec = TIMEOUT;
    4.     time_out.tv_usec = 0;
    5.     FD_ZERO (&read_s);
    6.     FD_SET (hSocket,&read_s);
    7.     if(select (0,&read_s,NULL,NULL,&time_out)!=0)
    8.         for(time_out.tv_sec=2;;) {         
    9.             ioctlsocket(hSocket,FIONREAD,&iResult);
    10.             if(iResult < 0 ) {
    11.                 return -1;
    12.             }
    13.             else if(iResult == 0)
    14.                 break;
    15.             *Out=(char*)realloc(*Out,recvbuflen+iResult);
    16.             recv(hSocket, *Out+recvbuflen, iResult, 0);
    17.             recvbuflen+=iResult;
    18.             cout<<"recv: "<<recvbuflen<<"\n";
    19.             FD_ZERO (&read_s);
    20.             FD_SET (hSocket,&read_s);
    21.             if(select (0,&read_s,NULL,NULL,&time_out)==0)
    22.                 break;
    23.         }
    24.     else {
    25.         cout<<"TIMEOUT\n";
    26.         return 0;
    27.     }
    правельнее конечно считывать с флагом MSG_PEEK, пока не встретится \r\n\r\n, потом прочитать хидеры, из хидеров взять Content-Length, и считать оставшуюся часть..
    Но для ознакомления и так пойдет:)
     
  7. iamlamer

    iamlamer New Member

    Публикаций:
    0
    Регистрация:
    20 июн 2005
    Сообщения:
    273
    Адрес:
    Russia
    Да я думал об этом, пробовал вставлять Sleep-ы. При маленьких задержках никакой разницы, при больших сервер с той стороны рвет коннект по таймауту (ошибка 10053). Спасибо за сырок. Я планировал сочинить что-то подобное, но все мерещилось, что можно проще.