websocket学习总结记录
Websocket
1.基本概念
WebSocket是一種網絡通信協議。
websocket和http 的區別,http的缺陷,只能從客戶端發起請求(單項請求)不能從服務器發起請求。如果服務器有連續性的變化時,如果我們想要每一個時刻都獲得最新的數據時,就需要不斷的由客戶端發起請求不停的發起連接,就會浪費很大一部分資源。
所以websocket的誕生就是為了解決之一問題,使得服務器也可以向客戶端發起請求~實現網站實時推送的需求
websocket的特點:
1.建立在tcp協議之上
2.兼容http協議,握手階段采用http協議。
3.數據格式輕量級,可以發送文本,還可以發送二進制數據
5.沒有同源限制,客戶端可以和任意服務器通信
協議標識:ws(加密后為wss
總結:websocket是一種網絡協議,支持服務器與客戶端的全雙工連接。
websocket前端往后端發送數據,
socket.send("這是來自客戶端的消息");執行socket.send后對應著后端的WebSocketService.onMessage事件
websocket同時還定義了幾個監聽函數
1、onopen 當網絡連接建立時觸發該事件
2、onerror 當網絡發生錯誤時觸發該事件
3、onclose 當websocket被關閉時觸發該事件
4、onmessage 當websocket接收到服務器發來的消息的時觸發的事件。
在后端我們可以新建一個WebSocketService類來做整對websocket的以上四種狀態來執行以下業務邏輯。
但在這之前需要導入websocket的依賴以及配置文件,很簡單:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency> @Configuration public class WebSocketConfig{@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();} }然后前端寫的四個狀態與后端定義的相對于
@Service @ServerEndpoint("/imServer/{username}") public class WebSocketService {public static PlayerService playerService;/**統計在線的人數*/private static int onlineCount = 0;/**concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。*/private static ConcurrentHashMap<String,WebSocketService> webSocketMap = new ConcurrentHashMap<>();/**與某個客戶端的連接會話,需要通過它來給客戶端發送數據*/private Session session;/**接收userId*/private String userId"";@OnOpenpublic void onOpen(Session session, @PathParam("username") String username){}@OnClosepublic void onClose() throws IOException {}@OnMessagepublic void onMessage(String message, Session session) throws IOException {}/*** 通過遍歷webSocketMap當前在線人數的session向每一個在線用戶推送消息* 實現服務器主動推送*/public void sendMessage(String message) throws IOException {for (String s : webSocketMap.keySet()) {webSocketMap.get(s).session.getBasicRemote().sendText(message);}}/*** 發送自定義消息* */public static void sendInfo(String message,@PathParam("username") String toUsername) throws IOException {if(webSocketMap.containsKey(toUsername)){webSocketMap.get(toUsername).sendMessage(message);}else{System.out.println("用戶"+toUsername+",不在線!");}}public static synchronized void addOnlineCount() {WebSocketService.onlineCount++;}public static synchronized void subOnlineCount() {WebSocketService.onlineCount--;}public static synchronize int getOnlineCount() {return onlineCount;} }@ServerEndpoint("/imServer/{username}")則表示前端在新建websocket的連接時需要的url
對應這里我寫的,前端代碼就是:
let url="http://localhost:8082/imServer/"+username; url=url.replace("https","ws").replace("http","ws"); socket =new WebSocket(url); //打開事件 socket.onopen = function() {}; //獲得消息事件 socket.onmessage = function(msg) {}; //關閉事件 socket.onclose = function(msg) {}; //發生了錯誤事件 socket.onerror = function() { }后端業務可以通過sendMessage方法向前端傳送數據:包括字符串、對象、二進制位、文件。
后端sendMessage傳來的數據時就會執行socket.onmessage里面的代碼
一.WebSocket與傳統的異同點
參考文章:http://blog.jobbole.com/106009/
短答案
就像Java和JavaScript,并沒有什么太大的關系,但又不能說完全沒關系。可以這么說:
- 命名方面,Socket是一個深入人心的概念,WebSocket借用了這一概念;
- 使用方面,完全兩個東西。
長答案
Socket可以有很多意思,和IT較相關的本意大致是指 在端到端的一個連接中,這兩個端叫做Socket 。對于IT從業者來說,它往往指的是TCP/IP網絡環境中的兩個連接端,大多數的API提供者(如操作系統,JDK)往往會提供基于這種概念的接口,所以對于開發者來說也往往是在說一種編程概念。同時,操作系統中進程間通信也有Socket的概念,但這個Socket就不是基于網絡傳輸層的協議了。
網絡中的 Socket
通常所說的Socket API,是指操作系統中(也可能不是操作系統)提供的對于傳輸層(TCP/UDP)抽象的接口。現行的Socket API大致都是遵循了BSD Socket規范(包括Windows)。這里稱規范其實不太準確,規范其實是POSIX,但BSD Unix中對于Socket的實現被廣為使用,所以成為了實際的規范。如果你要使用HTTP來構建服務,那么就不需要關心Socket,如果你想基于TCP/IP來構建服務,那么Socket可能就是你會接觸到的API。
Socket
歷史中使用到的Socket,包括TCP文檔中使用到的Socket,其實指的是網絡傳輸中的一端,是一個虛擬化的概念。也算是一種編程的思想。
WebSocket
w3c放棄了HTML然后有一群人(也有說是這些人供職的公司,不過官方的文檔上是說的個人)創立了WHATWG組織來推動HTML語言的繼續發展,同時,他們還發展了很多關于Web的技術標準,這些標準不斷地被官方所接受。WebSocket就屬于WHATWG發布的Web Application的一部分(即HTML5)的產物。
結論
可以把WebSocket想象成HTTP,HTTP和Socket什么關系,WebSocket和Socket就是什么關系。
也可以理解為
Socket其實并不是一個協議,而是為了方便使用TCP或UDP而抽象出來的一層,是位于應用層和傳輸控制層之間的一組接口。
Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
當兩臺主機通信時,必須通過Socket連接,Socket則利用TCP/IP協議建立TCP連接。TCP連接則更依靠于底層的IP協議,IP協議的連接則依賴于鏈路層等更低層次。
WebSocket則是一個典型的應用層協議。
? Socket是傳輸控制層協議,WebSocket是應用層協議。
websocket詳解
隨著互聯網的發展,傳統的HTTP協議已經很難滿足Web應用日益復雜的需求了。近年來,隨著HTML5的誕生,WebSocket協議被提出,
它實現了瀏覽器與服務器的全雙工通信,擴展了瀏覽器與服務端的通信功能,使服務端也能主動向客戶端發送數據。
我們知道,傳統的HTTP協議是無狀態的,每次請求(request)都要由客戶端(如 瀏覽器)主動發起,服務端進行處理后返回response結果,而服務端很難主動向客戶端發送數據;這種客戶端是主動方,服務端是被動方的傳統Web模式 對于信息變化不頻繁的Web應用來說造成的麻煩較小,而對于涉及實時信息的Web應用卻帶來了很大的不便,如帶有即時通信、實時數據、訂閱推送等功能的應 用。在WebSocket規范提出之前,開發人員若要實現這些實時性較強的功能,經常會使用折衷的解決方法:輪詢(polling)和Comet技術。其實后者本質上也是一種輪詢,只不過有所改進。
輪詢是最原始的實現實時Web應用的解決方案。輪詢技術要求客戶端以設定的時間間隔周期性地向服務端發送請求,頻繁地查詢是否有新的數據改動。明顯地,這種方法會導致過多不必要的請求,浪費流量和服務器資源。
Comet技術又可以分為長輪詢和流技術。長輪詢改進了上述的輪詢技術,減小了無用的請求。它會為某些數據設定過期時間,當數據過期后才會向服務端發送請求;這種機制適合數據的改動不是特別頻繁的情況。流技術通常是指客戶端使用一個隱藏的窗口與服務端建立一個HTTP長連接,服務端會不斷更新連接狀態以保持HTTP長連接存活;這樣的話,服務端就可以通過這條長連接主動將數據發送給客戶端;流技術在大并發環境下,可能會考驗到服務端的性能。
這兩種技術都是基于請求-應答模式,都不算是真正意義上的實時技術;它們的每一次請求、應答,都浪費了一定流量在相同的頭部信息上,并且開發復雜度也較大。
伴隨著HTML5推出的WebSocket,真正實現了Web的實時通信,使B/S模式具備了C/S模式的實時通信能力。WebSocket的工作流程是這 樣的:瀏覽器通過JavaScript向服務端發出建立WebSocket連接的請求,在WebSocket連接建立成功后,客戶端和服務端就可以通過 TCP連接傳輸數據。因為WebSocket連接本質上是TCP連接,不需要每次傳輸都帶上重復的頭部數據,所以它的數據傳輸量比輪詢和Comet技術小 了很多。
JavaEE 7中出了JSR-356:Java API for WebSocket規范。不少Web容器,如Tomcat,Nginx,Jetty等都支持WebSocket。Tomcat從7.0.27開始支持 WebSocket,從7.0.47開始支持JSR-356,下面的Demo代碼也是需要部署在****Tomcat7.0.47****以上的版本才能運行。
二.WebSocket示例
首先建一個javaxiangm
在pom.xml中添加Jar包依賴
<dependency><groupId>javax</groupId><artifactId>javaee-api</artifactId><version>7.0</version><scope>provided</scope> </dependency>客戶端(Web主頁)代碼:
<%@ page language="java" pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head><title>Java后端WebSocket的Tomcat實現</title> </head> <body> <h2>Hello World!</h2> webSocket測試 <div>請輸入發送內容:<input type="text" id="sendContent"><button id="send" οnclick="send()">發送消息</button><hr> </div> <div><button οnclick="closeWebSocket()">關閉webSocket連接</button> </div> <label>消息記錄:</label> <div id="message"> </div> </body> <script src="./js/jquery-3.2.1.min.js"></script> <script type="text/javascript">function myBrowser(){var userAgent = navigator.userAgent; //取得瀏覽器的userAgent字符串var isOpera = userAgent.indexOf("Opera") > -1;//判斷是否Opera瀏覽器if (isOpera) {return "Opera"};//判斷是否Firefox瀏覽器if (userAgent.indexOf("Firefox") > -1) {return "Firefox";}//判斷谷歌if (userAgent.indexOf("Chrome") > -1){return "Chrome";}//判斷是否Safari瀏覽器if (userAgent.indexOf("Safari") > -1) {return "Safari";}//判斷是否IE瀏覽器if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera) {return "IE";};}//發送按鈕保持選中$(document).keydown(function(event) {if (event.keyCode == 13) {$("#send").click();}});$(document).ready(function(){$("div").css("margin-top","50px");})var webSocket = null;//判斷瀏覽器是否支持webSocket通信if('WebSocket' in window){webSocket = new WebSocket("ws://localhost:8080/webSocket");}else{alert("您的瀏覽器不支持webSocket通信,請升級瀏覽器或更換瀏覽器重試!")}console.log(webSocket);//連接發生錯誤的回調方法webSocket.onerror = function () {setMessageInnerHTML("WebSocket連接發生錯誤");};//連接成功建立的回調方法webSocket.onopen = function () {setMessageInnerHTML("WebSocket連接成功");}//接收到消息的回調方法webSocket.onmessage = function (event) {var message = $("#message").html();setMessageInnerHTML(message+event.data+"<br/>");}//連接關閉的回調方法webSocket.onclose = function () {setMessageInnerHTML("WebSocket連接關閉");}//監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。window.onbeforeunload = function () {closeWebSocket();}//將消息顯示在網頁上function setMessageInnerHTML(innerHTML) {$("#message").html(innerHTML+"<br/>")}//關閉WebSocket連接function closeWebSocket() {webSocket.close();}//發送消息function send() {var browser = myBrowser()var message = $("#sendContent").val();webSocket.send(browser+": "+message);}</script> </html>Java Web后端代碼
package com.deng.websocket;/*** Created by surplusDeng on 2017/10/27.*/import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet;/*** @ServerEndpoint 注解是一個類層次的注解,它的功能主要是將目前的類定義成一個websocket服務器端,* 注解的值將被用于監聽用戶連接的終端訪問URL地址,客戶端可以通過這個URL來連接到WebSocket服務器端*/ @ServerEndpoint("/webSocket") public class WebSocketTest {//靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。private static int onlineCount = 0;//concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。若要實現服務端與單一客戶端通信的話,可以使用Map來存放,其中Key可以為用戶標識private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();//與某個客戶端的連接會話,需要通過它來給客戶端發送數據private Session session;/*** 連接建立成功調用的方法* @param session 可選的參數。session為與某個客戶端的連接會話,需要通過它來給客戶端發送數據*/@OnOpenpublic void onOpen(Session session){this.session = session;webSocketSet.add(this); //加入set中addOnlineCount(); //在線數加1System.out.println("有新連接加入!當前在線人數為" + getOnlineCount());}/*** 連接關閉調用的方法*/@OnClosepublic void onClose(){webSocketSet.remove(this); //從set中刪除subOnlineCount(); //在線數減1System.out.println("有一連接關閉!當前在線人數為" + getOnlineCount());}/*** 收到客戶端消息后調用的方法* @param message 客戶端發送過來的消息* @param session 可選的參數*/@OnMessagepublic void onMessage(String message, Session session) {System.out.println("來自客戶端的消息:" + message);//群發消息for(WebSocketTest item: webSocketSet){try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();continue;}}}/*** 發生錯誤時調用* @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error){System.out.println("發生錯誤");error.printStackTrace();}/*** session.getBasicRemote().sendText(message)發送消息到客戶端getBasicRemote,同步,getAsyncRemote異步* 這個方法與上面幾個方法不一樣。沒有用注解,是根據自己需要添加的方法。* @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--;}}總結
以上是生活随笔為你收集整理的websocket学习总结记录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【学术相关】211高校神级硕士论文刷屏!
- 下一篇: 【CV】使用OpenCV搭建违章停车检测