|
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));
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> "
+ "<span style='color:#00008b;font-weight:bold;'>"
+ from
+ " : </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"/> |
|