Socket 框架支持 Limyee 电商平台与浏览器进行实时双向通信。这允许直接向用户接收和发送编程消息。
我什么时候要使用 Socket?
当您需要将消息从服务器推送到浏览器时,Socket 很有用。一个需要考虑的简单示例是在服务器上发生操作时推送通知弹出窗口给用户。Socket 是平台内置功能(包括通知、聊天、实时主题预览和跟踪)与浏览器通信的机制。
概述
Socket 框架建立在 SignalR 之上,继承了SignalR的所有性能,稳定性和多浏览器支持。因为它是 Limyee 电商平台特定的,所以 Socket 框架还能够提供:
- 基于插件的扩展
- 内置和第三方插件都可以实现自己的 Socket,每个 Socket 定义可双向通信的服务器和客户端的方法和事件,所有都通过底层 PersistentConnection 进行复用。
- 内置用户映射
- 用户到连接的映射由 Limyee 电商平台透明地维护,允许知道哪个平台用户发送了消息,并允许将消息直接发送到与给定平台用户关联的所有活动连接。用户映射持久性得到有效维护并立即共享,无论是在单个应用程序实例还是在多个应用程序实例,它们之间都是同步。
- 内置、热插拔、横向扩展
- 为了支持多个应用程序实例,Limyee 电商平台捆绑了一个可选可安装的消息总线服务器。或者,Limyee 电商平台可以通过插件扩展性使用任何消息总线服务。要使用的消息总线服务器的配置(如果有)由基于插件的连接器定义,可以通过标准插件管理启用或禁用这些连接器,而无需重新启动应用程序。Socket 插件还公开了底层消息总线,可用作在多个实例之间保持应用程序状态实时同步。
Socket 框架支持 Limyee 电商平台中的通知、聊天、实时预览和跟踪。您也可以开发 Socket 插件,轻松提供支持其他功能。
应用程序接口
ISocket 插件
Socket 由实现 ISocket 接口的插件定义。单个 Socket 插件代表服务器和浏览器之间的单一通信通道。例如,Socket 插件可以代表与发送和接收实时聊天消息相关的所有功能。单个 Socket 插件可以发送和接收任何消息以及包含这些消息的可选数据,但发送或接收只能由同一 Socket 插件发送或接收。
Socket 插件是 ISocketController 的实例。
ISocketController 包含了IClientsController 实例,该实例包含用户连接、断开连接或发送消息的事件。IClientsController 还支持向用户发送消息或向所有用户广播。
每个启用的 Socket 插件都会生成相应的客户端 API。
Socket 插件通常还可以实现 IHtmlHeaderExtension 接口来注册 JavaScript,可以使用 JavaScript 处理或发送 Socket 消息以及插件所需的自定义逻辑。
ISocketMessageBus 插件
多应用程序实例横向扩展是通过使用实现 ISocketMessageBus 接口的插件来达到的。Limyee 电商平台捆绑了一个默认实现,但也支持与其他消息总线集成。有关详细信息,请参阅有关横向扩展的讨论。
客户端
Socket 端点
每个 ISocket 插件都实现一个 SocketName。此单个英文词字符串将成为客户端 API 的名称。例如,给定 Socket:
public class MySocket : ISocket { // ... string SocketName { get { return "mySocket"; } } // ... }
然后,以下 JavaScript API 将自动公开:
// receive and handle a message with optional data from the server $.limyee.sockets.mySocket.on(messageName, function(data) { // handle }); // send data to the server an optional object message $.limyee.sockets.mySocket.send(messageName, data);
开始
在尝试从客户端发送或接收消息之前,Socket 框架必须自行启动。这会自动发生,完成后,它会发布 socket.connected 客户端消息。
$.limyee.messaging.subscribe('socket.connected', function() { // set up message listeners or send messages });
示例 Socket
下面显示了将问候语从一个用户路由到另一个用户的 Socket 基本示例。用户提供另一个平台会员的用户名,单击“问候”,然后向他显示警报。
问候语由用户通过小组件启动,通过客户端 Socket API 发送到 ISocket 插件,然后传递给接收用户,接收用户的相同小组件实例将在 JavaScript alert() 中显示消息。
服务器端
创建一个实现 ISocket 接口的新插件。其 Name、Description 和 Initialize() 并不重要。对于 SocketName 和 SetController(),使用如下:
public string SocketName { get { return "greeter"; } } public void SetController(ISocketController controller) { controller.Clients.Received += (sender, e) => { // if this was a greting message, let's process it if (e.MessageName == "greet") { // get the associated recipient user // data passed from the client side is available on the dynamic MessageData object var to = Apis.Get<IUsers>().Get(new UsersGetOptions { Username = (string)e.MessageData.Recipient }); // get the sender user var from = Apis.Get<IUsers>().Get(new UsersGetOptions { Id = e.UserId }); // send a new message to the recipient controller.Clients.Send(to.Id.GetValueOrDefault(), "greet", new { Message = String.Format("您好!{0}。我是:{1}。", to.DisplayName, from.DisplayName) }); } }; }
客户端
创建包含以下内容的小组件:
## set up some unique ids for interface elements #set($recipientInputId = $limyee_v1_widget.UniqueId('recipient')) #set($sendLinkId = $limyee_v1_widget.UniqueId('greet')) ## basic interface <input type="text" id="$recipientInputId" /> <a href="#" id="$sendLinkId">问候</a> <script type="text/javascript"> // Wait for socket to be connected jQuery.limyee.messaging.subscribe('socket.connected', function() { // when 'Greet' is clicked, send a message to be delivered to the recipient jQuery('#$sendLinkId').on('click', function(e) { e.preventDefault(); jQuery.limyee.sockets.greeter.send('greet', { Recipient: jQuery('#$recipientInputId').val() }); }); // when a 'greet' message is received from the socket, display it as an alert jQuery.limyee.sockets.greeter.on('greet', function(data){ alert(data.Message); }); }); </script>
请注意使用 socket.connected 客户端消息以及自动生成的 jQuery.limyee.sockets.greeter API。
结果是一个简单的 UI,允许通过用户名向其他用户发送问候语。
下载
可以下载完整的示例:
using System; using Limyee.Extensibility; using Limyee.Extensibility.Api.Version1; using Limyee.Extensibility.Sockets.Version1; namespace Samples { public class Greeter : ISocket { #region IPlugin public string Name { get { return "Socket 插件问候示例"; } } public string Description { get { return "此插件将演示一个简单的 Socket 插件,该插件支持向用户发送消息。"; } } public void Initialize() { } #endregion #region ISocket public string SocketName { get { return "greeter"; } } public void SetController(ISocketController controller) { controller.Clients.Received += (sender, e) => { // if this was a greting message, let's process it if (e.MessageName == "greet") { // get the associated recipient user // data passed from the client side is available on the dynamic MessageData object var to = Apis.Get<IUsers>().Get(new UsersGetOptions { Username = (string)e.MessageData.Recipient }); // get the sender user var from = Apis.Get<IUsers>().Get(new UsersGetOptions { Id = e.UserId }); // send a new message to the recipient controller.Clients.Send(to.Id.GetValueOrDefault(), "greet", new { Message = String.Format("您好!{0}。我是:{1}。", to.DisplayName, from.DisplayName) }); } }; } #endregion } }
<scriptedContentWidgets> <scriptedContentWidget name="Greeter UI" version="2.1.0.19723" description="Sample UI corresponding to the Greeter Socket Plugin Demo" instanceIdentifier="c2bf6245923d40f49f065b091677a6e2" theme="" isCacheable="false" varyCacheByUser="false" showHeaderByDefault="true" cssClass="" > <contentScript language="Velocity"><![CDATA[## set up some unique ids for interface elements #set($recipientInputId = $limyee_v1_widget.UniqueId('recipient')) #set($sendLinkId = $limyee_v1_widget.UniqueId('greet')) ## basic interface <input type="text" id="$recipientInputId" /> <a href="#" id="$sendLinkId">问候</a> <script type="text/javascript"> // Wait for socket to be connected jQuery.limyee.messaging.subscribe('socket.connected', function() { // when 'Greet' is clicked, send a message to be delivered to the recipient jQuery('#$sendLinkId').on('click', function(e) { e.preventDefault(); jQuery.limyee.sockets.greeter.send('greet', { Recipient: jQuery('#$recipientInputId').val() }); }); // when a 'greet' message is received from the socket, display it as an alert jQuery.limyee.sockets.greeter.on('greet', function(data){ alert(data.Message); }); }); </script>]]></contentScript> </scriptedContentWidget> </scriptedContentWidgets>
横向扩展
概述
Socket 消息总线由 Socket 框架在内部使用,当 ISocket 插件使用通用消息总线,是通过注入的 IMessageBusController 实例,以便在发送消息时将消息推送到多个应用程序实例。
当只需要单个应用程序实例时,Socket 消息总线可使用内存总线。当需要多个应用程序实例时,无法使用内存总线。相反, 必须启用实现 ISocketMessageBus 的插件。
Socket 消息总线服务器
Limyee 电商平台附带了可选安装的 Socket Message Bus Windows Service 及其相应的 ISocketMessageBus 插件式连接器:Socket Message Bus Service Connector。这只是一个 Windows 服务,它将它收到的消息回显到所有连接的 Limyee 电商平台实例。
自定义消息总线
如果平台更喜欢使用现有的第三方消息总线解决方案,则可以针对它而编写自定义的 ISocketMessageBus 插件并进行部署。例如,消息总线服务支持单个总线服务器上的多个 Limyee 电商平台平台,方法是可以向插件发送和接收的字符串消息添加标识符,以便在总线服务器上路由消息。
通用消息
ISocket 插件中的自定义逻辑也可以使用消息总线。通用消息可用于在应用程序实例之间保持实时状态同步。例如,聊天状态可能保留在数据库中,但在多个实例中本地缓存。由于这些缓存保持同步非常重要,因此可以通过消息总线发送和接收状态更改消息,以便其他应用程序实例的插件进行处理。
注入到 ISocket 插件中的 ISocketController 包含一个 IMessageBusController 的实例。IMessageBusController 公开从应用程序实例接收通用消息的事件以及发送这些消息的方法。
状态更改的一个有用模式是向总线发送通用消息,并且仅在重新接收后才对其进行处理。这允许所有实例了解状态。此外,发送它的实例会立即收到消息,而不必等待往返,并且会知道消息的本地来源。通过这种方式,发送方还可以专门负责可能将状态更改保留在其他地方。
controller.MessageBus.Publish("something.happened",""); controller.MessageBus.Received += (sender, e) => { if (e.MessageName == "something.happened") { // update some local caches // ... if (e.Source == BusMessageSource.Local) { // update DB // ... } } };
提示和技巧
- Socket 只能由经过身份验证的非系统用户使用