Как обычно, работа сервера начинается с инициализации слушающего сокета, выполняющейся при нажатии кнопки Запустить(листинг 2.50).
Листинг 2.50. Инициализация сервера, основанного на сообщениях
procedure TServerForm.BtnStartServerClick(Sender: TObject);
var
// Адрес, к которому привязывается слушающий сокет
ServerAddr: TSockAddr;
begin
// Формируем адрес для привязки.
FillChar(ServerAddr.sin_zero, SizeOf(ServerAddr.sin_zero), 0);
ServerAddr.sin_family := AF_INET;
ServerAddr.sin_addr.S_addr := INADDR_ANY;
try
ServerAddr.sin_port := htons(StrToInt(EditPortNumber.Text));
if ServerAddr.sin_port = 0 then
begin
MessageDlg('Номер порта должен находиться в диапазоне 1-65535',
mtError, [mbOK], 0);
Exit;
end;
// Создание сокета
FServerSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if FServerSocket = INVALID_SOCKET then
begin
MessageDlg('Ошибка при создании сокета:'#13#10 +
GetErrorString, mtError, [mbOK], 0);
Exit;
end;
// Привязка сокета к адресу
if bind(FServerSocket, ServerAddr, SizeOf(ServerAddr)) = SOCKET_ERROR then
begin
MessageDlg('Ошибка при привязке сокета к адресу:'#13#10 +
GetErrorString, mtError, [mbOK], 0);
closesocket(FServerSocket);
Exit;
end;
// Перевод сокета в режим прослушивания
if listen(FServerSocket, SOMAXCONN) = SOCKET_ERROR then
begin
MessageDlg('Ошибка при переводе сокета в режим прослушивания:'#13#10 +
GetErrorString, mtError, [mbOK], 0);
closesocket(FServerSocket);
Exit;
end;
// Связь слушающего сокета с событием FD_ACCEPT
if WSAAsyncSelect(FServerSocket, Handle,
WM_ACCEPTMESSAGE, FD_ACCEPT) = SOCKET_ERROR then
begin
MessageDlg('Ошибка при установке асинхронного режима ' +
'cлушающего сокета:'#13#10 + GetErrorString, mtError, [mbOK], 0);
closesocket(FServerSocket);
Exit;
end;
// Перевод элементов управления в состояние "Сервер работает"
LabelPortNumber.Enabled := False;
EditPortNumber.Enabled := False;
BtnStartServer.Enabled := False;
LabelServerState.Caption := 'Сервер работает';
except
on EConvertError do
// Это исключение может возникнуть только в одном месте -
// при вызове StrToInt(EditPortNumber.Text)
MessageDlg('"' + EditPortNumber.Text +
'" не является целый числом', mtError, [mbOK], 0);
on ERangeError do
// Это исключение может возникнуть только в одном месте -
// при присваивании значения номеру порта
MessageDlg('Номер порта должен находиться в диапазоне 1-65535',
mtError, [mbOK], 0);
end;
end;
Этот код мало чем отличается от того, что мы уже видели (сравните, например, с листингами 2.19 и 2.30). Единственное существенное отличие здесь — вызов функции WSAAsyncSelect
после перевода сокета в режим прослушивания. Этот вызов связывает событие FD_ACCEPT
с сообщением WM_ACCEPTMESSAGE
.
Сообщение WM_ACCEPTMESSAGE
нестандартное, мы должны сами определить его. Использовать это сообщение сервер будет только для определения момента подключения нового клиента, определять момент прихода данных мы будем с помощью другого сообщения — WM_SOCKETMESSAGE
, которое тоже нужно определить. И, чтобы легче было писать обработчики для этих сообщений, объявим тип TWMSocketMessage
, "совместимый" с типом TMessage
(листинг 2.51).
Листинг 2.51. Сообщения, связанные с сокетами, и тип TWMSocketMessage
const
WM_ACCEPTMESSAGE = WM_USER + 1;
WM_SOCKETMESSAGE = WM_USER + 2;
type
TWMSocketMessage = packed record
Msg: Cardinal;
Socket: TSocket;
SockEvent: Word;
SockError: Word;
end;
Прежде чем реализовывать реакцию на эти сообщения, нужно позаботиться об обработке ошибок. Функция GetErrorString
(см. листинг 2.6), столько времени служившая нам верой и правдой, нуждается в некоторых изменениях. Это связано с тем, что теперь код ошибки может быть получен не только в результате вызова функции WSAGetLastError
, но и через параметр SockError
сообщения. Новый вариант функции GetErrorString
иллюстрирует листинг 2.52.
Листинг 2.52. Новый вариант функции GetErrorString
// функция GetErrorString возвращает сообщение об ошибке,
// сформированное системой на основе значения, которое
// передано в качестве параметра. Если это значение
// равно нулю (по умолчанию), функция сама определяет
// код ошибки, используя функцию WSAGetLastError.
// Для получения сообщения используется системная функция
// FormatMessage.
function GetErrorString(Error: Integer = 0): string;
var
Читать дальше
Конец ознакомительного отрывка
Купить книгу