Используйте mORMot Framework для отправки сообщения между сервером и клиентами
В mORMot Framework ( http://www.synopse.info/) добавлена поддержка WebSockets, на упаковке также есть демонстрационная версия о WebSockets (пример 31). В этом примере клиент отправляет сообщение на сервер, и этот ответ с новым сообщением клиенту. Я хотел бы использовать эту библиотеку, чтобы сделать это:
- Клиент отправляет сообщение на сервер и отправляет также IP-адрес (без ожидания сообщения от сервера.);
- Сервер может отправить сообщение одному клиенту по IP-адресу;
Примечание: IP-адрес используется только для идентификации клиента. Я могу использовать также уникальное имя.
Что-то вроде LAN-чата между клиентами и сервером. Я не понимаю, как редактировать образец n. 31, чтобы сделать это. Этот образец основан на интерфейсе.
1 ответ
Не нужно хранить IP или какой-либо параметр реализации низкого уровня (кстати, IP не сможет идентифицировать соединение уникальным образом: несколько клиентов могут использовать один и тот же IP).
В платформе mORMot асинхронные обратные вызовы реализуются через параметры интерфейса. На стороне сервера каждый экземпляр этого параметра на самом деле был бы "поддельным" экземпляром класса, связанным с входным соединением, способным вызывать клиент обратно через его методы интерфейса.
Это довольно простой способ реализации обратных вызовов - на самом деле это хороший способ реализации обратных вызовов SOLID на стороне сервера, а инфраструктура mORMot позволяет публиковать этот механизм клиент-серверным способом, используя WebSockets.
Итак, вы сначала определяете интерфейс обратного вызова и интерфейс службы:
IChatCallback = interface(IInvokable)
['{EA7EFE51-3EBA-4047-A356-253374518D1D}']
procedure BlaBla(const pseudo, msg: string);
end;
IChatService = interface(IInvokable)
['{C92DCBEA-C680-40BD-8D9C-3E6F2ED9C9CF}']
procedure Join(const pseudo: string; const callback: IChatCallback);
procedure BlaBla(const pseudo,msg: string);
procedure CallbackReleased(const callback: IInvokable);
end;
Затем на стороне сервера каждый вызов IChatService.Join()
подписался бы на внутренний список соединений:
TChatService = class(TInterfacedObject,IChatService)
protected
fConnected: array of IChatCallback;
public
procedure Join(const pseudo: string; const callback: IChatCallback);
procedure BlaBla(const pseudo,msg: string);
procedure CallbackReleased(const callback: IInvokable);
end;
procedure TChatService.Join(const pseudo: string;
const callback: IChatCallback);
begin
InterfaceArrayAdd(fConnected,callback);
end;
Затем удаленный вызов IChatService.BlaBla()
метод должен быть передан всем подключенным клиентам, просто вызвав IChatCallback.BlaBla()
метод:
procedure TChatService.BlaBla(const pseudo,msg: string);
var i: integer;
begin
for i := 0 to high(fConnected) do
fConnected[i].BlaBla(pseudo,msg);
end;
Обратите внимание, что все вызовы цикла IChatCallback.BlaBla()
будет выполняться через WebSockets асинхронным и неблокирующим образом, так что даже в случае большого количества клиентов IChatService.BlaBla()
метод не будет блокировать В случае большого количества сообщений платформа может даже собирать сообщения push-уведомлений в одно сообщение, чтобы сократить использование ресурсов.
Следующий метод будет вызываться сервером, когда клиентский экземпляр обратного вызова освобождается (либо явно, либо если соединение разорвано), поэтому его можно использовать для отмены подписки на уведомление:
procedure TChatService.CallbackReleased(const callback: IInvokable);
begin
InterfaceArrayDelete(fConnected,callback);
end;
На стороне сервера вы определяете службу как таковую:
Server.ServiceDefine(TChatService,[IChatService],sicShared).
SetOptions([],[optExecLockedPerInterface]);
Здесь optExecLockedPerInterface
опция была установлена, чтобы все вызовы методов были сделаны потокобезопасными, чтобы параллельный доступ к внутреннему fConnected[]
список будет в безопасности.
На стороне клиента вы реализуете IChatCallback
интерфейс обратного вызова:
type
TChatCallback = class(TInterfacedCallback,IChatCallback)
protected
procedure BlaBla(const pseudo, msg: string);
end;
procedure TChatCallback.BlaBla(const pseudo, msg: string);
begin
writeln(#13'@',pseudo,' ',msg);
end;
Затем вы подписываетесь на свой удаленный сервис как таковой:
var Service: IChatService;
callback: IChatCallback;
...
Client.ServiceDefine([IChatService],sicShared);
if not Client.Services.Resolve(IChatService,Service) then
raise EServiceException.Create('Service IChatService unavailable');
...
callback := TChatCallback.Create(Client,IChatCallback);
Service.Join(pseudo,callback);
...
try
repeat
TextColor(ccLightGray);
readln(msg);
if msg='' then
break;
Service.BlaBla(pseudo,msg);
until false;
finally
callback := nil;
Service := nil; // release the service local instance BEFORE Client.Free
end;
Если вы сравните с существующими клиент-серверными SOA-решениями (в Delphi, Java, C# или даже в Go или других средах), этот механизм обратного вызова на основе интерфейса звучит довольно уникально и с ним легко работать. Конечно, это работает от Delphi 6 до XE7, а также под Linux благодаря FPC или CrossKylix.
Я загрузил полный пример исходного кода в наш репозиторий, как на стороне сервера, так и на стороне клиента.