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
代码语言:javascript复制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
代码语言:javascript复制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
代码语言:javascript复制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结尾。如果启动后发现访问不了请检查一下是不是没有走进去。
最后是页面:
代码语言:javascript复制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