У экземпляра объекта ServerSocket есть следующие события:
- OnAccept
- OnClientConnect
- OnClientDisconnect
- OnClientError
- OnClientRead
- OnClientWrite
- OnGetSocket
- OnGetThread
- OnListen
- OnThreadEnd
- OnThreadStart
Запуск экземпляра ServerSocket например ServerSocket1 может быть произведён следующим кодом:
ServerSocket1.Port:= 5505;
ServerSocket1.Open;
Обратите внимание указывается только Port, IP адрес по какой то причине не предусмотрен компонентом, поэтому сервер будет доступен на всех IP адресах имеющихся у ПК и на указанном порту. После выполнения указанных строчек и до подключения к серверу клиентов единственное событие которое произойдёт - OnListen.
Если теперь подключится какой либо клиент(например через telnet для чего нужно выполнить telnet затем o IP_адрес порт и нажать Enter) то на ServerSocket последовательно произойдут следующие события:
- OnGetSocket
- OnClientConnect
- OnAcept
- OnClientWrite
Попробуем обратиться на порт с браузера, я для этого использовал Internet Explorer 6 и Firefox 43.0.1 и это не смотря на то что эта статья 2020 года, дело в том что на момент написания я чтобы не разбираться с особенностями работы Delphi 6 в Windows 10 установил виртуальную машину с Windows XP образ которой доступен по ссылке. Я не утверждаю, что стоит разрабатывать на Delphi 6 необходимо именно в Windows XP, в моём случаи я посчитал что такой вариант сэкономит мне время.
Так вот, при обращении на порт с ServerSocket с браузера к указанным выше 4-м событиям которые выполняются при подключении к порту клиента добавляется ещё событие OnClientRead, поскольку после соединения браузер считая что соединился с Web-сервером передаёт заголовки содержащие в частности информацию о запрашиваемой странице. При этом есть особенность, которая я пока не знаю с чем именно связана: при подключении на порт с браузера событие OnClientWrite может выполняться после OnClientRead, но в любом случаи оба эты события выполняются после события OnAcept. То есть запустим программу на Delphi и выполним ServerSocket1.Open; в одном случаи при соединении с портом с браузера(не важно IE 6 или Firefox 43.0.1) первым может выполниться событие OnClientWrite а затем OnClientRead, а в дргом аналогичном случаи эти события могут произойти в обратной последовательности, поэтому учитывайте это при разработке софта.
Если же после подключения клиента через telnet с telnet попробовать отправить какое либо сообщение(по нажатию любой буквы она отправляется в telnet сразу), то в программе на Delphi мы увидим сработавшее событие OnClientRead, если же попробовать отправить сообщение из программы на Delphi такому клиенту каких либо событий в программе не отобразится, при этом в telnet мы увидим отправленное сообщение.
Для подавления некоторых ошибок в работе ServerSocket и использовал следующий код:
procedure TForm1.ServerSocket1ClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
Socket.Close;
ErrorCode:=0;
Memo1.Lines.Add('ClientError');
end;
В данном коде закрывается Socket на котором произошла ошибка, а так же переменной кода ошибки присваивается 0. Данный код вовсе не означает что не произойдёт ошибок в результате которых работа программы завершится, например такое завершение может произойти при попытке отключить не существующего клиента, например кодом ServerSocket1.Socket.Connections[2].Close; в этом случаи если клиента нет в массиве соединений с таким индексом выполнение программы завершится ошибкой.
Особенности организации Web-сервера на компоненте ServerSocket
Возможно Вы знаете, что после того как Web-сервер принимает от клиента информацию о запрашиваемой странице с прочими заголовками запроса, далее Web-сервер отдаёт клиенту информацию с заголовками ответа а так же тело HTML страницы, при это заголовки отделяются от тела страницы двойным отступом, который в Delphi может быть добавлен к переменной строки следующим образом: #13+#10+#13+#10 то есть Enter(#13) + возврат каретки(#10) и как видно эта комбинация повторяется дважды и идут они друг за другом. Заголовки в ответе отделяются одним переносом строки(то есть #13+#10).
Касаемо самих заголовков, в Интернете я встречал статьи где при организации Web-сервера на ServerSocket предлагается в частности передавать заголовок ответа - Length который содержит длину строки содержащей HTML контент(без заголовков ответа Web-сервера и двух отступов). В Delphi если к примеру отдаваемый HTML страницы содержится в Memo1, то длина контента может быть получена следующей строкой: Length(Memo1.Text)). При этом на момент написания данной статьи в 2020 году, я посмотрел какие заголовки отдаёт Web-сервер Apache при генерации страницы с помощью PHP(версия 5.4) и увидел следующие:
- HTTP/1.1 200 OK
- Connection: Keep-Alive
- Content-Type: text/html; charset=UTF-8
- Date: Wed, 13 May 2020 01:26:25 GMT
- Expires: Thu, 19 Nov 1981 08:52:00 GMT
- Keep-Alive: timeout=5, max=100
- Server: Apache
- Transfer-Encoding: chunked
- Pragma:"no-cache"
Как можно заметить среди указанных заголовков нет заголовка Length.