在上篇文( 基于html5 WebSocket和WebRTC实现IM和视音频呼叫(一))里我们已经用Jetty-7.5.4.v20111024搭起了一个WebSocket server,现在就可以编写自己的WebSocket Server逻辑完成自己的实现了。
一、编写WebSocket服务端逻辑
MyWebSocketServlet类继承自Jetty开发包中的org.eclipse.jetty.websocket.WebSocketServlet类,用于实现我们的WebSocket 服务端入口。前期没有编写太多的服务端逻辑,只是实现了接受并记录所有连接client端,并广播所有client端消息的功能,代码如下:
MyWebSocketServlet.javapackage com.webrtcserver; import java.io.IOException; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.websocket.WebSocket; import org.eclipse.jetty.websocket.WebSocketServlet; public class MyWebSocketServlet extends WebSocketServlet { private static final long serialVersionUID = -7289719281366784056L; public static String newLine = System.getProperty("line.separator"); private final Set<TailorSocket> _members = new CopyOnWriteArraySet<TailorSocket>(); public void init() throws ServletException { super.init(); } private void broadcastMessage(String msg) { for (TailorSocket member : _members) { logs("Trying to send to Member!"); if (member.isOpen()) { logs("Sending!"); try { member.sendMessage(msg); } catch (IOException e) { e.printStackTrace(); } } } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { getServletContext().getNamedDispatcher("default").forward(request, response); logs("doGet"); } public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { return new TailorSocket(); } static private int serialNum = 0; class TailorSocket implements WebSocket.OnTextMessage { private Connection _connection; private String sdp; private String userName; public void onClose(int closeCode, String message) { _members.remove(this); } public void sendMessage(String data) throws IOException { _connection.sendMessage(data); } public void onMessage(String data) { logs("Received: " + data); broadcastMessage(data); } public boolean isOpen() { return _connection.isOpen(); } public void onOpen(Connection connection) { userName = String.format("%5d", serialNum++); _members.add(this); _connection = connection; logs("one memner connected!"); try { connection .sendMessage("onOpen:Server received Web Socket upgrade and added it to Receiver List."); } catch (IOException e) { e.printStackTrace(); } } } private void logs(String logMessage) { System.out.println(logMessage); } }
TailorSocket 继承自 WebSocket.OnTextMessage,每当一个client连接时就会调用doWebSocketConnect方法实例化一个client对象。
server端通过TailorSocket的_connection.sendMessage(data)方法向client发送文本消息;通过onMessage接收client发送的文本消息,并调用broadcastMessage方法向所有连接的client广播消息。
二、编写Client逻辑
WebSocket对象在不同的浏览器实现稍有区别,为了代码适应更多的浏览器在调用WebSocket对象前需要判断当前的浏览器,然后实现WebSocket的onopen,onmessage,onclose等方法,即可与WebSocket服务端建立连接,具体代码如下:
var server = {
connect : function() {
var location = get_appropriate_ws_url() + "/servlet/a";
if (BrowserDetect.browser == "Firefox") {
this._ws = new MozWebSocket(location, null);
} else {
this._ws = new WebSocket(location, null);
}
this._ws.onopen = this._onopen;
this._ws.onmessage = this._onmessage;
this._ws.onclose = this._onclose;
showLog("connecting...");
},
_onopen : function() {
showLog("connected to server!");
},
_send : function(message) {
if (this._ws)
this._ws.send(message);
},
send : function(text) {
if (text != null && text.length > 0) {
server._send(text);
}
},
_onmessage : function(m) {
if (m.data) {
showMessage("others", m.data);
}
showLog("onmessage");
},
_onclose : function(m) {
this._ws = null;
showLog("onclose");
}
};
下面的test.html和test.js简单实现了群聊聊天室:
test.html<!DOCTYPE HTML> <html lang="en"> <head> <meta charset = "utf-8"/> <title>WebRTC && Web Sockets Demo</title> <script type="text/javascript" src="JS/test.js"></script> <style type='text/css'> div { border: 0px solid black; } div#messageBox { clear: both; width: 40em; height: 20ex; overflow: auto; background-color: #f0f0f0; padding: 4px; border: 1px solid black; } div#logBox { clear: both; width: 40em; height: 20ex; overflow: auto; background-color: #f0f0f0; padding: 4px; border: 1px solid black; } div#input { clear: both; width: 40em; padding: 4px; background-color: #e0e0e0; border: 1px solid black; border-top: 0px } div.hidden { display: none; } span.alert { font-style: italic; } </style> </head> <body> <div> <input id='connect' class='button' type='submit' name='Connect' value='Connect' /> </div> <div id='messageBox'></div> <div id='inputButton'> <input id="inputBox" type="text"/> <input id="inputButton" type="button" value="SEND"/> </div> <div id='logBox'></div> <script type='text/javascript'> $('connect').onclick =function(event) { server.connect(); return false; }; $("inputButton").onclick = function(event) { if($("inputBox").value != "") { server.send($("inputBox").value); $("inputBox").value = ""; showMessage("me", text); } return false; } </script> </body> </html>
test.jsif (!window.WebSocket) alert("window.WebSocket unsuport!"); function $() { return document.getElementById(arguments[0]); } function $F() { return document.getElementById(arguments[0]).value; } function getKeyCode(ev) { if (window.event) return window.event.keyCode; return ev.keyCode; } function showMessage(from, content) { var messageBox = $('messageBox'); var spanText = document.createElement('span'); spanText.className = 'text'; spanText.innerHTML = from + ":" + content; var lineBreak = document.createElement('br'); messageBox.appendChild(spanText); messageBox.appendChild(lineBreak); messageBox.scrollTop = messageBox.scrollHeight - messageBox.clientHeight; } function showLog(log) { var logBox = $("logBox"); logBox.innerHTML = log + "<br />" + logBox.innerHTML; } function get_appropriate_ws_url() { var pcol; var u = document.URL; if (u.substring(0, 5) == "https") { pcol = "wss://"; u = u.substr(8); } else { pcol = "ws://"; if (u.substring(0, 4) == "http") u = u.substr(7); } u = u.split('/'); return pcol + u[0]; } var server = { connect : function() { var location = get_appropriate_ws_url() + "/servlet/a"; if (BrowserDetect.browser == "Firefox") { this._ws = new MozWebSocket(location, "dumb-increment-protocol"); } else { this._ws = new WebSocket(location, "dumb-increment-protocol"); } this._ws.onopen = this._onopen; this._ws.onmessage = this._onmessage; this._ws.onclose = this._onclose; showLog("connecting..."); }, _onopen : function() { showLog("connected to server!"); }, _send : function(message) { if (this._ws) this._ws.send(message); }, send : function(text) { if (text != null && text.length > 0) { server._send(text); } }, _onmessage : function(m) { showMessage("others", m.data); showLog("onmessage"); }, _onclose : function(m) { this._ws = null; showLog("onclose"); } }; /* BrowserDetect came from http://www.quirksmode.org/js/detect.html */ var BrowserDetect = { init: function () { this.browser = this.searchString(this.dataBrowser) || "An unknown browser"; this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || "an unknown version"; this.OS = this.searchString(this.dataOS) || "an unknown OS"; }, searchString: function (data) { for (var i=0;i<data.length;i++) { var dataString = data[i].string; var dataProp = data[i].prop; this.versionSearchString = data[i].versionSearch || data[i].identity; if (dataString) { if (dataString.indexOf(data[i].subString) != -1) return data[i].identity; } else if (dataProp) return data[i].identity; } }, searchVersion: function (dataString) { var index = dataString.indexOf(this.versionSearchString); if (index == -1) return; return parseFloat(dataString.substring(index+this.versionSearchString.length+1)); }, dataBrowser: [ { string: navigator.userAgent, subString: "Chrome", identity: "Chrome" }, { string: navigator.userAgent, subString: "OmniWeb", versionSearch: "OmniWeb/", identity: "OmniWeb" }, { string: navigator.vendor, subString: "Apple", identity: "Safari", versionSearch: "Version" }, { prop: window.opera, identity: "Opera", versionSearch: "Version" }, { string: navigator.vendor, subString: "iCab", identity: "iCab" }, { string: navigator.vendor, subString: "KDE", identity: "Konqueror" }, { string: navigator.userAgent, subString: "Firefox", identity: "Firefox" }, { string: navigator.vendor, subString: "Camino", identity: "Camino" }, { // for newer Netscapes (6+) string: navigator.userAgent, subString: "Netscape", identity: "Netscape" }, { string: navigator.userAgent, subString: "MSIE", identity: "Explorer", versionSearch: "MSIE" }, { string: navigator.userAgent, subString: "Gecko", identity: "Mozilla", versionSearch: "rv" }, { // for older Netscapes (4-) string: navigator.userAgent, subString: "Mozilla", identity: "Netscape", versionSearch: "Mozilla" } ], dataOS : [ { string: navigator.platform, subString: "Win", identity: "Windows" }, { string: navigator.platform, subString: "Mac", identity: "Mac" }, { string: navigator.userAgent, subString: "iPhone", identity: "iPhone/iPod" }, { string: navigator.platform, subString: "Linux", identity: "Linux" } ] }; BrowserDetect.init();
test2.html则用html5的canvas实现了电子白板的功能:
test2.html<!DOCTYPE html> <html lang="en"> <head> <meta charset=utf-8 /> <title>Minimal Websocket test app</title> </head> <body> <table> <tr> <td>Drawing color: <select id="color" onchange="update_color();"> <option value=#000000>Black</option> <option value=#0000ff>Blue</option> <option value=#20ff20>Green</option> <option value=#802020>Dark Red</option> </select> </td> <td id=wslm_statustd align=center><div id=wslm_status>Not initialized</div></td> </tr> <tr> <td colspan=2 width=500 align=center style="background-color: #e0e0e0;"><div id=wslm_drawing> </div></td> </tr> </table> <script> /* BrowserDetect came from http://www.quirksmode.org/js/detect.html */ var BrowserDetect = { init: function () { this.browser = this.searchString(this.dataBrowser) || "An unknown browser"; this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || "an unknown version"; this.OS = this.searchString(this.dataOS) || "an unknown OS"; }, searchString: function (data) { for (var i=0;i<data.length;i++) { var dataString = data[i].string; var dataProp = data[i].prop; this.versionSearchString = data[i].versionSearch || data[i].identity; if (dataString) { if (dataString.indexOf(data[i].subString) != -1) return data[i].identity; } else if (dataProp) return data[i].identity; } }, searchVersion: function (dataString) { var index = dataString.indexOf(this.versionSearchString); if (index == -1) return; return parseFloat(dataString.substring(index+this.versionSearchString.length+1)); }, dataBrowser: [ { string: navigator.userAgent, subString: "Chrome", identity: "Chrome" }, { string: navigator.userAgent, subString: "OmniWeb", versionSearch: "OmniWeb/", identity: "OmniWeb" }, { string: navigator.vendor, subString: "Apple", identity: "Safari", versionSearch: "Version" }, { prop: window.opera, identity: "Opera", versionSearch: "Version" }, { string: navigator.vendor, subString: "iCab", identity: "iCab" }, { string: navigator.vendor, subString: "KDE", identity: "Konqueror" }, { string: navigator.userAgent, subString: "Firefox", identity: "Firefox" }, { string: navigator.vendor, subString: "Camino", identity: "Camino" }, { // for newer Netscapes (6+) string: navigator.userAgent, subString: "Netscape", identity: "Netscape" }, { string: navigator.userAgent, subString: "MSIE", identity: "Explorer", versionSearch: "MSIE" }, { string: navigator.userAgent, subString: "Gecko", identity: "Mozilla", versionSearch: "rv" }, { // for older Netscapes (4-) string: navigator.userAgent, subString: "Mozilla", identity: "Netscape", versionSearch: "Mozilla" } ], dataOS : [ { string: navigator.platform, subString: "Win", identity: "Windows" }, { string: navigator.platform, subString: "Mac", identity: "Mac" }, { string: navigator.userAgent, subString: "iPhone", identity: "iPhone/iPod" }, { string: navigator.platform, subString: "Linux", identity: "Linux" } ] }; BrowserDetect.init(); var pos = 0; function get_appropriate_ws_url() { var pcol; var u = document.URL; /* * We open the websocket encrypted if this page came on an * https:// url itself, otherwise unencrypted */ if (u.substring(0, 5) == "https") { pcol = "wss://"; u = u.substr(8); } else { pcol = "ws://"; if (u.substring(0, 4) == "http") u = u.substr(7); } u = u.split('/'); return pcol + u[0]; } /* lws-mirror protocol */ var down = 0; var no_last = 1; var last_x = 0, last_y = 0; var ctx; var socket_lm; var color = "#000000"; if (BrowserDetect.browser == "Firefox") { socket_lm = new MozWebSocket(get_appropriate_ws_url() + "/servlet/a", "lws-mirror-protocol"); } else { socket_lm = new WebSocket(get_appropriate_ws_url() + "/servlet/a", "lws-mirror-protocol"); } try { socket_lm.onopen = function() { document.getElementById("wslm_statustd").style.backgroundColor = "#40ff40"; document.getElementById("wslm_status").textContent = " websocket connection opened "; } socket_lm.onmessage =function got_packet(msg) { j = msg.data.split(';'); f = 0; while (f < j.length - 1) { i = j[f].split(' '); if (i[0] == 'd') { ctx.strokeStyle = i[1]; ctx.beginPath(); ctx.moveTo(+(i[2]), +(i[3])); ctx.lineTo(+(i[4]), +(i[5])); ctx.stroke(); } if (i[0] == 'c') { ctx.strokeStyle = i[1]; ctx.beginPath(); ctx.arc(+(i[2]), +(i[3]), +(i[4]), 0, Math.PI*2, true); ctx.stroke(); } f++; } } socket_lm.onclose = function(){ document.getElementById("wslm_statustd").style.backgroundColor = "#ff4040"; document.getElementById("wslm_status").textContent = " websocket connection CLOSED "; } } catch(exception) { alert('<p>Error' + exception); } var canvas = document.createElement('canvas'); canvas.height = 300; canvas.width = 480; ctx = canvas.getContext("2d"); document.getElementById('wslm_drawing').appendChild(canvas); canvas.addEventListener('mousemove', ev_mousemove, false); canvas.addEventListener('mousedown', ev_mousedown, false); canvas.addEventListener('mouseup', ev_mouseup, false); offsetX = offsetY = 0; element = canvas; if (element.offsetParent) { do { offsetX += element.offsetLeft; offsetY += element.offsetTop; } while ((element = element.offsetParent)); } function update_color() { color = document.getElementById("color").value; } function ev_mousedown (ev) { down = 1; } function ev_mouseup(ev) { down = 0; no_last = 1; } function ev_mousemove (ev) { var x, y; if (ev.offsetX) { x = ev.offsetX; y = ev.offsetY; } else { x = ev.layerX - offsetX; y = ev.layerY - offsetY; } if (!down) return; if (no_last) { no_last = 0; last_x = x; last_y = y; return; } socket_lm.send("d " + color + "" + last_x + "" + last_y + "" + x + ' ' + y + ';'); last_x = x; last_y = y; } </script> </body> </html>
相关资料: