Сообщение WM_ACCEPTMESSAGE
нестандартное, мы должны сами определить его. Использовать это сообщение сервер будет только для определения момента подключения нового клиента, определять момент прихода данных мы будем с помощью другого сообщения — WM_SOCKETMESSAGE
, которое тоже нужно определить. И, чтобы легче было писать обработчики для этих сообщений, объявим тип TWMSocketMessage
, "совместимый" с типом TMessage
(листинг 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.
GetErrorString
// функция GetErrorString возвращает сообщение об ошибке,
// сформированное системой на основе значения, которое
// передано в качестве параметра. Если это значение
// равно нулю (по умолчанию), функция сама определяет
// код ошибки, используя функцию WSAGetLastError.
// Для получения сообщения используется системная функция
// FormatMessage.
function GetErrorString(Error: Integer = 0): string;
var
Buffer: array[0..2047] of Char;
begin
if Error = 0 then Error := WSAGetLastError;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, Error, $400,
@Buffer, SizeOf(Buffer), nil);
Result := Buffer;
end;
Сам обработчик сообщения WM_ACCEPTMESSAGE
приведен в листинге 2.53.
WM_ACCEPTMESSAGE
procedure TServerForm.WMAcceptMessage(var Msg: TWMSocketMessage);
var
NewConnection: PConnection;
// Сокет, который создаётся для вновь подключившегося клиента
ClientSocket: TSocket;
// Адрес подключившегося клиента
ClientAddr: TSockAddr;
// Длина адреса
AddrLen: Integer;
begin
// Страхуемся от "тупой" ошибки
if Msg.Socket <> FServerSocket then
raise ESocketError.Create(
'Внутренняя ошибка сервера - неверный серверный сокeт');
// Обрабатываем ошибку на сокете, если она есть.
if Msg.SockError <> 0 then
begin
MessageDlg('Ошибка при подключении клиента:'#13#10 +
GetErrorString(Msg.SockError) +
#13#10'Сервер будет остановлен', mtError, [mbOK], 0);
ClearConnections;
closesocket(FServerSocket);
OnStopServer;
Exit;
end;
// Страхуемся от еще одной "тупой" ошибки
if Msg.SockEvent <> FD_ACCEPT then
raise ESocketError.Create(
'Внутренняя ошибка сервера — неверное событие на сокете');
AddrLen := SizeOf(TSockAddr);
ClientSocket := accept(FServerSocket, @ClientAddr, @AddrLen);
if ClientSocket = INVALID_SOCKET then
begin
// Если произошедшая ошибка - WSAEWOULDBLOCK, это просто означает,
// что на данный момент подключений нет, а вообще все в порядке,