HTTP и сокеты в free pascal

Форум для изучающих FPC и их учителей.

Модератор: Модераторы

HTTP и сокеты в free pascal

Сообщение Leonius_Bad » 22.06.2014 11:56:27

Здравствуйте.
Пишу генератор и анализатор http-запросов, используя модуль sockets (без синапсов, инди и прочего).
Пока не вышел даже генератор :(
Гуглы не особо помогли. В основном опирался на справку.
Lazarus 1.2.0, Windows.

Опишу всё по порядку. Во-первых, вот мой с горем созданный код.
Код: Выделить всё
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  Sockets, resolve;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    Memo2: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;
  Sock, rc:Tsocket;
  sAddr : TSockAddr;
   sin, sout : Text;
   Line,dom : String;
   msg:string;
      rec:array[0..300] of char;//string;
      i:integer;
  hrs: THostResolver;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
  dom:=Edit1.Text;
hrs:= THostResolver.Create(nil);
hrs.NameLookup(dom);

with sAddr do
   begin
      sin_family := AF_INET;    //TCP\IP
      sin_port:= htons(80);        //Порт
      sin_addr:=StrToNetAddr(hrs.AddressAsString);
   end;

Sock:=fpsocket(AF_INET, SOCK_STREAM, 0);  //создание сокета: стек протоколов TCP\IP, Потоковый, TCP(по умолчанию)
ShowMessage('SOCK '+ IntToStr(sock)); //эти шоумесседжи для проверки создания\выполнения операции

rc:=fpconnect(Sock, @sAddr, sizeof(sAddr)); //Соединение сокета с адресом sAddr
ShowMessage('CONNECT '+ IntToStr(rc));

//сообщение для отправки. Исходные данные взяты из FireFox 30
msg := 'GET http://'+dom+'/ HTTP/1.1'+#13+#10+ // пробовал и "/" просто вместо адреса сайта целиком
'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0'+#13+#10+
'Host: '+dom+#13+#10+
'DNT: 1'+#13+#10+
'Connection: keep-alive'+#13+#10+
'Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3'+#13+#10+
'Accept-Encoding: gzip, deflate'+#13+#10+
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'+#13+#10+
#13+#10;
// Пробовал и просто одну строку типа "GET <домен> HTTP/1.1" (+пустая строка)
Memo2.Clear;
Memo2.Lines.Add(msg); //на всякий случай, дабы проверить все переносы и т.п.

rc:=fpsend(sock, @msg, sizeof(msg), 0);      //передача
ShowMessage('SEND '+ IntToStr(rc));

rc:=fprecv(sock, @rec, 72, 0); // приём ответной страницы
ShowMessage('RECV '+ IntToStr(rc));
Memo1.Lines.Add(rec);

rc:=fprecv(sock, @rec+72, 108, 0); // приём ответной страницы , след.порция
ShowMessage('RECV '+ IntToStr(rc));
Memo1.Lines.Add(rec);

Memo1.Clear;

Memo1.Lines.Add(rec);

CloseSocket(sock); 
FreeAndNil(hrs);
end;

end.         


Теперь вопросы :D
1. Самое печальное, что не один сервер ничего кроме 400 ошибки мне так и не вернул (а иные и вовсе виснут\не отвечают, как тут сделать таймаут ответа - пока не понял). Что я пишу не так в запросе? Вроде все переносы и пустая строка имеются. Может, с символами (#10 #13) что не так?
2. Не могу понять, как и где считать заголовки ответа? fprecv выдает лишь тело.
3. Как правильно считать при помощи всё той же fprecv весь ответ сразу? Мой метод с считыванием порциями (в коде черновой пример, лишь две "порции") явно кривой и нехороший.

Пока только такие вопросы. Но без их решения двигаться дальше у меня и не выйдет.
Очень надеюсь на советы и помощь :)
Leonius_Bad
новенький
 
Сообщения: 51
Зарегистрирован: 29.11.2012 19:18:07

Re: HTTP и сокеты в free pascal

Сообщение hinst » 22.06.2014 13:11:23

ну как можно таким быть? как можно посылать указатель на строку? Что возвращает SizeOf? Длину строки возвращает ф-ция Length

Добавлено спустя 1 минуту 40 секунд:
И там будет не @msg, а PChar(msg)
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: HTTP и сокеты в free pascal

Сообщение Leonius_Bad » 22.06.2014 14:04:33

hinst писал(а):ну как можно таким быть?

Да легко и просто:) Как жаль, что редко кто просто укажет на ошибку, не указав при этом на тугомозгость ошибавшегося:(
Не зря же я в подфорум "обучение" пишу).
Но безусловно признаю, что ошибся и благодарю Вас за подсказку.
Не увидел, что в сенд в любом случае 4 байта(?) уходит.

hinst писал(а):Что возвращает SizeOf?

Размер переменной или типа. Угадал? Но с Length проканало и впрямь. С сайзОф не учел, что строка без фиксированного размера всегда вернет 4 байта. Не понимаю, правда, почему) ..Возможно, т.к. он выдавал размер переменной AnsiString, кояя по сути четерхбайтовый указатель? В общем, ладно, с длиной пошло и хорошо.

Ну, с первыми двумя проблемами помогли, ещё раз большое спасибо)

А что с третьей? Как правильно и по феншую считать весь ответ? И тут же я наверняка удивляю методами (брал отсюда)
Leonius_Bad
новенький
 
