在上篇文( 基于html5 WebSocket和WebRTC实现IM和视音频呼叫(一))里我们已经用Jetty-7.5.4.v20111024搭起了一个WebSocket server,现在就可以编写自己的WebSocket Server逻辑完成自己的实现了。
一、编写WebSocket服务端逻辑
MyWebSocketServlet类继承自Jetty开发包中的org.eclipse.jetty.websocket.WebSocketServlet类,用于实现我们的WebSocket 服务端入口。前期没有编写太多的服务端逻辑,只是实现了接受并记录所有连接client端,并广播所有client端消息的功能,代码如下:
package 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简单实现了群聊聊天室:
<!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>
if (!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实现了电子白板的功能:
<!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>
相关资料: