今天在這里講解一下關於開源框架Pushlet中的定點推送消息和與瀏覽器參數交互
通過上面的方法我就可以完成點對點的網頁版本的聊天軟件了,當然需要達到上面的要求我們這里需要對Pushlet的源碼進行改進。
首先,我這里就講述Pushlet的入門配置,默認大家是了解Pushlet框架的。
1. 與瀏覽器參數交互
需要定位一個客戶端就必須要給這個客戶設置一個唯一的key值,我這里就叫它為clientid,當然也有朋友會說,我使用Pushlet中每個Session的id來作為clientid,這樣當然是可以的,但為了便於管理我們一般會生成自己的clientid來進行操作,我在這里要做的就是使用自己生成的clientid來替換Pushlet自己生成的Sesssion的id。
在Pushlet初始化去監聽一個事件的時候我們是沒有辦法傳遞參數的,這里我就需要改動一下ajax-pushlet-client.js中的代碼,來滿足我們的需求,添加參數數組的定義,並且修改_doRequest函數如下代碼:
var PL = { NV_P_FORMAT: 'p_format=xml-strict', NV_P_MODE: 'p_mode=pull', pushletURL: null, webRoot: null, sessionId: null, parameters: new Array(new Array),// 對參數數組的定義 STATE_ERROR: -2, STATE_ABORT: -1, STATE_NULL: 1, STATE_READY: 2, STATE_JOINED: 3, STATE_LISTENING: 3, state: 1, ...... _doRequest: function(anEvent, aQuery) { ...... if(PL.parameters.length > 0) { for (var i = 0; i < PL.parameters.length; i++) { var para = PL.parameters[i]; url += "&" + para.name + "=" + para.value; } } ...... },
加入代碼后的_doRequest函數如下:
1 _doRequest: function(anEvent, aQuery) { 2 ...... 3 4 // Construct base URL for GET 5 var url = PL.pushletURL + '?p_event=' + anEvent; 6 7 // Optionally attach query string 8 if (aQuery) { 9 url = url + '&' + aQuery; 10 } 11 12 // Optionally attach session id 13 if (PL.sessionId != null) { 14 url = url + '&p_id=' + PL.sessionId; 15 if (anEvent == 'p_leave') { 16 PL.sessionId = null; 17 } 18 } 19 20 if(PL.parameters.length > 0) { 21 for (var i = 0; i < PL.parameters.length; i++) { 22 var para = PL.parameters[i]; 23 url += "&" + para.name + "=" + para.value; 24 } 25 } 26 27 PL.debug('_doRequest', url); 28 PL._getXML(url, PL._onResponse); 29 30 },
代碼改好后就可以這樣寫我的Pushlet初始化代碼了,傳入我們所需要的clientid給服務器,代碼如下:
1 var initPushlet = function() { 2 PL._init(); 3 PL.parameters.push({"clientid":"servicekey", "value":"4214f0c0da6760a9e95e3c164998ac06"}); 4 PL.joinListen('/conversation'); 5 };
那么,客戶端把參數傳過來了服務端要怎么接收它呢?這就就需要修改一下Pushlet的java源碼,首先我們修改nl.justobjects.pushlet.core.SessionManager中的createSession方法,如下:
1 /** 2 * Create new Session (but add later). 3 */ 4 public Session createSession(Event anEvent) throws PushletException { 5 // Trivial 6 return Session.create(anEvent); 7 }
再修改nl.justobjects.pushlet.core.Session中的create方法,如下:
1 public static Session create(Event anEvent) throws PushletException { 2 Session session; 3 try { 4 session = (Session) Config.getClass(SESSION_CLASS, "nl.justobjects.pushlet.core.Session").newInstance(); 5 } catch (Throwable t) { 6 throw new PushletException("Cannot instantiate Session from config", t); 7 } 8 9 // get clientid 10 session.id = anEvent.getField("clientid"); 11 session.controller = Controller.create(session); 12 session.subscriber = Subscriber.create(session); 13 session.event = anEvent; 14 return session; 15 }
上面就完成了SessionId的轉變。
如果我們要完成一個點對點聊天的話客戶端和服務器通信當然是必不可少,具體怎么向服務端發消息呢?在這里我使用里Pushlet的js中的p_publish()方法,示例代碼如下:
1 p_publish('answer','msg',encodeURIComponent('你好嗎?'),'clientid','ce8954e8557fa9db8c1b2d6774e471a6');
上面方法第一個參數是:執行的操作命令command,后面是都是傳給服務器的參數,是以一個參數名+參數值的形式。參數值如果存在中文的話需要使用encodeURIComponent()方法來進行編碼再傳輸。
在服務端我們要接收這一個客戶端發過來的消息並進行處理,我需要怎么接收它呢?就需要在nl.justobjects.pushlet.core.Controller這個類中的doCommand()方法中進行處理了,找到這個方法中的eventType.equals(Protocol.E_PUBLISH)判斷內,並加入自己的處理代碼,代碼如下:
1 public void doCommand(Command aCommand) { 2 try { 3 ...... 4 } else if (eventType.equals(Protocol.E_PUBLISH)) { 5 // Publish event 6 doPublish(aCommand); 7 8 // get command 9 System.out.println(aCommand.reqEvent.getField("p_subject")); 10 // get clientid 11 System.out.println(aCommand.reqEvent.getField("clientid")); 12 // get msg 13 System.out.println(new String(aCommand.reqEvent.getField("msg").getBytes("ISO8859-1"), "UTF-8")); 14 } else if (eventType.equals(Protocol.E_LISTEN)) { 15 // Listen to pushed events 16 doListen(aCommand); 17 } 18 19 ...... 20 } catch (Throwable t) { 21 warn("Exception in doCommand(): " + t); 22 t.printStackTrace(); 23 } 24 }
aCommand.reqEvent.getField("p_subject"))就可以獲得客戶端傳過來的command命令,aCommand.reqEvent.getField("參數名")來獲取傳過來參數。當然在正常的開發中,所用到的命令會很多,我們可以把每一個命令做成一個個子類去實現,完成不同的需求。至於怎么將消息發送給客戶端,將在下面的定點推送消息中講解。
2. 定點推送消息
上面已經講述了每個客戶端都會存在一個clientid,這個clientid可以定位每一個客戶端,那么既然我們需要向某一個客戶端推送消息,那必然要知道這個客戶端的clientid,這里我建議開發者自己寫一個管理器用於管理連接上來的客戶端,用於保存這些客戶端的信息。如果需要開發聊天程序的話則需要寫一個聊天會話管理器,用於管理一對對的聊天會話Session。
言歸正傳,我們接着1內容中的最后說,當我們在doCommand()方法中獲得了某一個客戶端傳過來的消息時,我們可能需要做的是將處理的結果返回給它,或將它的消息發給另外一個客戶端,至於怎么做呢?我們直接看代碼,還是上面的代碼加入了些內容如下:
1 public void doCommand(Command aCommand) { 2 try { 3 ...... 4 } else if (eventType.equals(Protocol.E_PUBLISH)) { 5 // Publish event 6 doPublish(aCommand); 7 8 // get command 9 System.out.println(aCommand.reqEvent.getField("p_subject")); 10 // get clientid 11 System.out.println(aCommand.reqEvent.getField("clientid")); 12 // get msg 13 System.out.println(new String(aCommand.reqEvent.getField("msg").getBytes("ISO8859-1"), "UTF-8")); 14 15 // 發送給其他客戶端 16 Event event = Event.createDataEvent("/conversation"); // 監聽的事件 17 event.setField("cmd", "say"); 18 event.setField("code", 100); 19 event.setField("msg", new String(aCommand.reqEvent.getField("msg").getBytes("ISO8859-1"), "UTF-8")); 20 Dispatcher.getInstance().unicast(event, aCommand.reqEvent.getField("clientid"));// 指定的clientid 21 22 // 回傳給自己 23 event = Event.createDataEvent("/conversation"); // 監聽的事件 24 event.setField("cmd", "answerResult"); 25 event.setField("code", 100); 26 Dispatcher.getInstance().unicast(event, session.getId());// 當前sessionid即為clientid 27 28 } else if (eventType.equals(Protocol.E_LISTEN)) { 29 // Listen to pushed events 30 doListen(aCommand); 31 } 32 33 ...... 34 } catch (Throwable t) { 35 warn("Exception in doCommand(): " + t); 36 t.printStackTrace(); 37 } 38 }
上面代碼就實現了將消息發給指定客戶端,以及回傳消息給自己。客戶端要如何接收服務端傳過來的消息,見如下代碼:
1 function onData(event) { 2 alert(event.get("cmd")); 3 alert(event.get("code")); 4 alert(event.get("msg")); 5 }
頁面上寫上此js就可以接收到服務器傳過來的內容,內容全都包含在event當中了。
總結:到此為止我們對Pushlet中的定點推送消息和與瀏覽器參數交互有了比較深入的學習,大家可以按照這個方案開發自己想要的東西了。
另:還有我想說的就是,Pushlet這個框架是基於Servlet來做的,Servlet是需要在Tomcat這樣的容器中運行的,這樣一來它的客戶端連接數必然受到限制。還有由於Pushlet的機制使得它並不是那么的實時。接下來我想說的是通過http長連接的方式來做。