這是我參與更文挑戰的第2天,活動詳情查看: 更文挑戰前端
本文正在參加「Java主題月 - Java 開發實戰」,詳情查看 活動連接java
[TOC]nginx
HTTP請求用於咱們開發以及用戶之間最爲普遍的一種協議,在HTTP中咱們能夠簡單的經過瀏覽器獲取到咱們須要的內容(頁面)。可是他也有他的侷限性。今天咱們的主角websocket將爲展示他的功能web
這樣多多少少的形成資源的浪費。spring
HTTP是無記憶的。每次請求服務端是沒法瞭解到客戶端以前的行爲的,可是咱們平時瀏覽器網站的時候感受瀏覽器是知道咱們以前作的事情的。這是網站在請求是添加的cookie這些服務端提供的數據。對咱們而言咱們感受是有記憶的。實則否則後端
HTTP1.1以後採用了短鏈接、長鏈接兩種方式。HTTP請求的發送每次也須要三次握手機制。因此每次的鏈接耗費資源。1.1後才必定時間內HTTP其實採用的是長鏈接,這樣能夠減小資源的開銷瀏覽器
上述說道的長鏈接有人可能有疑問,其實HTTP協議是基於TCP協議開發的。因此天然有長鏈接的特性。緩存
HTTP由於短鏈接的特性因此是無記憶的。爲了解決這個問題每一個請求都是由General+Request Head+Request Paylaod+Response Headers組成的。其中Heads就是瀏覽器須要記住的東西,每次傳遞來傳遞去的非常耗費性能。安全
websocket因爲是長鏈接特性,一次鏈接就能夠一直的雙向通訊。從載體來講websocket關注的更少,只須要通訊當前須要的信息。歷史信息雙方都是有的。springboot
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
複製代碼
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
複製代碼
public abstract class BaseWebSocket extends BaseController{
/** * 靜態變量,用來記錄當前在線鏈接數。應該把它設計成線程安全的。 */
private int onlineCount = 0;
/** * concurrent包的線程安全Set,用來存放每一個客戶端對應的MyWebSocket對象。 */
public CopyOnWriteArraySet<BaseWebSocket> webSocketSet = new CopyOnWriteArraySet<BaseWebSocket>();
/** * 與某個客戶端的鏈接會話,須要經過它來給客戶端發送數據 */
public Session session;
private Logger log = LoggerFactory.getLogger("BaseWebSocket");
/** * 鏈接創建成功調用的方法 * @param session */
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) throws IOException {
this.session = session;
//加入set中
webSocketSet.add(this);
//在線數加1
addOnlineCount();
log.debug("有新鏈接加入!當前在線人數爲" + getOnlineCount());
//發送信息
MultiMap multiMap = new MultiMap();
if (null!=session.getQueryString()&&!"".equals(session.getQueryString())) {
UrlEncoded.decodeTo(session.getQueryString(), multiMap, "UTF-8");
}
sendInfo(defaultMessage(multiMap));
}
/** * 鏈接關閉調用的方法 */
@OnClose
public void onClose() {
//從set中刪除
webSocketSet.remove(this);
//在線數減1
subOnlineCount();
log.info("有一鏈接關閉!當前在線人數爲" + getOnlineCount());
}
/** * 收到客戶端消息後調用的方法 * @param message 客戶端發送過來的消息 * @param session 緩存 * @throws IOException */
@OnMessage
public void onMessage(String message, Session session) throws IOException {
this.session = session;
try {
Map paraMap = (Map) JSONObject.parse(message);
handlerMessage(paraMap);
} catch (JSONException e) {
MultiMap multiMap = new MultiMap();
UrlEncoded.decodeTo(message, multiMap, "UTF-8");
handlerMessage(multiMap);
//throw new BusinessException("傳遞消息格式錯誤(Json)");
}
}
/** * 處理消息接受 * @param paraMap 接受到map類型的參數 */
public void handlerMessage(Map paraMap) {
try {
sendInfo(defaultMessage(paraMap));
} catch (IOException e) {
e.printStackTrace();
}
}
public Object defaultMessage(Map<String, Object> paraMap) {
Object obj = new Object();
try {
obj = defauleMessage(paraMap);
} catch (BusinessException e) {
return formatReturnAppData(e.getMessage());
}
return obj;
}
/** * 默認的發送數據 * @param paraMap 鏈接時傳遞的參數 * @return */
public abstract Object defauleMessage(Map<String, Object> paraMap);
public static boolean isJson(String content) {
try {
JSONObject.parse(content);
return true;
} catch (Exception e) {
return false;
}
}
/** * 發生錯誤時調用 @OnError **/
public void onError(Session session, Throwable error) {
log.error("onMessage方法異常"+error.toString());
error.printStackTrace();
}
/** * 發送消息需注意方法加鎖synchronized,避免阻塞報錯 * 注意session.getBasicRemote()與session.getAsyncRemote()的區別 * @param message * @throws IOException */
public synchronized void sendMessage(Object message) throws IOException {
// this.session.getBasicRemote().sendText(message);
this.session.getAsyncRemote().sendText(JSONObject.toJSONString(message));
}
/** * 羣發自定義消息 * */
public void sendInfo(Object message) throws IOException {
for (BaseWebSocket item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
continue;
}
}
}
public synchronized int getOnlineCount() {
return onlineCount;
}
public synchronized void addOnlineCount() {
onlineCount++;
}
public synchronized void subOnlineCount() {
onlineCount--;
}
}
複製代碼
@ServerEndpoint(value = "/accident/getAccident")
@Component
public class AccidentGetAccident extends BaseWebSocket {
AccidentController accidentController;
@Override
public Object defauleMessage(Map<String, Object> paraMap) {
accidentController = ContextUtil.getApplicationContext().getBean(AccidentController.class);
return accidentController.getAccident(paraMap);
}
}
複製代碼
ws = new WebSocket(wsUrl);
複製代碼
ws.onclose = function () {
console.log('連接關閉');
};
ws.onerror = function() {
console.log('發生異常了');
};
ws.onopen = function () {
console.log('新建鏈接');
};
ws.onmessage = function (event) {
console.log("接收到服務端反饋的信息了");
}
複製代碼
var lockReconnect = false;//避免重複鏈接
var wsUrl = "ws://127.0.0.1:8088/accident/getAccident?entId=zhonghuaxingzhong";
var ws;
var tt;
function createWebSocket() {
try {
ws = new WebSocket(wsUrl);
init();
} catch(e) {
console.log(e+'catch');
reconnect(wsUrl);
}
}
function init() {
ws.onclose = function () {
console.log('連接關閉');
reconnect(wsUrl);
};
ws.onerror = function() {
console.log('發生異常了');
reconnect(wsUrl);
};
ws.onopen = function () {
//心跳檢測重置
heartCheck.start();
};
ws.onmessage = function (event) {
setMessageInnerHTML(event.data);
//拿到任何消息都說明當前鏈接是正常的
console.log('接收到消息');
heartCheck.start();
}
}
function reconnect(url) {
if(lockReconnect) {
return;
};
lockReconnect = true;
//沒鏈接上會一直重連,設置延遲避免請求過多
tt && clearTimeout(tt);
tt = setTimeout(function () {
createWebSocket(url);
lockReconnect = false;
}, 4000);
}
//心跳檢測
var heartCheck = {
timeout: 3000,
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
}
}
//將消息顯示在網頁上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
createWebSocket(wsUrl);
複製代碼
你們好!原創不易!springcloud系列+jvm系列正在創做中。。。。。