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