解決瀏覽器跨域限制方案之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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM