`
hedahai119
  • 浏览: 104455 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

一个WebChat的服务(可插拔jetty版module)

阅读更多

1.这个是一个jetty版的web chat server,用的是cometd技术,server push

2.这个还是一个可插拔的模块

3.如果你愿意你可以给他做成jar包形式,用命令来启动(目前只能手动启动)

4.注解是我写的,很垃圾,将就看吧

5.在你的服务器把这个服务起了,在你自己的web项目上加一个chat页面就可以聊天了,可以群聊,也可以私聊

6.全部代码在下面的zip包里面,是一个Myeclipse8.5 的工程,编译环境JDK1.5

7.2010-08-23 加入jar启动模式,里面有配置文件可以配置,server信息和数据库信息

 

代码示例 - 1:server的mian类,直接执行这个类,chat server 就启动了,端口可以自己指定,目前是8000

package com.brandt.main;
/** 
 * @author Dahai He
 * @date 2010-8-9 
 */
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.handler.MovedContextHandler;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.resource.Resource;
import org.mortbay.resource.ResourceCollection;
import org.mortbay.thread.QueuedThreadPool;


public class CometdServer {
	
	public CometdServer(){
		
	}
	
	public static void main(String[] args) throws Exception{
		int port = 8000;
        State state = new State();
        //state.gameServer = args[0];
        Server server = new Server();
        QueuedThreadPool qtp = new QueuedThreadPool();
        qtp.setMinThreads(5);
        qtp.setMaxThreads(200);
        server.setThreadPool(qtp);
        SelectChannelConnector connector = new SelectChannelConnector();
        connector.setPort(port);
        server.addConnector(connector);
        SocketConnector bconnector = new SocketConnector();
        bconnector.setPort(port + 1);
        server.addConnector(bconnector);
        ContextHandlerCollection contexts = new ContextHandlerCollection();
        server.setHandler(contexts);
        MovedContextHandler moved = new MovedContextHandler(contexts, "/", "/cometd");
        moved.setDiscardPathInfo(true);
        Context context = new Context(contexts, "/cometd", 1);
        String version = Server.getVersion();
        if("6.1.x".equals(version)){
            version = "6.1-SNAPSHOT";
        }
        context.setBaseResource(new ResourceCollection(new Resource[] {
            Resource.newResource("./src/com/brandt/webapp/")
        }));
        ServletHolder dftServlet = context.addServlet("org.mortbay.jetty.servlet.DefaultServlet", "/");
        dftServlet.setInitOrder(1);
        ServletHolder comet = context.addServlet("org.mortbay.cometd.continuation.ContinuationCometdServlet", "/cometd/*");
        comet.setInitParameter("timeout", "250000");
        comet.setInitParameter("interval", "100");
        comet.setInitParameter("maxInterval", "10000");
        comet.setInitParameter("multiFrameInterval", "1500");
        comet.setInitParameter("JSONCommented", "true");
        comet.setInitParameter("logLevel", "0");
        comet.setInitOrder(2);
        ServletHolder brandtChat = context.addServlet("com.brandt.main.ChatServlet", "/brandtChat");
        brandtChat.setInitOrder(3);
        ServletHolder command = context.addServlet("com.brandt.main.CommandServlet", "/command");
        command.setInitOrder(4);
        server.start();
        ChatServlet ls = (ChatServlet)brandtChat.getServlet();
        ls.setStateObject(state);
        CommandServlet cs = (CommandServlet)command.getServlet();
        cs.setStateObject(state);
//        AbstractBayeux bayeux = ((ContinuationCometdServlet)comet.getServlet()).getBayeux();
//        state._bayeux = bayeux;
	}

}

 代码示例 - 2 : chat service main类,主要用于发送消息

package com.brandt.main;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.cometd.Bayeux;
import org.cometd.Channel;
import org.cometd.Client;
import org.cometd.Message;
import org.cometd.MessageListener;
import org.cometd.RemoveListener;
import org.mortbay.cometd.BayeuxService;
import org.mortbay.log.Log;

/** 
 * @author Dahai He
 * @date 2010-8-9 
 */
@SuppressWarnings("unchecked")
public class ChatService extends BayeuxService {
	
	/**
	 * NOTICE: channel === room
	 * a channel is just a chat room
	 */
	
	//To store the online user for chat room
	//pattern : {"/brandt/chat":{"dermot":"xw4rrgw145a","christophe":"dwtt6dxaw12c"}}
	private ConcurrentMap<String,Map> _members = new ConcurrentHashMap<String,Map>();

	public ChatService(Bayeux bayeux) {	
		super(bayeux, "chat");
		//output the debug info
		Log.info("In ChatService constructor");
		//set callback function for channel "/chat/**"
        subscribe("/chat/**", "trackMembers");
        //set callback function for channel "/service/**"
        subscribe("/service/**", "privateChat");
	}
	
	/**
	 * When somebody join room "/chat/**" will execute this method
	 * use case => tell the joiner, who is already in this room
	 * @param joiner, who join this chat room
	 * @param channel, the room name
	 * @param map, message
	 * @param id, message id
	 */
	public void trackMembers(final Client joiner, final String channel,Object message, final String id) {
	
		clientFilter(joiner);
		
		if (message instanceof Map) {
			Map<String, Object> data = (Map<String, Object>) message;

			if (Boolean.TRUE.equals(data.get("join"))) {
				final Map members = getMembersInChannel(channel);
				final String username = (String) data.get("user");
				//put this new joiner to the member list for this channel
				members.put(username, joiner.getId());
				//add RmoveListener, when this joiner leave tell other members
				joiner.addListener(createRmoveListener(members,channel,id));
				//just for debug, out put the message information
				joiner.addListener(new MessageListener() {
					public void deliver(Client fromClient,
							final Client toClient, Message message) {
						Log.info("message from"+fromClient.getId()+"is "+ message.getData());
					}
				});
				//publish the member information to the channel 
				publishMessage(channel,members.keySet(),id);
			}
		}
	}
	
	/**
	 * when a user join channel "/service/**" will execute this method
	 * use case => when a user want to send a message to a single user
	 * @param source
	 * @param channel
	 * @param data
	 * @param messageId
	 */
	public void privateChat(Client source, String channel, Map data, String messageId){
		SimpleDateFormat dfm = new SimpleDateFormat("HH:mm");

		String roomName = (String)data.get("room");
		Map membersMap = (Map)_members.get(roomName);
		String peerName = (String)data.get("peer");
		String peerId = (String)membersMap.get(peerName);
		if(peerId != null){
			Client peer = getBayeux().getClient(peerId);
			if(peer != null){
				//TODO make chat log, insert the message into DB
				Map message = new HashMap();
			    message.put("chat", data.get("chat"));
			    message.put("user", data.get("user"));
			    message.put("scope", "private");
			    message.put("peer", peerName);
			    message.put("time", dfm.format(new Date()));		    
			    peer.deliver(getClient(), roomName, message, messageId);
			    source.deliver(getClient(), roomName, message, messageId);
			}
		}else{
			Map message = new HashMap();
		    message.put("chat", "SYSTEM : Sorry "+peerName+" is not online now!");
		    message.put("user", data.get("user"));
		    message.put("scope", "private");
		    message.put("peer", peerName);
		    message.put("time", dfm.format(new Date()));	
			source.deliver(getClient(), roomName, message, messageId);
		}
	}
	
	/**
	 * check if the client is null
	 * @param client
	 */
	private void clientFilter(Client client){
		if(client == null){
			return;
		}
	}
	
	/**
	 * get the users in a channel
	 * @param channelName, name of a channel
	 * @return a map
	 */
	private Map getMembersInChannel(String channelName){
		Map m = _members.get(channelName);
		if (m == null) {
			Map new_list = new ConcurrentHashMap();
			m = _members.putIfAbsent(channelName, new_list);
			if (m == null) {
				m = new_list;
			}
		}
		return m;
	}
	
	/**
	 * create a RemoveListener 
         * @param members, members in this channel
	 * @param channelName, channel name
	 * @param id, message id
	 * @return a instance of RemoveListener
	 */
	private RemoveListener createRmoveListener(final Map members,final String channelName,final String id){
		RemoveListener listener = new RemoveListener() {
			public void removed(String clientId, boolean timeout) {
				members.values().remove(clientId);
				Log.info((new StringBuilder()).append("members: ").append(members).toString());
				Channel c = getBayeux().getChannel(channelName, false);
				if (c != null) {
					c.publish(getClient(), members.keySet(), id);
				}
			}
		};
		return listener;
	}
	
	/**
	 * publish message to a channel
	 * @param channelName
	 * @param message
	 * @param messageId
	 */
	private void publishMessage(String channelName,Object message,String messageId){
		getBayeux().getChannel(channelName, false).publish(getClient(),message, messageId);
	}
	
}
 

代码示例 - 3 :前台用于chat的javascript main 文件 jquery-chat.js

// TODO rewrite with widget factory, moving join/leave etc. to widget methods and init block to init
var username;
var tabList = [];
var tab;

jQuery(function(jQuery) {
	
	var last, meta, connected = false;
	
	// let the login user join the chat room
	init();
	// when user close or refresh Browser, let the user out 
	jQuery(window).unload(leave);

	function init() {
		join();
	}

	function join() {
		
		username = jQuery.trim(jQuery("#loginUser").text());
		
		if (!username) {
			alert('Please enter a username!');
			return;
		}

		var url = document.location.protocol +"//"+document.location.hostname+":8000/cometd/cometd";
		
		//connect with chat server
		jQuery.comet.init(url);
		connected = true;

		//subscribe and join
		jQuery.comet.startBatch();
		jQuery.comet.subscribe("/chat/brandt", receive);
		jQuery.comet.publish("/chat/brandt", {
			user : username,
			join : true,
			chat : username + " has joined"
		});
		jQuery.comet.endBatch();

		// handle cometd failures while in the room
		if (meta) {
			jQuery.comet.unsubscribe(meta);
		}
		meta = jQuery.comet.subscribe("/cometd/meta",
				function(e) {
					// console.debug(e);
					if (e.action == "handshake") {
						if (e.reestablish) {
							if (e.successful) {
								jQuery.comet.subscribe("/chat/demo",receive);
								jQuery.comet.publish("/chat/demo", {
									user : username,
									join : true,
									chat : username + " has re-joined"
								});
							}
							receive( {
								data : {
									join : true,
									user : "SERVER",
									chat : "handshake " + e.successful ? "Handshake OK"
											: "Failed"
								}
							});
						}
					} else if (e.action == "connect") {
						if (e.successful && !connected) {
							receive( {
								data : {
									join : true,
									user : "SERVER",
									chat : "reconnected!"
								}
							});
						}
						if (!e.successful && connected) {
							receive( {
								data : {
									leave : true,
									user : "SERVER",
									chat : "disconnected!"
								}
							});
						}
						connected = e.successful;
					}
			 	});
	}
	
	//not been used for now
	function send() {
		var phrase = jQuery("#phrase");
		var text = phrase.val();
		phrase.val("");

		if (!text || !text.length) {
			return false;
		}
		
		var colons = text.indexOf("::");
		if (colons > 0) {
			jQuery.comet.publish("/service/privatechat", {
				room: "/chat/demo", // This should be replaced by the room name
				user: username,
				chat: text.substring(colons + 2),
				peer: text.substring(0, colons)
			});
			alert(colons);
		} else {
			jQuery.comet.publish("/chat/demo", {
				user: username,
				chat: text
			});
		}
//		jQuery.ajax({
//			url:"./test",
//			type:"GET",
//			success:function(data){
//				alert("success");
//			}
//		});
	}
	
	//when user receive messages from server, this method will execute
	function receive(message) {
		if (!message.data) {
			window.console && console.warn("bad message format " + message);
			return;
		}
		/*
		 * if message.data instanceof Array
		 * 		this case is tell you who is online
		 * else
		 * 		this case is receive some messages from server
		 * 		eg: {user=Christophe, peer=Dermot, scope=private, chat=hello}
		 */
		if (message.data instanceof Array) {
			var list = "";
			for ( var i in message.data){
				//filter some bad message, send from iceface framework! Fuck!~
				var roomuser = jQuery.trim(String(message.data[i]));
				var hasF =  roomuser.indexOf("function");
				if(hasF >= 0){
					break;
				}
				//add the online user				
				if(roomuser == username){
					//you can not chat with yourself
					list += roomuser + "<br/>";
				}else{
					list += "<a href='javascript:select(\""+roomuser+"\")'>" + roomuser + "</a><br/>";
				}				
			}
			jQuery('.userList').html(list);
		} else {
			
			var special = message.data.join || message.data.leave;
			var from = message.data.user;
			var to = message.data.peer;
			var text = message.data.chat;
			var time = message.data.time;
			var chat;
			
			//bad message
			if (!text) {
				return;
			}
			
			if(!special){
				var chatdiv1 = jQuery('#chat'+from);
				var chatdiv2 = jQuery('#chat'+to);
				if(chatdiv1.length > 0){
					chat = chatdiv1;
				}else if(chatdiv2.length > 0){
					chat = chatdiv2;
				}else{
					//if this no chatDiv create a new one
					createChatDiv(from);
					chat = jQuery('#chat'+from);
				}
				
				chat.append("<span style='color:#8B4500'>"
						+ time 
						+"</span>&nbsp;"
						+ "<span style='color:#00008b;font-weight:bold;'>"
						+ from
						+ "&nbsp;:&nbsp;</span>" 
						+ "<span>" 
						+ text
						+ "</span><br/>");
				
				chat[0].scrollTop = chat[0].scrollHeight - chat[0].clientHeight;
			}

		}
	}
	
	//leave the chat room
	function leave() {
		if (!username) {
			return;
		}
		if (meta) {
			jQuery.comet.unsubscribe(meta);
		}
		meta = null;
		username = null;
		jQuery.comet.disconnect();
	}
})

//when user click a user name in the "online user list", call this method
function select(name){
	createChatDiv(name);
}

//create a div for chat
function createChatDiv(name){
	var title = "Chat with " + name;
	var content = "<div>"
				+ "  <div id='chat"+name+"' class='chat' style='height:120px;width:260px;border:1px solid rgb(185,201,239);overflow: auto;'></div>"
				+ "  <input type='text' name='chatText' value='' id='chatText-"+name+"'/>"
				+ "  <input type='button' value='Send'  name='sendB' onclick='sendMessage(\""+name+"\")'/>"
				+ "</div>";
	jQuery.messager.lays(300, 200);
    jQuery.messager.show(title, content, 0);    
}

//action for "send" button in chat div
function sendMessage(name){
	var element = jQuery("input[id='chatText-"+name+"']");
	var text = element.val();
	element.val("");
	if (!text || !text.length) {
		return false;
	}
	jQuery.comet.publish("/service/privatechat", {
		room: "/chat/brandt", // This should be replaced by the room name
		user: username,
		chat: text,
		peer: name
	});
}

 代码示例 - 4:用到的javascript清单

<script type="text/javascript" src="./javascripts/jquery-1.4.2.min.js"/>
<script type="text/javascript" src="./javascripts/jquery.messager.js"/>
<script type="text/javascript" src="./javascripts/json2.js"/>
<script type="text/javascript" src="./javascripts/jquery.comet.js"/>
<script type="text/javascript" src="./javascripts/jquery-chat.js"/>
分享到:
评论
2 楼 liushian 2011-10-26  
就是啊,麻烦把web中的相关代码文件也给一份吧
1 楼 luotb 2011-08-17  
附件里面没有和前台相关的文件、?

相关推荐

Global site tag (gtag.js) - Google Analytics