Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Работа с сетью в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 20.12.2013, 16:02   #1
Lardes
Форумчанин
 
Аватар для Lardes
 
Регистрация: 19.08.2011
Сообщений: 329
По умолчанию Winsock API HTTP Server

Всем доброго времени суток! Есть код:
Код:
program HTTPServer;

{$APPTYPE CONSOLE}

uses
  Winapi.Windows,
  System.Classes,
  System.SysUtils,
  Winapi.WinSock;

var
  WSAData: TWSAData;
  ServerSocket, ClientSocket: TSocket;
  InAddr, FromAddr: sockaddr_in;
  LenSock: Integer;

procedure ProcessConnection(AClientSocket: TSocket);
var
  Socket: TSocket;
  buff: array [0..16384] of AnsiChar;

  str: AnsiString;
  RecvBytes: Integer;

  ContentStream: TMemoryStream;
begin
  ContentStream := TMemoryStream.Create;

  Socket := AClientSocket;

  repeat
    FillChar(buff, SizeOf(buff), 0);
    RecvBytes := recv(Socket, buff, SizeOf(buff), 0);

    WriteLn('Size: ', RecvBytes);
    // WriteLn('Error ' + IntToStr(WSAGetLastError));

    if RecvBytes > 0 then
    begin
      ContentStream.Write(buff, RecvBytes);
    end
    else
      Break;

  until false;

  ContentStream.SaveToFile(ExtractFilePath(ParamStr(0)) + FormatDateTime('dd-mm hh-mm-ss', Now) + '.dat');

  str := 'HTTP/1.1 200 OK'#13#10#13#10 + '<!DOCTYPE html><html><head><title>Отправка файла на сервер</title>' +
    '</head><body><form enctype="multipart/form-data" method="post"><p><input type="file" name="f">' +
    '<input type="submit" value="Отправить"></p></form></body></html>';
  send(Socket, str[1], Length(str), 0);

  WriteLn('Disconnect');
  WriteLn('');
  closesocket(Socket);

  ContentStream.Free;
end;

begin
  WriteLn('running...');

  if WSAStartup($202, WSAData) <> 0 then
  begin
    WriteLn('Error WSAStartup ' + IntToStr(WSAGetLastError));
    Exit;
  end;

  WriteLn('started...');
  ServerSocket := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if ServerSocket < 0 then
  begin
    WriteLn('Error socket ' + IntToStr(WSAGetLastError));
    Exit;
  end;

  WriteLn('sock init...');

  InAddr.sin_family := AF_INET;
  InAddr.sin_port := htons(8080);
  InAddr.sin_addr.S_addr := INADDR_ANY;

  if bind(ServerSocket, InAddr, SizeOf(InAddr)) <> 0 then
  begin
    WriteLn('Error bind ' + IntToStr(WSAGetLastError));
    Exit;
  end;

  WriteLn('binded...');

  if listen(ServerSocket, SOMAXCONN) <> 0 then
  begin
    WriteLn('Error listen ' + IntToStr(WSAGetLastError));
    exit;
  end;

  WriteLn('Listening port 8080...');

  while True do
  begin
    LenSock := SizeOf(FromAddr);
    ClientSocket := accept(ServerSocket, @FromAddr, @LenSock);

    if ClientSocket <> INVALID_SOCKET then
    begin
      WriteLn('Connected: ', Format('%u.%u.%u.%u:%u', [Ord(FromAddr.sin_addr.S_un_b.s_b1),
        Ord(FromAddr.sin_addr.S_un_b.s_b2), Ord(FromAddr.sin_addr.S_un_b.s_b3),
        Ord(FromAddr.sin_addr.S_un_b.s_b4), ntohs(FromAddr.sin_port)]));

      ProcessConnection(ClientSocket);
    end;
  end;

  WriteLn('sock close...');

  closesocket(ServerSocket);

  WriteLn('cleanup...');

  WSACleanUp;

  WriteLn('terminated...');

end.
Возможно, некоторые поняли, что это код HTTP-сервера, но проблема в том, что до строчки
Код:
  ContentStream.SaveToFile(ExtractFilePath(ParamStr(0)) + FormatDateTime('dd-mm hh-mm-ss', Now) + '.dat');
не доходит... Пробовал различные методы - делал сокет неблокирующим и в этом случае ответ от клиента доходит не всегда полным (т.е. либо он полный, либо приходят только HTTP-заголовки). Меня давно мучает вопрос как исправить подобное дело... Были предложения использовать таймер, но по-моему это не есть решение вопроса, так как может быть, что клиент за определенное время не успеет отправить ответ (файл) и сокет разорвет соединение... Как быть?
Lardes вне форума Ответить с цитированием
Старый 20.12.2013, 22:15   #2
Son Of Pain
Участник клуба
 
Регистрация: 23.12.2010
Сообщений: 1,129
По умолчанию

