基于html5 WebSocket和WebRTC实现IM和视音频呼叫

在上篇文( 基于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>

 
相关资料:

http://git.warmcat.com/cgi-bin/cgit/libwebsockets/

http://dev.w3.org/html5/websockets/

你可能感兴趣的:(websocket)