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