WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。
在浏览器中通过http仅能实现单向的通信,comet可以一定程度上模拟双向通信,但效率较低,并需要服务器有较好的支持; flash中的socket和xmlsocket可以实现真正的双向通信,通过 flex ajax bridge,可以在javascript中使用这两项功能. 可以预见,如果websocket一旦在浏览器中得到实现,将会替代上面两项技术,得到广泛的使用.面对这种状况,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。
在JavaEE7中也实现了WebSocket协议。
官方语言结束。
spring自身支持websocket,有自己的一套流程,这里并不是这样的做的,关于spring的可以自己百度学习一下,另外注意jdk要是1.7的,tomcat要是7.0.51以上的。浏览器的话,ie9我测的是不支持的,其他谷歌火狐肯定支持的。不过html5的东西有句话说的好,叫一段代码,放到ie上不运行,放到其他浏览器上却运行了,那么这段代码就是html5.
废话不多说,看代码:
首先要有个扫描注解@ServerEndpoint的驱动器:
WebScoketScanner.java
package com.cmicroentropy.soa.websocket.scanner; import java.util.HashSet; import java.util.Set; import javax.websocket.Endpoint; import javax.websocket.server.ServerApplicationConfig; import javax.websocket.server.ServerEndpointConfig; import org.apache.log4j.Logger; public class WebScoketScanner implements ServerApplicationConfig{ private Logger logger=Logger.getLogger(WebScoketScanner.class); @Override public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) { logger.info("开始扫描所有websocket服务(注解)"); Set<Class<?>> res=new HashSet<>(); for(Class<?> cs:scanned){ if(cs.getPackage().getName().startsWith("com.cmicroentropy.soa.websocket.servers")){ res.add(cs); } } return res; } @Override public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> arg0) { logger.info("开始扫描所有websocket服务(继承)"); Set<ServerEndpointConfig> res=new HashSet<>(); /* //使用Programmatic api的服务器地址 if (scanned.contains(EchoEndpoint.class)) { res.add(ServerEndpointConfig.Builder.create( EchoEndpoint.class, "/websocket/echoProgrammatic").build()); } */ return res; } }
然后建一个节点类处理请求:
ChatServer.java
package com.cmicroentropy.soa.websocket.servers.chatserver; import static com.cmicroentropy.soa.websocket.transform.MessageTransFormUtil.tranOtherMsg; import static com.cmicroentropy.soa.websocket.transform.MessageTransFormUtil.tranSysMsg; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import org.apache.log4j.Logger; import scc.util.SccUtilFactory; import com.cmicroentropy.soa.websocket.transform.MessageTransFormUtil; @ServerEndpoint(value = "/ChatServer.do") // 用了这个之后,你的服务地址为ws://localhost:port/projectName/ChatController public class ChatServer { private Logger logger=Logger.getLogger(ChatServer.class); private static final Set<ChatServer> connections = new CopyOnWriteArraySet<ChatServer>(); private String nickname; private Session session; private Integer userid; public ChatServer() { } @OnOpen public void start(Session session) { this.session = session; Map<String, String> paras = SccUtilFactory.instanceString().getParameterFromUri(session.getRequestURI()); if (null == nickname) nickname = paras.get("username"); if (null == userid) userid = Integer.valueOf(paras.get("userid")); connections.add(this); logger.info(new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date())+" 用户:[" + nickname + "]登录系统."); broadcast(new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date())+" 用户:[" + nickname + "]登录系统.", 1); } @OnClose public void end() { connections.remove(this); logger.info(new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date())+" 用户:[ " + nickname + "]退出系统."); broadcast(new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date())+" 用户:[ " + nickname + "]退出系统.",1); } @OnMessage public void receive(String message) { broadcast("<font style='color:#FE4D00'>["+nickname + "-"+new SimpleDateFormat("HH:mm").format(new Date())+"]:</font>" + message,2); } public Integer getUserid() { return this.userid; } private void broadcast(String msg, Integer type) { switch (type) { // 1是系统消息 case 1: msg = tranSysMsg(msg); for (ChatServer client : connections) { try { client.session.getBasicRemote().sendText(msg); } catch (IOException e) { connections.remove(client); try { client.session.close(); } catch (IOException e1) { e1.printStackTrace(); } continue; } } break; case 2: for (ChatServer client : connections) { try { if(client.getUserid().equals(this.userid)){ client.session.getBasicRemote().sendText(MessageTransFormUtil.tranSelfMsg(msg)); }else{ client.session.getBasicRemote().sendText(tranOtherMsg(msg)); } } catch (IOException e) { connections.remove(client); try { client.session.close(); } catch (IOException e1) { e1.printStackTrace(); } continue; } } break; } } }
静态引入MessageTransFormUtil类是一个返回字符串封装的格式工具类,代码如下:
MessageTransFormUtil.java
package com.cmicroentropy.soa.websocket.transform; public class MessageTransFormUtil { public static String tranSysMsg(String word){ StringBuilder sb=new StringBuilder(); sb.append("<div><div class='tuling-sys'><span >"); sb.append(word); sb.append("</span></div></div>"); return sb.toString(); } public static String tranSelfMsg(String word){ StringBuilder sb=new StringBuilder(); sb.append("<div class='tuling-mn'><img src='images/proexp_014.jpg' class='pull-right'><span class='pull-right tuling tuling2'>"); sb.append(word); sb.append("</span></div>"); return sb.toString(); } public static String tranOtherMsg(String word){ StringBuilder sb=new StringBuilder(); sb.append("<div><div class='tuling-mn'><img src='images/proexp_011.jpg' class='pull-left'><span class='pull-left tuling'>"); sb.append(word); sb.append("</span></div></div>"); return sb.toString(); } }
特别注意的是@ServerEndpoint后的值,我这里springmvc拦截的以.do结尾的请求。所以在value里也以.do结尾。如果启动后发现访问不了请检查一下是不是没有走进去。
最后是页面:
function initchat(){ //显示消息记录 Console.log = (function(message) { var size=$("#chatbox>div>div:first>div").size(); if(size==0){ $("#chatbox").mCustomScrollbar({ autoHideScrollbar : true, theme : "dark-thin" }); }else{ var divheight=$("#chatbox>div>div:first").height(); if(size>300&&divheight>9900){ $("#chatbox>div>div:first>div:lt(1)").remove(); $("#chatbox").mCustomScrollbar("update"); } } $("#chatbox>div>div:first").append(message); $("#chatbox").mCustomScrollbar("update"); $("#chatbox").mCustomScrollbar("scrollTo","last"); /* while ($("#chatbox>div").size() > 500) { $("#chatbox>div:first").remove(); } $("#chatbox")[0].scrollTop = $("#chatbox")[0].scrollHeight;*/ }); Chat.socket = null; Chat.connect = (function(host) { if ('WebSocket' in window) { Chat.socket = new WebSocket(host); } else if ('MozWebSocket' in window) { Chat.socket = new MozWebSocket(host); } else { Console.log('错误: 聊天室 不支持此浏览器请更换成谷歌或火狐浏览器、360浏览器、360极速浏览器等高版本浏览器'); return; } //建立连接触发事件 Chat.socket.onopen = function () { Console.log("<div><div class='tuling-sys'><span >信息: 聊天室服务器 链接成功.</span></div></div>"); $("#sendchattext").keydown(function(event){ if(event.keyCode==13&& event.ctrlKey) { Chat.sendMessage(); } }); }; //关闭连接触发事件 Chat.socket.onclose = function () { $("#sendchattext").unbind("keydown"); Console.log("<div><div class='tuling-sys'><span >信息: 聊天室服务器 已关闭.</span></div></div>"); }; //接收消息触发事件 Chat.socket.onmessage = function (message) { if(message.data.indexOf("pull-right")==-1) setchatMsgnum(); Console.log(message.data); }; }); //发送聊天信息方法 Chat.sendMessage = (function() { var message = $("#sendchattext").val(); if (message != ''&&message.length<500) { Chat.socket.send(message); $("#sendchattext").val(""); $("#sendchattext").focus(); }else{ alertMsg("您输入的字数过多(>500),请拆分后发送!"); $("#sendchattext").focus(); } }); //初始化聊天对象方法,注意URL中的项目名称和Servlet名称 Chat.initialize = function() { if (window.location.protocol == 'http:') { Chat.connect('ws://' + window.location.host + '/sjws_soa_product/ChatServer.do?username='+username+'&userid='+userid); } else { Chat.connect('wss://' + window.location.host + '/sjws_soa_product/ChatServer.do?username='+username+'&userid='+userid); } }; Chat.initialize(); } function sendchatMessage(){ Chat.sendMessage(); }
如果直接copy页面代码因为没有样式等可能看不到实际效果,下面给出一个当初学习websocket借鉴的项目,名字记不得了,感谢前人馈赠!百度网盘地址http://pan.baidu.com/s/1g2V3C