Сообщения: 51
Зарегистрирован: 29.11.2012 19:18:07

Re: HTTP и сокеты в free pascal

Сообщение hinst » 22.06.2014 14:09:31

С третьим: видимо, считывать ответ пока не закроется соединение.
http://www.freepascal.org/docs-html/rtl ... precv.html
Можно попробовать считывать в цикле пока не вернёт ESockENOTCONN // и по мере того как данные приходят записывать их куда хочется
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: HTTP и сокеты в free pascal

Сообщение Leonius_Bad » 22.06.2014 14:55:28

Ну, вот так не получилось.

Код: Выделить всё
while  socketerror<>EsockENOTCONN
do begin
rc:=fprecv(sock, @rec, 300, 0); // приём ответной страницы //Тут виснет, когда данные кончаются
ShowMessage('RECV '+ IntToStr(rc)+' SE: '+inttostr(socketerror));
Memo1.Lines.Add(rec);
end;   

Сравнивать с rc тоже не выходит, ибо там, где оно по идеи должно быть -1 - оно виснет и ничего не выдаёт.
Leonius_Bad
новенький
 
Сообщения: 51
Зарегистрирован: 29.11.2012 19:18:07

Re: HTTP и сокеты в free pascal

Сообщение hinst » 22.06.2014 16:37:34

надо как-то так
Код: Выделить всё
repeat
  rc := fprecv(...)
until rc <= 0; // exit condition


Добавлено спустя 10 минут 59 секунд:
http://www.freepascal.org/docs-html/rtl ... ckopt.html
эххх
http://www.sbras.ru/cgi-bin/www/unix_he ... tsockopt+2
fpsetsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, тут в общем timeval какой-то, SizeOf(timeval) )
fpsetsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, тут то же самое...)

И вот так вот можно установить

Добавлено спустя 56 секунд:
да. нет. Либо установить неблокирующий режим, погугли там в общем по non-blocking mode sockets, fprecv fpsetsockopt, в общем там будешь работать с ними в неблокирующем режиме

Добавлено спустя 1 минуту 8 секунд:
в общем надо гуглить и читать документацию чтобы понять как с этим работать. Как вариант, читать исходный код библиотеки Synapse если совсем не получается найти нужную информацию
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: HTTP и сокеты в free pascal

Сообщение Vapaamies » 23.06.2014 06:19:36

Leonius_Bad писал(а):В общем, ладно, с длиной пошло и хорошо.

С методом тыка можно навсегда остаться начинающим. Все предположения насчет AnsiString как указателя верны.
Аватара пользователя
Vapaamies
постоялец
 
Сообщения: 291
Зарегистрирован: 24.07.2012 22:37:59
Откуда: Санкт-Петербург

Re: HTTP и сокеты в free pascal

Сообщение Leonius_Bad » 23.06.2014 15:05:51

Пока только с репитом, но с ним всё плохо,да.. Оно виснет не приёмке (fprecv) во время того, как соединение закрылось (вернуло ноль).
Т.е. "порции" у меня по 300. Первая 300-ка пришла, вторая пришла, в третьей всего 157 приняло (к примеру), пошло на следующий, где очевидно будет 0 - и вот тут повисает на несколько секунд, но отвисает позже :D Если сделать большую "порцию", то оно всё равно пойдёт за нулём.
Код: Выделить всё
Repeat
rc:=fprecv(sock, @rec, length(rec), 0); // приём ответной страницы
ShowMessage('RECV '+ IntToStr(rc)+' SE: '+inttostr(socketerror));
if rc>0 then
   Memo1.Lines.Add(rec[0..rc-1]);
until (rc<1); 

А если сделать вот так:
Код: Выделить всё
until (rc<length(rec));

Если порция пришла меньше размера буфера - закрывай лавочку. Оно будет прекрасно работать, но вот только если последняя "порция" будет равна размеру буферу - косяк повторится. Не выход. Буду думать и гуглить дальше, да.

Vapaamies писал(а):С методом тыка можно навсегда остаться начинающим. Все предположения насчет AnsiString как указателя верны.

Ну я же дал в итоге верный ответ на то, что не понимал :wink: Спасибо за подтверждение.

Добавлено спустя 34 минуты 32 секунды:
А можно еще так пришедшую информацию считать.
Код: Выделить всё
Sock2Text(Sock, Sin, sout);
Reset(sin);
  while not EOF(sin) do begin
ReadLn(sin, Line);
Memo1.Lines.Add(Line);
  end;
CloseFile(sin); 

И всё бы было в шоколаде, если бы оно по совсем мне непонятным причинам не висло где-то в цикле после прочтения последней строки :(
Может я снова совсем уж глупую ошибку тут допустил? не вижу её( Вроде бы всегда так считывал файлы.

Добавлено спустя 16 минут 35 секунд:
А вот так работает!
Код: Выделить всё
  while not EOF(sin)
  do begin
ReadLn(sin, Line);
Memo1.Lines.Add(Line);
if (Pos('</HTML>',Line)<>0) or  (Pos('</html>',Line)<>0)
then break;
  end;

Не особо быстро он считывает и заносит эти строки, но считывает же и все! И не подвисает.
Вроде бы, в http-ответе после </html> ничего идти не должно? Вроде нет, заголовки и html-страница.
Leonius_Bad
новенький
 
Сообщения: 51
Зарегистрирован: 29.11.2012 19:18:07


Вернуться в Обучение Free Pascal

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6

Рейтинг@Mail.ru