jetty8中已經自帶有websocket功能,因此咱們能夠很方便搭建一個本身的websocket服務。javascript
源程序:http://sdrv.ms/N5BuKwhtml
啓動類:org.noahx.websocket.WebSocketServerhtml5
訪問地址:http://127.0.0.1:8085/test.htmljava
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.noahx</groupId> <artifactId>ws-test</artifactId> <version>1.0</version> <properties> <jetty.version>8.1.5.v20120716</jetty.version> <slf4j.version>1.6.1</slf4j.version> </properties> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-websocket</artifactId> <version>${jetty.version}</version> </dependency> </dependencies> </project>
WebSocketServerjquery
package org.noahx.websocket; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.util.resource.FileResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; /** * Created with IntelliJ IDEA. * User: noah * Date: 8/8/12 * Time: 5:10 PM * To change this template use File | Settings | File Templates. */ public class WebSocketServer { private Logger logger = LoggerFactory.getLogger(this.getClass()); private Server server; private FlashPolicyServer fpServer; private int port; public static void main(String[] args) { WebSocketServer server = new WebSocketServer(8085); server.start(); } public WebSocketServer(int port) { this.port=port; } public void start(){ fpServer=new FlashPolicyServer(10843); fpServer.start(); server = new Server(port); MyWebSocketHandler myWebSocketHandler = new MyWebSocketHandler(); URL url=this.getClass().getClassLoader() .getResource("org/noahx/websocket/http"); ResourceHandler resourceHandler=new ResourceHandler(); try { resourceHandler.setBaseResource(new FileResource(url)); } catch (IOException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } catch (URISyntaxException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } myWebSocketHandler.setHandler(resourceHandler); server.setHandler(myWebSocketHandler); try { server.start(); server.join(); } catch (Exception e) { logger.error(e.getMessage(),e); } } }
websocket服務端口我設置的爲8085。經過向jetty server中加入WebSocketHandler就能夠提供websocket服務了。因爲WebSocketHandler是HandlerWrapper的子類,因此這個handler中還能夠再加入一個ResourceHandler。這樣就能夠在一個端口上同時提供websocket與http服務。ResourceHandler指向了類路徑org/noahx/websocket/http下,這個包下的全部資源都將能夠發佈爲web資源給http請求。 git
MyWebSocketHandler與MyWebSocketgithub
package org.noahx.websocket; import org.eclipse.jetty.websocket.WebSocket; import org.eclipse.jetty.websocket.WebSocketHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Timer; /** * Created with IntelliJ IDEA. * User: noah * Date: 8/8/12 * Time: 5:16 PM * To change this template use File | Settings | File Templates. */ public class MyWebSocketHandler extends WebSocketHandler { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) { if (logger.isDebugEnabled()) { logger.debug("url=" + request.getRequestURL() + ",protocol=" + protocol); } return new MyWebSocket(); } public class MyWebSocket implements WebSocket.OnTextMessage { private Logger logger = LoggerFactory.getLogger(this.getClass()); private Connection connection; private Timer timer = new Timer(); @Override public void onMessage(String data) { if (logger.isDebugEnabled()) { logger.debug("onMessage"); } } @Override public void onOpen(Connection connection) { if (logger.isDebugEnabled()) { logger.debug("onOpen"); } this.connection = connection; timer.schedule(new MemTask(this), 0, 500); } @Override public void onClose(int closeCode, String message) { if (logger.isDebugEnabled()) { logger.debug("onClose"); } timer.cancel(); } public void send(String msg) { try { if (logger.isDebugEnabled()) { logger.debug("send:" + msg); } connection.sendMessage(msg); } catch (IOException e) { logger.error(e.getMessage(), e); timer.cancel(); } } } }
doWebSocketConnect中實現doWebSocketConnect方法,返回咱們須要的WebSocket對象。
如:ws://127.0.0.1:8085/ws1與ws://127.0.0.1:8085/ws2web
MyWebSocket所實現的內容是以每500毫秒的速度,向瀏覽器發送0-100的隨機數(注意這裏是由服務器主動推送)。ajax
MemTaskapache
package org.noahx.websocket; import java.util.Random; import java.util.TimerTask; /** * Created with IntelliJ IDEA. * User: noah * Date: 8/8/12 * Time: 5:31 PM * To change this template use File | Settings | File Templates. */ public class MemTask extends TimerTask { private MyWebSocketHandler.MyWebSocket myWebSocket; public MemTask(MyWebSocketHandler.MyWebSocket myWebSocket) { this.myWebSocket = myWebSocket; } @Override public void run() { myWebSocket.send("" +new Random().nextInt(100)); } }
取隨機數的任務類
使用到了如下內容:
web-socket-js,https://github.com/gimite/web-socket-js/
就是這個解決了不支持html5的websocket的瀏覽器也能夠調用websocket的問題。web-socket-js會自動判斷是否是支持html5的websocket,若是支持沒有什麼區別。若是發現不支持將經過Flash自動調用websocket來作socket的中轉。
highcharts,http://www.highcharts.com/
這個是經過純javascript(jquery)來繪製圖表的js圖表框架。來配合websocket,作到實時的動態圖表。
test.html
<html> <head> <title></title> <script type="text/javascript" src="swfobject.js"></script> <script type="text/javascript" src="web_socket.js"></script> <script type="text/javascript" src="jquery-1.7.2.min.js"></script> <script src="hc/highcharts.js"></script> <script src="hc/modules/exporting.js"></script> </head> <body> <script type="text/javascript"> var host = window.location.host.split(":")[0]; WEB_SOCKET_SWF_LOCATION = "WebSocketMain.swf"; WEB_SOCKET_DEBUG = false; try { WebSocket.loadFlashPolicyFile("xmlsocket://" + host + ":10843"); } catch (e) { } var ws; function init(series) { ws = new WebSocket("ws://" + host + ":8085/"); ws.onopen = function () { output("onOpen"); }; ws.onmessage = function (e) { var dStr = e.data; outputmem(dStr); var x = (new Date()).getTime(), // current time y = parseInt(dStr); series.addPoint([x, y], true, true); }; ws.onclose = function () { output("onClose"); }; ws.onerror = function () { output("onError"); }; } function outputmem(str) { var mem = document.getElementById("mem"); mem.innerHTML = str; } function output(str) { var log = document.getElementById("log"); var escaped = str.replace(/&/, "&").replace(/</, "<"). replace(/>/, ">").replace(/"/, """); // " log.innerHTML = escaped + "<br>" + log.innerHTML; } $(function () { $(document).ready(function () { Highcharts.setOptions({ global:{ useUTC:false } }); var chart; chart = new Highcharts.Chart({ chart:{ renderTo:'container', type:'spline', marginRight:10, events:{ load:function () { // set up the updating of the chart each second var series = this.series[0]; init(series); } } }, title:{ text:'WebSocket random data' }, xAxis:{ type:'datetime', tickPixelInterval:150 }, yAxis:{ title:{ text:'Value' }, plotLines:[ { value:0, width:1, color:'#808080' } ] }, tooltip:{ formatter:function () { return '<b>' + this.series.name + '</b><br/>' + Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' + Highcharts.numberFormat(this.y, 2); } }, legend:{ enabled:false }, exporting:{ enabled:false }, series:[ { name:'Zero data', data:(function () { var data = [], time = (new Date()).getTime(), i; for (i = -19; i <= 0; i++) { data.push({ x:time + i * 1000, y:0 }); } return data; })() } ] }); }); }); </script> <div id="mem"></div> <div id="log"></div> <div id="container" style="min-width: 400px; height: 400px; margin: 0 auto"></div> </body> </html>
原理是經過web-socket-js創建與服務器的websocket鏈接,服務器發現鏈接後會主動推送數據給瀏覽器。
收到服務器推送過來的數據會觸發onmessage,這時在onmessage中對圖表進行繪製。從而達到效果。
ie78能夠使用websocket的關鍵。web-socket-js會調用flash請求websocket。
flash請求websocket也有一個前提。flash會檢查安全策略文件(xml)是否容許它這樣作。默認flash會請求相同host的843端口,發送policy-file-request請求,這時返回正常的安全策略時,websocket才能夠正常使用。固然咱們也能夠經過WebSocket.loadFlashPolicyFile(方法從新指定策略文件url。樣例是指向了10843端口,由於若是監聽<1000的端口須要root權限,因此內嵌Java版的Flash Policy Server端口爲10843。
但仍是建議不要手動指定WebSocket.loadFlashPolicyFile,而是用843。由於flash老是先會從843找,若是找不到再從websocket端口找(8085)。因此若是設置其它url了會影響加載速度。
Flash Policy的資料能夠參考:http://www.adobe.com/devnet/flashplayer/articles/socket_policy_files.html
裏面提供了一個flashpolicyd_v0.6程序,能夠作Flash Policy Server,但不是java實現的。
我也寫了一個Flash Policy Server,java版的
FlashPolicyServer
package org.noahx.websocket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; /** * Created with IntelliJ IDEA. * User: noah * Date: 8/8/12 * Time: 10:05 PM * To change this template use File | Settings | File Templates. */ public class FlashPolicyServer { private ServerSocket serverSocket; private static Thread serverThread; private int port; private static boolean listening = true; private Logger logger = LoggerFactory.getLogger(this.getClass()); public FlashPolicyServer() { this(843); } public FlashPolicyServer(int port) { this.port = port; } public void start() { try { serverThread = new Thread(new Runnable() { public void run() { try { logger.info("FlashPolicyServer: Starting..."); serverSocket = new ServerSocket(port); while (listening) { final Socket socket = serverSocket.accept(); Thread t = new Thread(new Runnable() { public void run() { try { if (logger.isDebugEnabled()) { logger.debug("FlashPolicyServer: Handling Request..."); } socket.setSoTimeout(10000); InputStream in = socket.getInputStream(); byte[] buffer = new byte[23]; if (in.read(buffer) != -1 && (new String(buffer, "ISO-8859-1")).startsWith("<policy-file-request/>")) { if (logger.isDebugEnabled()) { logger.debug("PolicyServerServlet: Serving Policy File..."); } OutputStream out = socket.getOutputStream(); byte[] bytes = ("<?xml version=\"1.0\"?>\n" + "<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\n" + "<cross-domain-policy> \n" + " <site-control permitted-cross-domain-policies=\"master-only\"/>\n" + " <allow-access-from domain=\"*\" to-ports=\"*\" />\n" + "</cross-domain-policy>").getBytes("ISO-8859-1"); out.write(bytes); out.write(0x00); out.flush(); out.close(); } else { logger.warn("FlashPolicyServer: Ignoring Invalid Request"); logger.warn(" " + (new String(buffer))); } } catch (SocketException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } finally { try { socket.close(); } catch (Exception ex2) { } } } }); t.start(); } } catch (IOException ex) { logger.error("PolicyServerServlet Error---"); logger.error(ex.getMessage(), ex); } } }); serverThread.start(); } catch (Exception ex) { logger.error("PolicyServerServlet Error---"); logger.error(ex.getMessage(), ex); } } public void stop() { logger.info("FlashPolicyServer: Shutting Down..."); if (listening) { listening = false; } if (!serverSocket.isClosed()) { try { serverSocket.close(); } catch (Exception ex) { } } } }
這樣就能夠和websocket server整合在一塊兒。
安全策略文件的樣例格式:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <site-control permitted-cross-domain-policies="master-only"/> <allow-access-from domain="*" to-ports="*" /> </cross-domain-policy>
繼ajax後websocket會給界面帶來更快更好的交互效果與體驗,伴隨移動市場的壯大websocket也將有可能成爲標準的api協議。
框架介紹:
AS3WebSocket,https://github.com/Worlize/AS3WebSocket
提供給ActiveScript3使用的websocket框架
jWebSocket,http://jwebsocket.org/
有服務器與客戶端,高一級別websocket框架,提供了對基礎websocket的擴展,如jsonSocket,xmlSocket,cvsSocket
kaazing(商業),http://kaazing.com/ 商業級整套html5與websocket解決方案,效果最好,惋惜是商業的