Ну да. Если ты вызываешь recv на блокирующем сокете, когда там уже нет данных - он не вернет 0, как предполагает твой код, а будет ждать, пока данные не появятся. Потому выполнение никогда и не доходит до конца цикла.

Правильный способ - определять по самим полученным данным, когда заканчивается запрос, и не делать после этого лишний recv.
Son Of Pain вне форума Ответить с цитированием
Старый 20.12.2013, 22:46   #3
Lardes
Форумчанин
 
Аватар для Lardes
 
Регистрация: 19.08.2011
Сообщений: 329
По умолчанию

Son Of Pain, а каким образом это определять?
Lardes вне форума Ответить с цитированием
Старый 20.12.2013, 23:18   #4
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,097
По умолчанию

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

P.S. Могу показать класс сервера на сокетах, который я для себя писал.
I'm learning to live...
Stilet вне форума Ответить с цитированием
Старый 20.12.2013, 23:26   #5
Son Of Pain
Участник клуба
 
Регистрация: 23.12.2010
Сообщений: 1,129
По умолчанию

Если это запрос get, например - конец можно определить по пустой строке после заголовков.
Если post - там будет поле content-length, из которого можно взять длину данных.
Son Of Pain вне форума Ответить с цитированием
Старый 21.12.2013, 02:13   #6
Lardes
Форумчанин
 
Аватар для Lardes
 
Регистрация: 19.08.2011
Сообщений: 329
По умолчанию

Stilet, с неблокирующим сокетом уже пробовал - безрезультативно. Как-то странно ведет себя сервер (то принимает все, либо принимает часть). За пример был бы признателен =) Son Of Pain, соглашусь с Вами, как альтернатива может решить вопрос.
Lardes вне форума Ответить с цитированием
Старый 21.12.2013, 08:01   #7
Son Of Pain
Участник клуба
 
Регистрация: 23.12.2010
Сообщений: 1,129
По умолчанию

Цитата:
Сообщение от Lardes Посмотреть сообщение
Stilet, с неблокирующим сокетом уже пробовал - безрезультативно. Как-то странно ведет себя сервер (то принимает все, либо принимает часть).
Почему странно? Для неблокирующего сокета recv вернет ту часть данных, которая пришла в момент вызова, и дальше будет отправлять ошибку до тех пор, пока новые данные не появятся. При одном запуске к моменту вызова успел придти запрос целиком; при другом - только частично.

К тому же в данной ситуации неблокирующий сокет не даст абсолютно никакого преимущества. Сейчас ты делаешь recv в цикле, а так будешь делать select + recv; и в обоих случаях должен будешь смотреть на полученные данные, чтобы понять, когда нужно остановиться.
Son Of Pain вне форума Ответить с цитированием
Старый 21.12.2013, 08:06   #8
Slym
Участник клуба
 
Регистрация: 07.12.2011
Сообщений: 1,025
По умолчанию

тебе обязательно на чистом API?
тут на TServerSocket http://www.programmersforum.ru/showthread.php?t=217802
Не стесняемся, плюсуем!
Slym вне форума Ответить с цитированием
Старый 21.12.2013, 11:38   #9
Lardes
Форумчанин
 
Аватар для Lardes
 
Регистрация: 19.08.2011
Сообщений: 329
По умолчанию

Son Of Pain, благодарю. Учту это. Slym, TServerSocket тоже порой "тупит" - с неблокирующим режимом тоже не все хорошо
Lardes вне форума Ответить с цитированием
Старый 21.12.2013, 11:57   #10
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,097
По умолчанию

Цитата:
За пример был бы признателен =)
Это легко устроить, но предупреждаю - разработка сыроваста. Особо заморачиваться я не стал.
В аттаче сервер и всякая всячина
Приемер заюза:
Код:
  fphttp:=TFPServer.Create(true);
  fphttp.Port:=80;
  fphttp.ServType:=FPTypeHTTP;
  fphttp.OnRead:=@FPHttpGet;
  fphttp.Resume;
...
procedure TForm1.FPHttpGet(Sender: TObject);
var h:TFPServerOnReadThread;
begin
  init;
  h:=TFPServerOnReadThread(sender);
   h.DataStr.WriteString('Hello world');
end;
Код на Лазаре, но все моменты на Винапи.
Вложения
Тип файла: rar fpsockets.rar (8.9 Кб, 31 просмотров)
I'm learning to live...
Stilet вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Winsock Client Server fucil Работа с сетью в Delphi 10 06.09.2012 00:37
считывание фала с http (winsock) bananasus C/C++ Сетевое программирование 10 20.11.2011 16:56
http server,http сервер, проблемы с post реализация на java snajper_ro Общие вопросы по Java, Java SE, Kotlin 1 23.10.2011 14:25
Winsock и http Armatus Win Api 10 06.04.2011 01:25
Winsock http-запросы SmartCream Работа с сетью в Delphi 5 14.03.2011 15:34