`
shazhifeng
  • 浏览: 125232 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Private Messages with cometD Chat

阅读更多

One of the common misconceptions regarding cometD, is that it can only do publish-subscribe messaging. While this misconception may be encouraged by the protocol design, it is definitely possible to do private messaging with cometD. In this article, I’ll look as some of the recent additions to the cometD chat demo and how they use private messages to implement member lists and private chat.

The basics of the cometd chat demo is definitely publish subscribe. All clients subscribe to the “/chat/demo” channel to receive chat, and publish to the same channel to send chat:

dojox.cometd.subscribe("/chat/demo", room, "_chat");
dojox.cometd.publish("/chat/demo", {
	user: room._username,
	join: true,
	chat: room._username + " has joined"
});

The cometD server is able to support this style of chat without any server-side chat specific components. But a chat room without a members list is a pretty basic chat room, and for that we need to introduce some server-side services to track members. For this demo, tracking members is a little harder than normal, because we are not running behind any authentication, so we cannot easily identify the user. If we had a real authentication mechanism in place, tracking users would simply be a matter of creating a ClientBayeuxListener instance and implementing the clientAdded and clientRemoved methods. So without authentication, the demo trusts the clients to tell us who they are in a join message.

So on the server, we create a ChatService that extends BayeuxService and registers to listen for all messages to chat channels:

  public class ChatService extends BayeuxService
  {
    private final ConcurrentMap<String, Map<String, String>> _members = 
      new ConcurrentHashMap<String, Map<String, String>>();
 
    public ChatService(Bayeux bayeux)
    {
      super(bayeux, "chat");
      subscribe("/chat/**", "trackMembers");
    }

This requests that the trackMembers method be called for all messages to “/chat/**”. This method is implemented to trigger on the join messages and to find/create a map of username to cometD clientId for each room encountered:

    public void trackMembers(final Client joiner, final String channelName, 
      Map<String, Object> data)
    {
      if (Boolean.TRUE.equals(data.get("join")))
      {
        Map<String, String> membersMap = _members.get(channelName);
        if (membersMap == null)
        {
          Map<String, String> newMembersMap = new 
            ConcurrentHashMap<String, String>();
          membersMap = _members.putIfAbsent(channelName, 
            newMembersMap);
          if (membersMap == null) membersMap = newMembersMap;
        }

The joining user is then added to the map of all users in the chat room and the updated set of all user names is published to the channel so that all clients receive the list:

        final String userName = (String)data.get("user");
        members.put(userName, joiner.getId());
        getBayeux().getChannel(channelName, false)
          .publish(getClient(), members.keySet(), null);

As well as joining the chat room, we need to track the leaving the chat room. The most reliable way to do this is to register a RemoveListener against the client, which is called if the client is removed from cometD for any reason:

        final Map<String, String> members = membersMap;
        joiner.addListener(new RemoveListener()
        {
          public void removed(String clientId, boolean timeout)
          {
            members.values().remove(clientId);
            Channel channel = getBayeux().getChannel(channelName, false);
            if (channel != null) 
              channel.publish(getClient(), members.keySet(), null);
          }
      });
    }
  }

So that was a little more involved that if we had an authentication mechanism, but it’s simple enough and we now have the server side tracking our users and maintaining a map between username and cometD clientID. This makes it relatively simple to add a service for private messages between users. We start by adding another subscription to the ChatService for private messages:

    public ChatService(Bayeux bayeux)
    {
        super(bayeux, "chat");
        subscribe("/chat/**", "trackMembers");
        subscribe("/service/privatechat", "privateChat");
    }

This subscribes the privateChat method to the channel “/service/privatechat”. Any channel in “/service/**” is special, in that it is not a broadcast publish/subscribe channel and any messages published is delivered only to the server or to clients that are explicitly called. In this case, clients publish to the privatechat channel and the messages are sent only to the server. The client JavaScript is updated to handle name::text as a way of sending a private message:

chat: function(text){
	var priv = text.indexOf("::");
	if (priv > 0) {
		dojox.cometd.publish("/service/privatechat", {
			room: "/chat/demo",
			user: room._username,
			chat: text.substring(priv + 2),
			peer: text.substring(0, priv)
		});
	} else {
		dojox.cometd.publish("/chat/demo", {
			user: room._username,
			chat: text
		});
	}
},

The privateChat service method is implemented to create a private message from the data passed and deliver it to the identified peer client and echo it back to the talking client:

  public void privateChat(Client source, String channel, 
    Map<String, Object> data,String id)
  {
    String room = (String)data.get("room");
    Map<String, String> membersMap = _members.get(room);
    String peerName = (String)data.get("peer");
    String peerId = membersMap.get(peerName);
    if (peerId!=null)
    {
      Client peer = getBayeux().getClient(peerId);
      if (peer!=null)
      {
        Map<String, Object> message = new HashMap<String, Object>();
	message.put("chat", data.get("chat"));
	message.put("user", data.get("user"));
	message.put("scope", "private");
	peer.deliver(getClient(), roomName, message, id);
	source.deliver(getClient(), roomName, message, id);
	return;
      }
    }
  }

The key code here are the calls to deliver on the source and peer Client instances. Unlike a call to Channel.publish(...), which will broadcast a message to all subscribers for a channel, a call to Client.deliver(...) will deliver the message to the channel handler only for that client. Thus both the source and the peer clients will receive the private message on the “/chat/demo” channel, but no other subscribers to the “/chat/demo” channel will receive that message.

It is the distinction between Channel.publish(...) and Client.deliver(...) that is the key to private messaging in cometD. Both use the channel to identify which handler(s) on the client will receive the message, but only the publish method uses the list of subscribers maintained by the server to determine which clients to deliver a published message to.

分享到:
评论

相关推荐

    cometd-1.0.0rc0 源码

    CometD是一个开源的、基于Bayeux协议的JavaScript库,用于实现服务器推送技术(Server-Sent Events)。它使得Web应用程序能够实现实时通信,即服务器可以主动向客户端推送数据,而无需客户端频繁地发送请求。这个...

    cometd-jquery的jar

    cometd.subscribe('/chat', function(message) { console.log('Received message:', message); }); // 发布消息 $('#send').click(function() { var message = {data: $('#message').val()}; cometd.publish...

    Cometd 开发指南

    CometD是一种利用了Comet技术的框架,它能够在客户端和服务端之间建立持久的连接,并实现实时双向通信。Comet技术主要是为了解决HTTP协议的短连接问题,允许服务器主动向客户端发送数据,而不是传统Web应用中客户端...

    cometd 框架实现的几个小程序

    1. **聊天应用(Chat)**:在实时通讯应用中,CometD可以实现实时的消息推送。用户发送的消息可以通过CometD服务直接推送到其他在线用户,无需客户端不断地轮询服务器获取新消息。这大大提高了用户体验,降低了...

    CometD 简介

    ### CometD 简介 #### 一、CometD 概述 CometD 是一个基于 Bayeux 协议实现的实时通信框架,它允许客户端与服务器之间进行双向数据传输,支持多种消息传递机制,包括轮询、长轮询、流式传输等。CometD 的设计目标...

    Cometd可运行实例

    这是一份Cometd的实例源码文件,导入到eclipse中用Tomcat发布服务即可。由于最近开发需要,本人根据cometd-3.0.5的开发文档,整理代码得出这份源码例子,http://localhost:8080/ComtedTest/ 输入昵称并点击加入即可...

    CometD2.x官方帮助手册

    CometD是一个开源的、基于Java的WebSocket和Bayeux协议的实时Web应用程序框架,它使得服务器能够与浏览器或其他客户端进行双向通信,实现高效的推送服务。这个“CometD2.x官方帮助手册”是该技术的离线版文档,包含...

    mycometd_push_Cometd_消息推送_

    CometD是一个开源的、基于Bayeux协议的双向通信框架,它允许Web服务器向客户端实时推送数据,而无需客户端不断发起请求。这种技术在现代Web应用中非常常见,尤其适用于实时聊天、股票更新、在线游戏等场景。下面将...

    Jetty cometd(Continuation)学习笔记

    【Jetty彗星技术(Cometd)及Continuation机制详解】 Jetty是一个高效、轻量级的Java Servlet容器,不仅可作为HTTP服务器,还能作为HTTP客户端。它以其小巧、稳定和高性能的特点受到广泛赞誉,适合企业级应用的需求。...

    cometd-5.0.x.zip

    CometD是一个开源的、基于WebSocket的实时通信框架,它实现了Bayeux协议,使得Web应用可以实现双向通信,即服务器可以主动向客户端推送数据,而不仅仅是响应客户端的请求。在"cometd-5.0.x.zip"这个压缩包中,包含了...

    cometd实例demo

    CometD是一个基于Bayeux协议的开源JavaScript库,它实现了服务器推送技术,允许服务器主动向客户端发送数据,而不仅仅是响应客户端的请求。这个“cometd实例demo”提供了使用CometD的一个实际示例,可以帮助开发者...

    cometd-bayeux.jar

    cometd bayeux jar 类库

    cometd-2.9.0-distribution.tar.gz

    CometD是一个开源的、基于Java的实时Web通信框架,它实现了Bayeux协议,允许服务器与客户端之间进行双向通信,实现高效的推送服务。在Web应用中,CometD广泛用于构建实时聊天、股票更新、在线游戏等需要即时数据交换...

    cometd注解实现java端代码实例

    本例是通过注解的形式写的cometd服务器端代码。通过spring的自动注入的功能,把cometd的服务可以注入到web controller中,controller可以调用cometd的服务方法向客户端发送任何需要的信息。网上很多例子都是客户端...

    cometd-3.0.0.beta2-distribution.tar.gz

    CometD是一个开源的、基于Java的WebSocket和Bayeux协议的实时Web应用程序框架。它提供了服务器推送技术,使得服务器可以主动向客户端发送数据,而不仅仅是响应客户端的请求。CometD的核心理念是通过长连接来实现双向...

    cometd-java-oort-3.0.0.zip

    【标题】"CometD Java Oort 3.0.0" 是一个专注于实时通信的开源项目,而GWT(Google Web Toolkit)数据类型库则是为GWT项目提供通用数据类型和基础设施支持的一个简单库。 在Java编程领域,CometD是一个基于Bayeux...

    一种基于Terracotta的透明分布式CometD引擎实现.pdf

    【标题】: "一种基于Terracotta的透明分布式CometD引擎实现.pdf" 【描述】: 提到的资源是关于如何利用Terracotta技术改进CometD引擎的分布式实现,旨在减少开发工作量并提高服务器的可扩展性。 【标签】: "分布式...

    cometd-2.9.1-distribution.tar.gz_Cometd_微信推送 java_推送

    CometD是一个开源的、基于Bayeux协议的实时通信框架,主要应用于构建WebSocket和Ajax的长连接,实现服务器向客户端的实时数据推送。在Java领域,CometD被广泛用于构建类似QQ聊天和微信这样的实时通讯应用。下面将...

    cometd-nodejs-client:NodeJS 的 CometD 客户端

    CometD 计划 CometD NodeJS 客户端 该项目实现了允许在 NodeJS 环境中运行的适配器代码。 适配器代码导出XMLHttpRequest的实现,以便 CometD JavaScript 客户端在 NodeJS 中像在浏览器环境中一样工作。 通过包支持...

Global site tag (gtag.js) - Google Analytics