解决浏览器跨域限制方案之WebSocket

WebSocket是在HTML5中引入的浏览器与服务端的通信协议,可以类比HTTP。
可以在支持HTML5的浏览器版本中使用WebSocket进行数据通信,常见的案例是使用WebSocket进行实时数据刷新。
关于WebSocket详细的功能性描述,详见:https://zh.wikipedia.org/wiki/WebSocket。
在这里主要说明在tomcat中如何编写WebSocket服务端程序。

从tomcat7开始支持WebSocket,但是从tomcat8之后使用了关于WebSocket的注解,就得WebSocket API废弃不用。
所以,需要分别按tomcat7和tomcat8+来说明如何使用WebSocket。

tomcat7使用websocket

<!-- tomcat7中实现websocket: 定义servlet -->
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>7.0.39</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-coyote</artifactId>
    <version>7.0.39</version>
    <scope>provided</scope>
</dependency>

在tomcat7中实现WebSocket服务端,与编写一个Servlet程序是一样的。

/**
 * tomcat7中实现websocket servlet
 * @desc org.chench.test.web.websocket.WsServlet
 * @author chench9@lenovo.com
 * @date 2017年10月9日
 */
public class WsChatServlet extends WebSocketServlet {
	private static final long serialVersionUID = 1L;
	private static final Logger logger = LoggerFactory.getLogger(WsChatServlet.class);
	
	private List<MyMessageInBound> milist = new ArrayList<MyMessageInBound>(); // 客户端列表

	/**
	 * 对应每一个客户端连接都创建不同的对象处理
	 */
	@Override
	protected StreamInbound createWebSocketInbound(String arg0, HttpServletRequest arg1) {
		return new MyMessageInBound();
	}
	
	/**
	 * 处理客户端WebSocket连接请求
	 * @desc org.chench.test.web.websocket.MyMessageInBound
	 * @author chench9@lenovo.com
	 * @date 2017年10月9日
	 */
	private class MyMessageInBound extends MessageInbound {
		private WsOutbound wso = null;

		@Override
		protected void onBinaryMessage(ByteBuffer message) throws IOException {
			if(logger.isDebugEnabled()) {
				logger.debug("onBinaryMessage");
			}
			// do nothing
		}

		@Override
		protected void onTextMessage(CharBuffer message) throws IOException {
			if(logger.isDebugEnabled()) {
				logger.debug("onTextMessage");
			}
			
			for(MyMessageInBound mmib : milist) {
				CharBuffer buffer =	CharBuffer.wrap(message);
				mmib.getWsOutbound().writeTextMessage(buffer);
				mmib.getWsOutbound().flush();
			}
		}

		@Override
		protected void onOpen(WsOutbound outbound) {
			if(logger.isDebugEnabled()) {
				logger.debug("websocket client connection add");
			}
			this.wso = outbound;
			milist.add(this);
			try {
				outbound.writeTextMessage(CharBuffer.wrap("Hello"));
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			logger.info("websocket client list size: {}", milist.size());
			
		}

		@Override
		protected void onClose(int status) {
			if(logger.isDebugEnabled()) {
				logger.debug("websocket client closed! {}", this.wso.toString());
			}
			milist.remove(this);
		}
	}
}

最后在web.xml文件中配置该Servlet即可。

tomcat8+使用websocket

特别注意: tomcat7中的WebSocket API在tomcat8之后就已经废弃,要根据实际的运行环境选择对应实现。

在tomcat8之后,使用了新的WebSocket API。具体来说,是通过注解方式编写WebSocket服务端。

<!-- 在tomcat8中实现websocket: 使用注解 -->
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-websocket-api</artifactId>
    <version>8.0.1</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-websocket</artifactId>
    <version>8.0.1</version>
</dependency>
/**
 * 在tomcat8+中实现websocket,通过注解
 * @desc org.chench.test.web.websocket.WsChatAnnotation
 * @author chench9@lenovo.com
 * @date 2017年10月9日
 */
@ServerEndpoint(value="/ws/chatAnnotation")
public class WsChatAnnotation {
	private static final Logger logger = LoggerFactory.getLogger(WsChatAnnotation.class);
	
	private static final AtomicInteger counter = new AtomicInteger(0);                                    // 客户端计数器
	private static final Set<WsChatAnnotation> connections = new CopyOnWriteArraySet<WsChatAnnotation>(); // 客户端websocket连接
	private Session session = null;
	private Integer number = 0;                                                                           // 客户端编号

	public WsChatAnnotation() {
		number = counter.incrementAndGet();
	}
	
	/**
	 * 客户端建立websocket连接
	 * @param session
	 */
	@OnOpen
	public void start(Session session) {
		logger.info("on open");
		this.session = session;
		connections.add(this);
		try {
			session.getBasicRemote().sendText(new StringBuffer().append("Hello: ").append(number).toString());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 客户端断开websocket连接
	 */
	@OnClose
	public void close() {
		logger.info("session close");
		try {
			this.session.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			connections.remove(this);
		}
	}
	
	/**
	 * 接收客户端发送的消息
	 * @param message
	 */
	@OnMessage
	public void message(String message) {
		logger.info("message");
		logger.info("message: {}", message);
		
		for(WsChatAnnotation client : connections) {
			synchronized (client) {
				try {
					client.session.getBasicRemote().sendText(message);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		// end for
	}
	
	@OnError
	public void error(Throwable t) {
		logger.error("client: {} error", number, t);
	}
}

【参考】
http://www.cnblogs.com/xdp-gacl/p/5193279.html Java后端WebSocket的Tomcat实现
http://blog.fens.me/java-websocket-intro/ Java现实WebSocket
http://tomcat.apache.org/tomcat-7.0-doc/web-socket-howto.html tomcat7 web socket
http://tomcat.apache.org/tomcat-8.0-doc/web-socket-howto.html tomcat8 web socket
https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket WebSocket对象api
https://www.zhihu.com/question/20215561 WebSocket 是什么原理?为什么可以实现持久连接?
https://www.nginx.com/blog/websocket-nginx/ NGINX as a WebSocket Proxy

原文地址:https://www.cnblogs.com/nuccch/p/7496203.html