Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Сети > Аналог InternetReadFile на сокетах


Автор: Maks1509 13.11.2008, 14:59
Здраствуйте. Необходимо скачать текстовый файл с сервера через сокеты. Раньше использовал WinInet функции. Соединялся, проверял ответ от сервера и заполнял буфер данными, которые потом использовал по назначению.

Код был такой:

Код

function GetTextFileFromServer(szURL : AnsiString) : AnsiString;
var
  FSession  : HINTERNET;
  hConnect  : HINTERNET;
  szBuffer  : Array [0..4095] of AnsiChar;
  dwIndex   : DWORD;
  dwCodeLen : DWORD;
  hRequest  : LongBool;
  szNotify  : AnsiString;
  dwTimeout : Integer;
  dwRead    : Cardinal;
begin
  Result := '';
  try
    { открываем интернет сессию для выполнения последующих действий }
    FSession := InternetOpen('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
    { устанавливаем таймер на сессию - 10 секунд }
    dwTimeout := 10 * 1000;
    InternetSetOption(FSession, INTERNET_OPTION_CONNECT_TIMEOUT, @dwTimeout, SizeOf(dwTimeout));
    InternetSetOption(FSession, INTERNET_OPTION_RECEIVE_TIMEOUT, @dwTimeout, SizeOf(dwTimeout));
    InternetSetOption(FSession, INTERNET_OPTION_SEND_TIMEOUT, @dwTimeout, SizeOf(dwTimeout));
    if FSession <> nil then
    try
      { получаем хэндл открытого файла по сети }
      hConnect := InternetOpenUrl(FSession, PAnsiChar(szURL), nil, 0, INTERNET_FLAG_NO_UI, 0);
      if hConnect <> nil then
      try
        { проверяем открытое соединение на ошибки }
        dwIndex  := 0;
        dwCodeLen := Length(szBuffer);
        hRequest := HttpQueryInfo(hConnect, HTTP_QUERY_STATUS_CODE, @szBuffer[0], dwCodeLen, dwIndex);
        if hRequest then
          begin
            szNotify := szBuffer;
            { если код ошибки 200 - все прошло нормально и продолжаем }
            if szNotify = '200' then
              begin
                repeat
                  FillChar(szBuffer, SizeOf(szBuffer), 0);
                  if InternetReadFile(hConnect, @szBuffer[0], SizeOf(szBuffer), dwRead) then
                    Result := Result + szBuffer
                  else
                    Break;
                until
                  dwRead = 0;
              end;
          end;
      finally
        InternetCloseHandle(hConnect);
      end;
    finally
      InternetCloseHandle(FSession);
    end;
  except
  end;
end;


Собственно до поры дов ремени мне было этого достаточно. Но недостаток этого кода в том фиг знает, но если что-то пошло не так с первого раза после запуска приложения - файл не качается - хоть до посинения вызывай эту функцию...

Решил скачивать файл через сокеты. Посмотрел что люди пишут и решил написать код:

Код

function GetHttpData(szURL, szMethod : AnsiString; dwPort : WORD; szArgs, szContent : AnsiString) : AnsiString;
var
  hSocket  : TSocket;
  SockAddr : TSockAddr;
  szBuffer : Array [0..1023] of AnsiChar;
  BuffSize : Cardinal;
  WSAData  : TWSAData;
  szTemp   : AnsiString;
  szHost   : AnsiString;
  sHostEnt : PHostEnt;
  sTimeout : Integer;
begin
  { возвращаем пустой результат в функцию }
  Result := '';
  { извлекаем название хоста для соединения }
  szTemp := szURL;
  if Pos('//', szTemp) > 0 then
    Delete(szTemp, 1, Pos('//', szTemp) + 1);
  szHost := Copy(szTemp, 1, Pos('/', szTemp) - 1);
  { запускаем библиотеку winsock версии 2 }
  if WSAStartup(MAKEWORD(2, 0), WSAData) <> NOERROR then
    Exit;
  { пытаемся извлечь название хоста }
  sHostEnt := GetHostByName(PAnsiChar(szHost));
  if sHostEnt = nil then
    begin
      WSACleanup;
      Exit;
    end;
  { создаем сокет с нужными нам параметрами }
  hSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
  if hSocket = INVALID_SOCKET then
    begin
      WSACleanup;
      Exit;
    end;
  FillChar(SockAddr, SizeOf(sockaddr_in), 0);
  SockAddr.sin_family := AF_INET;
  SockAddr.sin_addr   := PInAddr(sHostEnt^.h_addr_list^)^;
  SockAddr.sin_port   := htons(dwPort);
  { устанавливаем интервалы таймаута }
  sTimeout := 1000000;
  setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, @sTimeout, SizeOf(Integer));
  setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, @sTimeout, SizeOf(Integer));
  { если не удалось соединиться - останавливаемся }
  if connect(hSocket, SockAddr, SizeOf(SockAddr)) = SOCKET_ERROR then
    begin
      shutdown(hSocket, SD_BOTH);
      closesocket(hSocket);
      WSACleanup;
      Exit;
    end;  { формируем строку для отправки запроса }
  szTemp := Format('%s %s HTTP/1.1' + sLineBreak +
                   '%s' + sLineBreak +
                   'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)' + sLineBreak +
                   'Host: %s' + sLineBreak +
                   'Content-Length: %s' + sLineBreak +
                   'Connection: Keep-Alive' + sLineBreak +
                   'Cache-Control: no-cache' + sLineBreak + sLineBreak +
                   szArgs, [szMethod, szURL, szContent, szHost, IntToStr(Length(szArgs))]);
  { отправляем через сокет запрос }
  if send(hSocket, Pointer(szTemp)^, Length(szTemp), 0) = SOCKET_ERROR then
    begin
      shutdown(hSocket, SD_BOTH);
      closesocket(hSocket);
      WSACleanup;
      Exit;
    end;
  { получаем результат и сохраняем его }
  FillChar(szBuffer, SizeOf(szBuffer), 0);
  repeat
    BuffSize := recv(hSocket, szBuffer, SizeOf(szBuffer), 0);
    if BuffSize > 0 then
      begin
        Result := Result + PAnsiChar(@szBuffer);
        FillChar(szBuffer, SizeOf(szBuffer), 0);
      end;
  until
    BuffSize > 0;
  { очищаем буфер, закрываем сокет и выгружаем winsock }
  FillChar(szBuffer, SizeOf(szBuffer), 0);
  shutdown(hSocket, SD_BOTH);
  closesocket(hSocket);
  WSACleanup;
end;

Вроде бы все работает нормально. Вслучае неудачного соединения или какой-то ошибки сокет благополучно закрывается.

Вопрос следующий. Я хочу скачать текстовый файл с сервера и получить в результате только именно текст, находящийся в этом файле. Естественно приходит информация побольше, содержащая всякие Content-Type, Keep-Alive, Content-Length и какие-то цифры/буквы посреди которых данные с текстового файла. Как мне выцепить только эти данные?

На 200 код сделал такую проверку. Кусок кода:

Код

 s := GetHttpData('ссылка', 'GET', 80, '', 'Content-Type: text/plain');
  if Pos('HTTP/1.1 200 OK', s) > 0 then
    begin
      p := Pos(#13#10 + #13#10, s);
      s := Copy(s, p + 4, Length(s) - p + 4);
    end
  else
    s := 'пусто';


Может быть как-то еще можно выполнить проверку на удачность соединения? Или как-то в параметрах подключения у сокетов указать?

Спасибо.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)