hedahai119 发表于 2013-1-29 11:52:04

一个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;      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 infoLog.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 channelmembers.put(username, joiner.getId());//add RmoveListener, when this joiner leave tell other membersjoiner.addListener(createRmoveListener(members,channel,id));//just for debug, out put the message informationjoiner.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 DBMap 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 initvar username;var tabList = [];var tab;jQuery(function(jQuery) {var last, meta, connected = false;// let the login user join the chat roominit();// 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 serverjQuery.comet.init(url);connected = true;//subscribe and joinjQuery.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 roomif (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 nowfunction 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 nameuser: 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 executefunction 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));var hasF =roomuser.indexOf("function");if(hasF >= 0){break;}//add the online userif(roomuser == username){//you can not chat with yourselflist += 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 messageif (!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 onecreateChatDiv(from);chat = jQuery('#chat'+from);}chat.append("<span style='color:#8B4500'>"+ time +"</span> "+ "<span style='color:#00008b;font-weight:bold;'>"+ from+ " : </span>" + "<span>" + text+ "</span><br/>");chat.scrollTop = chat.scrollHeight - chat.clientHeight;}}}//leave the chat roomfunction 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 methodfunction select(name){createChatDiv(name);}//create a div for chatfunction 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 divfunction sendMessage(name){var element = jQuery("input");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 nameuser: 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"/>
页: [1]
查看完整版本: 一个WebChat的服务(可插拔jetty版module)