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/ (A barebones WebSocket client and server implementation written in 100% Java.)

使用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 lirufei@lg mailto:lirufei0808@gmail.com
* @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 lirufei@lg mailto:lirufei0808@gmail.com
* @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 lirufei@lg mailto:lirufei0808@gmail.com
* @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 lirufei@lg mailto:lirufei0808@gmail.com
* @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 lirufei@lg mailto:lirufei0808@gmail.com
* @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 lirufei@lg mailto:lirufei0808@gmail.com
* @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 lirufei@lg mailto:lirufei0808@gmail.com
* @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 lirufei@lg mailto:lirufei0808@gmail.com
* @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

最新文章

  1. jstl标签
  2. 如何利用tomcat和cas实现单点登录(1):配置tomcat的ssl和部署cas
  3. 【BZOJ-1452】Count 树状数组 套 树状数组
  4. ios view的frame和bounds之区别(位置和大小)
  5. yii的常用配置文件
  6. Spark技术内幕:Client,Master和Worker 通信源码解析
  7. 什么是 docker?
  8. .net通用权限框架B/S (五)--WEB(2)登录
  9. IntelliJ IDEA 13.1.4新建java web项目
  10. ACE-6.1.0 linux 下的编译与安装步骤
  11. 菜鸟学IT之python网页爬取初体验
  12. PHP定界符&lt;&lt;&lt;EOF
  13. 2018-2019-2 20165336《网络攻防技术》Exp5 MSF基础应用
  14. vs2008将 win32项目改为console项目
  15. R 脚本读取汇总 Excel 表格数据
  16. [Hive_3] Hive 建表指定分隔符
  17. Vue(三)之前端路由
  18. codeforces570C
  19. pyhon 前面补充和set
  20. python之组合与重用性

热门文章

  1. AS3.0函数定义的方法
  2. 关于java reflect
  3. 源码编译基于Android平台的XBMC笔记
  4. HTML5之一HTML5简介
  5. Zoj 3868 GCD Expectation
  6. 【关于php】Appserv的安装注意事项
  7. 《Braid》碎片式台词
  8. Python、Lua和Ruby——脚本大P.K.
  9. iOS图案锁,支持动画、图片、绘图
  10. 【BZOJ2434-[Noi2011]】阿狸的打字机(AC自动机(fail树)+离线+树状数组)