基于java實(shí)現(xiàn)websocket協(xié)議過程詳解
最近了解了下websocket和socket這個(gè)東西,說不得不來說下為何要使用 WebSocket ,和為何不用http。
為何需要WebSocket ?
HTTP 協(xié)議是一種無狀態(tài)的、無連接的、單向的應(yīng)用層協(xié)議。它采用了請求/響應(yīng)模型。通信請求只能由客戶端發(fā)起,服務(wù)端對請求做出應(yīng)答處理。
這種通信模型有一個(gè)弊端:HTTP 協(xié)議無法實(shí)現(xiàn)服務(wù)器主動(dòng)向客戶端發(fā)起消息。
這種單向請求的特點(diǎn),注定了如果服務(wù)器有連續(xù)的狀態(tài)變化,客戶端要獲知就非常麻煩。大多數(shù) Web 應(yīng)用程序?qū)⑼ㄟ^頻繁的異步JavaScript和XML(AJAX)請求實(shí)現(xiàn)長輪詢。輪詢的效率低,非常浪費(fèi)資源(因?yàn)楸仨毑煌_B接,或者 HTTP 連接始終打開)。
因此,工程師們一直在思考,有沒有更好的方法。WebSocket 就是這樣發(fā)明的。WebSocket 連接允許客戶端和服務(wù)器之間進(jìn)行全雙工通信,以便任一方都可以通過建立的連接將數(shù)據(jù)推送到另一端。WebSocket 只需要建立一次連接,就可以一直保持連接狀態(tài)。這相比于輪詢方式的不停建立連接顯然效率要大大提高。
下面來一個(gè)例子,實(shí)現(xiàn)web客戶端和服務(wù)器端的連續(xù)通訊。
客戶端頁面index.html
<!DOCTYPE html>
<html>
<head>
<title>Java后端WebSocket的Tomcat實(shí)現(xiàn)</title>
</head>
<body>
Welcome<br/><input id="text" type="text"/>
<button onclick="send()">發(fā)送消息</button>
<hr/>
<button onclick="closeWebSocket()">關(guān)閉WebSocket連接</button>
<hr/>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//判斷當(dāng)前瀏覽器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/Testwebsocket/websocket");
}
else {
alert('當(dāng)前瀏覽器 Not support websocket')
}
//連接發(fā)生錯(cuò)誤的回調(diào)方法
websocket.onerror = function () {
setMessageInnerHTML("WebSocket連接發(fā)生錯(cuò)誤");
};
//連接成功建立的回調(diào)方法
websocket.onopen = function () {
setMessageInnerHTML("WebSocket連接成功");
}
//接收到消息的回調(diào)方法
websocket.onmessage = function (event) {
setMessageInnerHTML(event.data);
}
//連接關(guān)閉的回調(diào)方法
websocket.onclose = function () {
setMessageInnerHTML("WebSocket連接關(guān)閉");
}
//監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時(shí),主動(dòng)去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口,server端會(huì)拋異常。
window.onbeforeunload = function () {
closeWebSocket();
}
//將消息顯示在網(wǎng)頁上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//關(guān)閉WebSocket連接
function closeWebSocket() {
websocket.close();
}
//發(fā)送消息
function send() {
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
服務(wù)器端:服務(wù)器端的很簡單。
1.創(chuàng)建一個(gè)web 的maven項(xiàng)目
2.引入一個(gè)包
<!-- websocket --> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency>
3.創(chuàng)建一個(gè)類。
package com.sun.web.handler;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
/**
* @ServerEndpoint 注解是一個(gè)類層次的注解,它的功能主要是將目前的類定義成一個(gè)websocket服務(wù)器端,
* 注解的值將被用于監(jiān)聽用戶連接的終端訪問URL地址,客戶端可以通過這個(gè)URL來連接到WebSocket服務(wù)器端
*/
@ServerEndpoint("/websocket")
public class WebSocketTest {
//靜態(tài)變量,用來記錄當(dāng)前在線連接數(shù)。應(yīng)該把它設(shè)計(jì)成線程安全的。
private static int onlineCount = 0;
//concurrent包的線程安全Set,用來存放每個(gè)客戶端對應(yīng)的MyWebSocket對象。若要實(shí)現(xiàn)服務(wù)端與單一客戶端通信的話,可以使用Map來存放,其中Key可以為用戶標(biāo)識(shí)
private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();
//與某個(gè)客戶端的連接會(huì)話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
private Session session;
/**
* 連接建立成功調(diào)用的方法
* @param session 可選的參數(shù)。session為與某個(gè)客戶端的連接會(huì)話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在線數(shù)加1
System.out.println("有新連接加入!當(dāng)前在線人數(shù)為" + getOnlineCount());
}
/**
* 連接關(guān)閉調(diào)用的方法
*/
@OnClose
public void onClose(){
webSocketSet.remove(this); //從set中刪除
subOnlineCount(); //在線數(shù)減1
System.out.println("有一連接關(guān)閉!當(dāng)前在線人數(shù)為" + getOnlineCount());
}
/**
* 收到客戶端消息后調(diào)用的方法
* @param message 客戶端發(fā)送過來的消息
* @param session 可選的參數(shù)
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("來自客戶端的消息:" + message);
//群發(fā)消息
for(WebSocketTest item: webSocketSet){
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
/**
* 發(fā)生錯(cuò)誤時(shí)調(diào)用
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
System.out.println("發(fā)生錯(cuò)誤");
error.printStackTrace();
}
/**
* 這個(gè)方法與上面幾個(gè)方法不一樣。沒有用注解,是根據(jù)自己需要添加的方法。
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException{
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketTest.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketTest.onlineCount--;
}
}
4.以上三步就OK了,然后就是用tomcat運(yùn)行就行了。
效果如圖:

后期整合了springboot + websocket,并且實(shí)現(xiàn)了定時(shí)推送給前,還可以實(shí)現(xiàn)推送給某個(gè)人,不過需要參數(shù).
地址 :
https://github.com/sun2955/spring-test-study.git
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 基于java實(shí)現(xiàn)websocket代碼示例
- Vue+Java 通過websocket實(shí)現(xiàn)服務(wù)器與客戶端雙向通信操作
- Java開發(fā)中常用的 Websocket 技術(shù)參考
- java與微信小程序?qū)崿F(xiàn)websocket長連接
- java 實(shí)現(xiàn)websocket的兩種方式實(shí)例詳解
- Java后端WebSocket的Tomcat實(shí)現(xiàn)
- Java中使用websocket實(shí)現(xiàn)在線聊天功能
- Java中Spring WebSocket詳解
- java使用websocket,并且獲取HttpSession 源碼分析(推薦)
- Java中websocket消息推送的實(shí)現(xiàn)代碼
- 基于Tomcat7、Java、WebSocket的服務(wù)器推送聊天室實(shí)例
- 用java WebSocket做一個(gè)聊天室
- 使用Java和WebSocket實(shí)現(xiàn)網(wǎng)頁聊天室實(shí)例代碼
- 使用JavaWeb webSocket實(shí)現(xiàn)簡易的點(diǎn)對點(diǎn)聊天功能實(shí)例代碼
- 教你怎么使用Java實(shí)現(xiàn)WebSocket
相關(guān)文章
淺析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法
下面小編就為大家?guī)硪黄獪\析Java類和數(shù)據(jù)結(jié)構(gòu)中常用的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09
基于SpringMVC中的路徑參數(shù)和URL參數(shù)實(shí)例
這篇文章主要介紹了基于SpringMVC中的路徑參數(shù)和URL參數(shù)實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
java中對list分頁并顯示數(shù)據(jù)到頁面實(shí)例代碼
這篇文章主要介紹了java中對list分頁并顯示數(shù)據(jù)到頁面實(shí)例代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02
BeanUtils.copyProperties復(fù)制不生效的解決
這篇文章主要介紹了BeanUtils.copyProperties復(fù)制不生效的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
Java通過Socket實(shí)現(xiàn)簡單多人聊天室
這篇文章主要為大家詳細(xì)介紹了Java通過Socket實(shí)現(xiàn)簡單多人聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04
Spring Mybatis Mapper模糊查詢的幾種方法
在Spring結(jié)合Mybatis進(jìn)行開發(fā)時(shí),實(shí)現(xiàn)模糊查詢是一個(gè)常見需求,在Mybatis中,LIKE查詢可以通過多種方式實(shí)現(xiàn),本文給大家介紹了Spring Mybatis Mapper模糊查詢的幾種方法,需要的朋友可以參考下2024-03-03

