Tomcat方式实现WebSockethtml
Oracle官方规范定义了一组用于开发web socket应用的Java API,请参见:java
http://www.oracle.com/technetwork/articles/java/jsr356-1937161.htmlweb
用到的包以下:编程
javax.websocket.server包含注解,类,接口用于建立和配置服务端点 javax.websocket包含服务端和客户端公用的注解,类,接口,异常
主类有两种建立的方式:浏览器
Interface-Driven Approach 编程式的实现,须要继承Endpoint类,重写它的方法
public class myOwnEndpoint extends javax.websocket.Endpoint { public void onOpen(Session session, EndpointConfig config) {...} public void onClose(Session session, CloseReason closeReason) {...} public void onError (Session session, Throwable throwable) {...} } ``` Annotation-Driven Approach 注解式的实现,将本身的写的类以及类中的一些方法用前面提到的包中的注解装饰(@EndPoint,@OnOpen等等)
@ServerEndpoint("/websocket") public class WebSocketTest { @OnOpen public void start(Session session){ System.out.println("链接成功! " + session.getId()); }安全
@OnMessage public void reMessage(Session session, String str){ try { session.getBasicRemote().sendText(str + " who are you"); }catch (IOException e){ e.printStackTrace(); } } @OnError public void error(Session session, Throwable t){ t.printStackTrace(); } @OnClose public void close(){ }
当建立好一个(服务)端点以后,将它以一个指定的URI发布到应用当中,这样远程客户端就能链接上它了。 Websocket(服务)端点以URI表述,有以下的访问方式:
ws://host:port/path?query wss://host:port/path?query服务器
实例代码以下 JAVA类:
package me.gacl.websocket;websocket
import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet;session
import javax.servlet.http.HttpSession; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint;oracle
/**
@ServerEndpoint("/websocket")
public class WebSocketTest { //静态变量,用来记录当前在线链接数。应该把它设计成线程安全的。 private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每一个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通讯的话,能够使用Map来存放,其中Key能够为用户标识 private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>(); //与某个客户端的链接会话,须要经过它来给客户端发送数据 private Session session; //用来存放 httpSessionId/session 的map ,key 为httpSessionId ,value 为各个终端的session 对象 private static ConcurrentHashMap<String,WebSocketTest> httpSessionWebSocketMap = new ConcurrentHashMap<String,WebSocketTest>(); /** * 链接创建成功调用的方法 * @param session 可选的参数。session为与某个客户端的链接会话,须要经过它来给客户端发送数据 */ @OnOpen public void onOpen(Session session){ this.session = session; webSocketSet.add(this); //加入set中 addOnlineCount(); //在线数加1 System.out.println("有新链接加入!当前在线人数为" + getOnlineCount()); } /** * 链接关闭调用的方法 */ @OnClose public void onClose(){ webSocketSet.remove(this); //从set中删除 subOnlineCount(); //在线数减1 System.out.println("有一链接关闭!当前在线人数为" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * @param message 客户端发送过来的消息 * @param session 可选的参数 */ @OnMessage public void onMessage(String message, Session session) { System.out.println("来自客户端的消息:" + message); //群发消息 for(WebSocketTest item: webSocketSet){ try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); continue; } } } /** * 发生错误时调用 * @param session * @param error */ @OnError public void onError(Session session, Throwable error){ System.out.println("发生错误"); error.printStackTrace(); } /** * 这个方法与上面几个方法不同。没有用注解,是根据本身须要添加的方法。 * @param message * @throws IOException */ public void sendMessage(String message) throws IOException{ if(this.session.isOpen()){ this.session.getBasicRemote().sendText(message); //this.session.getAsyncRemote().sendText(message); this.session.setMaxTextMessageBufferSize(102400); } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketTest.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketTest.onlineCount--; }
}
Web页面: var websocket = null; //判断当前浏览器是否支持WebSocket if ('WebSocket' in window) { websocket = new WebSocket("ws://localhost:8080/JavaWebSocket/websocket"); } else { alert('当前浏览器 Not support websocket') } //链接发生错误的回调方法 websocket.onerror = function () { setMessageInnerHTML("WebSocket链接发生错误"); }; //链接成功创建的回调方法 websocket.onopen = function () { setMessageInnerHTML("WebSocket链接成功"); } //接收到消息的回调方法 websocket.onmessage = function (event) { setMessageInnerHTML(event.data); } //链接关闭的回调方法 websocket.onclose = function () { setMessageInnerHTML("WebSocket链接关闭"); } //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket链接,防止链接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function () { closeWebSocket(); } //将消息显示在网页上 function setMessageInnerHTML(innerHTML) { document.getElementById('message').innerHTML += innerHTML + '<br/>'; } //关闭WebSocket链接 function closeWebSocket() { websocket.close(); } //发送消息 function send() { var message = document.getElementById('text').value; websocket.send(message); } 若是须要WebSocketSession和HttpSession通讯,则须要继承ServerEndpointConfig.Configurator 类并重写一些方法,来完成custom endpoint configuration 的逻辑代码。 修改WebSocketTest.java:
@ServerEndpoint(value="/websocket",configurator=GetHttpSessionConfigurator.class) public class WebSocketTest{ private Session session; private HttpSession httpSession; }
custom endpoint configuration GetHttpSessionConfigurator的代码
/** * */ import javax.servlet.http.HttpSession; import javax.websocket.HandshakeResponse; import javax.websocket.server.HandshakeRequest; import javax.websocket.server.ServerEndpointConfig; import javax.websocket.server.ServerEndpointConfig.Configurator;
/**
/ public class GetHttpSessionConfigurator extends Configurator { /* * */ public GetHttpSessionConfigurator() { // TODO Auto-generated constructor stub }
@Override public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) { HttpSession httpSession = (HttpSession)request.getHttpSession(); config.getUserProperties().put(HttpSession.class.getName(),httpSession); }
}
运行结果以下: 