https://github.com/TooTallNate/Java-WebSocket (websockect类库包)
http://blog.openlg.net/index.php/archives/129(实例篇)
http://my.oschina.net/yushulx/blog/298140 (使用Jetty搭建Java Websocket Server,实现图像传输)
http://linxh83.iteye.com/blog/1466017 (jWebSocket使用指南)
http://findhy.com/blog/2014/06/12/java-websocket/(Java-WebSocket)
http://tomcat.apache.org/ (7.0.26支持websocket)
http://java-websocket.org/
使用WebSocket技术实现浏览器和服务器的双向通信(一)
WebSocket 规范的目标是在浏览器中实现和服务器端双向通信。双向通信可以拓展浏览器上的应用类型,例如实时的数据推送(股票行情)、游戏、聊天等.
目前在浏览器中通过http仅能实现单向的通信,comet可以一定程度上模拟双向通信,但效率较低,并需要服务器有较好的支持; flash中的socket和xmlsocket可以实现真正的双向通信,通过 flex ajax bridge,可以在javascript中使用这两项功能. 可以预见,如果websocket一旦在浏览器中得到实现,将会替代上面两项技术,得到广泛的使用.面对这种状况,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。
WebSocket protocol 是HTML5一种新的协议(protocol)。它是实现了浏览器与服务器全双工通信(full-duplex)。
现在,很多网站为了实现即时通讯(real-time),所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(time interval)(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request d的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求(request),然而HTTP request 的header是非常长的,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽。
而最比较新的技术去做轮询的效果是Comet – 用了AJAX。但这种技术虽然可达到全双工通信,但依然需要发出请求(reuqest)。
在 WebSocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即使服务带来了两大好处:
1. Header
互相沟通的Header是很小的-大概只有 2 Bytes
2. Server Push
服务器可以主动传送数据给客户端
明天为大家分享如何使用Java实现WebSocket Server端。
目录结构:
1.WebSocket Server服务抽象类WSServer.java:
package com.hisense.romeo.websocket.server; /** * * WebSocket Server服务抽象类 * 自定义处理WebSocket客户端请求的服务: * 1.继承并实现此类抽象方法 * 2.修改配置文件/WEB-INF/conf/spring/applicationContext-websocket.xml,给wsServerDispatcher添加处理 * 例如:实现一个简单的聊天室,在配置文件中添加 * {@code * * } * 当客户端请求ws://127.0.0.1:8089/chat时,系统会自动将请求分派给com.hisense.romeo.websocket.server.dispatcher.ChatWSServer处理 * 3.如需发送消息可以直接调用wsServerDispatcher.sendMessage方法 * * * @see com.hisense.romeo.websocket.server.IWSServerDispatcher * * @author [email protected] mailto:[email protected] * @version v0.0.1 */ public abstract class WSServer implements IWSServer { /** * 当前调度WebSocket请求的服务,实现者可以用它进行向客户但发送消息 */ protected IWSServerDispatcher wsServerDispatcher; /** * 注册当前WebSocket服务调度员的方法 * * @param wsServerDispatcher */ public void registerWSServerDispatcher(IWSServerDispatcher wsServerDispatcher) { this.wsServerDispatcher = wsServerDispatcher; } }
2.WebSocket请求调度工作WSServerDispatcher.java:
package com.hisense.romeo.websocket.server; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.java_websocket.WebSocket; import org.java_websocket.WebSocketServer; import org.java_websocket.handshake.ClientHandshake; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; /** * * 实现WebSocket请求调度工作 * 根据客户端请求地址中的请求资源进行调度 * 例如:Address: ws://127.0.0.1:8089/chat * 程序会获取address中的chat进行分发请求。 * * @author [email protected] mailto:[email protected] * @version * */ public class WSServerDispatcher extends WebSocketServer implements IWSServerDispatcher, InitializingBean, DisposableBean{ /** * Log4j */ private Logger logger = Logger.getLogger(this.getClass()); private Map<string, iwsserver=""> wsServers; private static int port = 8089; public WSServerDispatcher() throws UnknownHostException { this(new InetSocketAddress(port)); } public WSServerDispatcher(InetSocketAddress address){ super(address); logger.debug("default using port ‘" + address.getPort() + "‘ to startup the WebSocket Server." ); } /** * 建立新连接时自动调用此方法 * * @see org.java_websocket.WebSocketServer#onOpen(org.java_websocket.WebSocket, org.java_websocket.handshake.ClientHandshake) */ @Override public void onOpen(WebSocket ws, ClientHandshake clientHandData) { ws.setHandshakedata(clientHandData); logger.debug("new Client:\n " + ws.getRemoteSocketAddress() + "\n " + clientHandData.getResourceDescriptor()); } /** * 当接收到新消息时调用此方法 * * @see org.java_websocket.WebSocketServer#onMessage(org.java_websocket.WebSocket, java.lang.String) */ @Override public void onMessage(WebSocket ws, String message) { logger.debug("new Message:\n: " + ws.getRemoteSocketAddress() + ":\n " + message); String resource = getResourceDescriptor(ws); if(resource != null && resource.trim().length()!=0){ IWSServer listener = wsServers.get(resource); if(listener != null) listener.onMessage(ws, message); else logger.error("请求资源" + resource + "不存在!"); } else logger.error("请求资源不能为空!"); } /** * 连接关闭时自动调用此方法 * * @see org.java_websocket.WebSocketServer#onClose(org.java_websocket.WebSocket, int, java.lang.String, boolean) */ @Override public void onClose(WebSocket ws, int arg1, String arg2, boolean arg3) { logger.debug("client close:\n" + ws.getRemoteSocketAddress()); } /** * 当连接出现错误时自动调用此方法 * * @see org.java_websocket.WebSocketServer#onError(org.java_websocket.WebSocket, java.lang.Exception) */ @Override public void onError(WebSocket ws, Exception e) { logger.error("client error:\n" + ws.getRemoteSocketAddress() + "\n", e); } /** * 根据请求资源导航串获取所有的客户端 * * @param navigation * @return */ @Override public Set getClientByNavigation(String navigation){ if( navigation == null) return null; Set wss = new HashSet(); Set conections = this.connections(); for (WebSocket webSocket : conections) { String resource = this.getResourceDescriptor(webSocket); if(resource != null && resource.equals(navigation)) wss.add(webSocket); } return wss; } /** * 获取客户端数量 * @see com.hisense.romeo.websocket.server.IWSSendMessage#getClientCount(java.lang.String) */ @Override public int getClientCount(String name){ Set set = this.getClientByNavigation(name); if(set != null) return set.size(); return 0; } /** * 给${navigation}的所有客户段发送消息${message} * * @param message * @param navigation Not Null */ public void sendMessage(String message, String navigation){ this.sendMessage(this.getClientByNavigation(navigation), message); } /** * 发送消息给所有客户端 * * @param message */ public void sendMessageToAll(String message){ this.sendMessage(this.connections(), message); } /** * 发送消息给wss
* * @param wss * @param message */ public void sendMessage(Set wss, String message){ if( wss != null) for (WebSocket webSocket : wss) { webSocket.send(message); } } /** * 当服务器退出时调用此方法 * * @see org.springframework.beans.factory.DisposableBean#destroy() */ @Override public void destroy() throws Exception { logger.info("Stop web socket server..."); super.stop(); if( wsServers != null && !wsServers.isEmpty()){ Set set = wsServers.keySet(); for (String key : set) { wsServers.get(key).destroy(); } } } /** * 当服务器启动时调用此方法 * * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @Override public void afterPropertiesSet() throws Exception { logger.info("**Startup web socket server,listening on port <" + port + ">.**"); if(wsServers == null ) wsServers = new HashMap<string, iwsserver="">(); try { WebSocket.DEBUG = Logger.getLogger("com.hisense.romeo.websocket.server").getLevel().toInt() == Level.DEBUG_INT; this.start(); logger.debug("**Initialization dispatchers.**"); Collection collection = wsServers.values(); for (IWSServer wsServerListener : collection) { wsServerListener.registerWSServerDispatcher(this); } logger.info("**WebSocket server has started.**"); } catch (Exception e) { e.printStackTrace(); } } /** * 获取客户端webSocket
请求的资源 * * @param webSocket * @return */ private String getResourceDescriptor(WebSocket webSocket){ if(webSocket != null && webSocket.getHandshakedata() != null){ String resource = webSocket.getHandshakedata().getResourceDescriptor(); if(resource != null && resource.length() > 0) return resource.substring(1); else return resource; } else return null; } /** * 设置WebSocket服务的监听端口 * * @param port */ public void setPort(int port) { WSServerDispatcher.port = port; this.setAddress(new InetSocketAddress(port)); logger.debug("Update port to ‘" + port + "‘ ant startup the WebSocket Server." ); } public int getPort(){ return super.getPort(); } public Map<string, iwsserver=""> getWsServers() { return wsServers; } public void setWsServers(Map<string, iwsserver=""> wsServers) { this.wsServers = wsServers; } }
3.接口IWSServerDispatcher.java:
package com.hisense.romeo.websocket.server; import java.util.Set; import org.java_websocket.WebSocket; /** * <pre> * getClientByNavigation(String navigation) 根据请求资源导航串获取所有的客户端 * sendMessage(String message, String navigation) 给请求${navigation}的所有客户段发送消息${message} * sendMessage(Set<WebSocket> wss, String message) 发送消息给${wss} * sendMessageToAll(String message) 发送消息给所有客户端 * </pre> * @author [email protected] mailto:[email protected] * @version v0.0.1 * */ public interface IWSServerDispatcher extends IWSSendMessage{ /** * 根据请求资源导航串获取所有的客户端 * * @param navigation * @return */ public Set<WebSocket> getClientByNavigation(String navigation); }
4.接口IWSServer.java:
package com.hisense.romeo.websocket.server; import org.java_websocket.WebSocket; /** * * * @author [email protected] mailto:[email protected] * @version * */ public interface IWSServer { /** * 注册当前WebSocket服务调度员的方法 * * @param wsServerDispatcher */ public void registerWSServerDispatcher(IWSServerDispatcher wsServerDispatcher); /** * 当接收到客户端消息时,会调用这个方法 * * @param ws 客户端信息 * @param msg 接收到的消息 */ public abstract void onMessage(WebSocket ws, String msg); /** * 销毁方法,当服务器停止时调用这个方法 */ public abstract void destroy(); }
5.接口IWSSendMessage.java:
package com.hisense.romeo.websocket.server; import java.util.Set; import org.java_websocket.WebSocket; /** * 定义使用WebSocket向客户端发送消息的方法 * * @author [email protected] mailto:[email protected] * @version * */ public interface IWSSendMessage { /** * 给请求${navigation}的所有客户段发送消息${message} * * @param message 要发送的消息 * @param navigation Not Null */ public void sendMessage(String message, String navigation); /** * 发送消息给<code>wss</code> * * @param wss * @param message */ public void sendMessage(Set<WebSocket> wss, String message); /** * 发送消息给所有客户端 * * @param message */ public void sendMessageToAll(String message); /** * 根据名字查询客户端数量 * * @param name */ public int getClientCount(String name); }
6.消息值对象:MessageEntity.java
package com.hisense.romeo.websocket.common; import java.io.Serializable; /** * @author [email protected] mailto:[email protected] * @version v0.0.1 * */ public class MessageEntity implements Serializable{ /** * */ private static final long serialVersionUID = 1L; /** * 用户名 */ private String user; /** * 密码 */ private String pass; /** * 目标模块 */ private String target; /** * 消息 */ private String message; /** * 是否成功 */ private boolean success = true; public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } public String getTarget() { return target; } public void setTarget(String target) { this.target = target; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public boolean getSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } }
7.业务值对象:RefreshVO.java
package com.hisense.romeo.websocket.common; /** * <pre> * 值对象 * 用于WebSocket推送数据变化的进行数据刷新的值对象 * </pre> * @author [email protected] mailto:[email protected] * @version * */ public class RefreshVO extends MessageEntity { /** * */ private static final long serialVersionUID = 1L; /** * 变化的表名称 */ private String[] tables; public String[] getTables() { return tables; } public void setTables(String[] tables) { this.tables = tables; } }
8.用于聊天的服务端程序ChatWSServer.java
package com.hisense.romeo.websocket.server.dispatcher; import org.java_websocket.WebSocket; import com.hisense.romeo.websocket.server.WSServer; /** * 实现简单聊天室 * * @author [email protected] mailto:[email protected] * @version * */ public class ChatWSServer extends WSServer{ /** * @see com.hisense.romeo.websocket.server.IWSServer#onMessage(org.java_websocket.WebSocket, java.lang.String) */ @Override public void onMessage(WebSocket ws, String msg) { if(wsServerDispatcher != null ) wsServerDispatcher.sendMessage(ws.getRemoteSocketAddress().getHostName() + "说:" + msg, "chat"); } /** * @see com.hisense.romeo.websocket.server.IWSServer#destroy() */ @Override public void destroy() { } }
9.用于发送和接收消息的客户端代码chat.js:
// Write your code in the same way as for native WebSocket: var hostname = location.hostname, ws = new WebSocket(‘ws://‘ + hostname + ‘:8089/chat‘), msg = document.getElementById(‘msg‘), text = document.getElementById(‘text‘), btn = document.getElementById(‘button‘); setMsgValue = function(val){ msg.value = msg.value + val; if(msg.scrollByLines) msg.scrollByLines(5); else msg.scrollTop = msg.value.length; }; ws.onopen = function() { setMsgValue(‘open‘); }; ws.onmessage = function(e) { setMsgValue(‘\n‘ + e.data); }; ws.onclose = function() { setMsgValue(‘\n‘ + ‘close‘); }; btn.onclick = function(){ ws.send(text.value); text.value=‘‘; }; text.onkeyup = function(e){ if(/(m|M)(s|S)(i|I)(e|E)/.test(navigator.userAgent)) e = window.event; if(e.keyCode === 13){ ws.send(text.value); text.value=‘‘; } }
源码下载:http://42.96.146.196/share/ws.7z