柚子快報(bào)激活碼778899分享:arm開發(fā) 應(yīng)用層網(wǎng)絡(luò)協(xié)議
柚子快報(bào)激活碼778899分享:arm開發(fā) 應(yīng)用層網(wǎng)絡(luò)協(xié)議
tags: [“計(jì)算機(jī)網(wǎng)絡(luò)”] descripution: “學(xué)習(xí)應(yīng)用層的一些常用協(xié)議”
網(wǎng)絡(luò)協(xié)議:約定的信息傳輸?shù)母袷?,如幾個(gè)字節(jié)是消息頭、消息頭記錄什么信息之類的;c/s架構(gòu):不一定是兩臺(tái)計(jì)算機(jī),而是兩個(gè)應(yīng)用、兩個(gè)端口工具:實(shí)際使用中不用手動(dòng)封裝協(xié)議再發(fā)消息,而是直接使用封裝的軟件、庫等實(shí)現(xiàn)功能。如SSH工具OpenSSH
命令行常用
一些協(xié)議名和命令名一樣(或相似)的協(xié)議。結(jié)合應(yīng)用來學(xué)習(xí)。
還有不以協(xié)議為名的網(wǎng)絡(luò)工具,放附錄介紹
SSL&&TLS
SSL(Secure Sockets Layer 安全套接字協(xié)議),及其繼任者傳輸層安全(Transport Layer Security,TLS)是為網(wǎng)絡(luò)通信提供安全及數(shù)據(jù)完整性的一種安全協(xié)議。TLS與SSL在傳輸層與應(yīng)用層之間對(duì)網(wǎng)絡(luò)連接進(jìn)行加密。
為Netscape所研發(fā)。
SSL協(xié)議位于TCP/IP協(xié)議]與各種應(yīng)用層協(xié)議之間,為數(shù)據(jù)通訊提供安全支持。SSL協(xié)議可分為兩層:
SSL記錄協(xié)議(SSL Record Protocol):它建立在可靠的傳輸協(xié)議(如TCP)之上,為高層協(xié)議提供數(shù)據(jù)封裝、壓縮、加密等基本功能的支持。SSL握手協(xié)議SSL Handshake Protocol):它建立在SSL記錄協(xié)議之上,用于在實(shí)際的數(shù)據(jù)傳輸開始前,通訊雙方進(jìn)行身份認(rèn)證、協(xié)商加密算法、交換加密密鑰等。
功能:
1)認(rèn)證用戶和服務(wù)器,確保數(shù)據(jù)發(fā)送到正確的客戶機(jī)和服務(wù)器;
2)加密數(shù)據(jù)以防止數(shù)據(jù)中途被竊??;
3)維護(hù)數(shù)據(jù)的完整性,確保數(shù)據(jù)在傳輸過程中不被改變。
流程圖之類的在計(jì)網(wǎng)書中有嗎
OpneSSL
ca-bundle.crt-安裝git時(shí)可選
9、查看路徑
which openssl
查看版本
openssl version
https://www.cnblogs.com/xiangyuecn/p/8365634.html
https的ssl證書必須綁定域名,所以 https://{ip} 這種是絕對(duì)會(huì)顯示不安全的。
那么一個(gè)簡(jiǎn)單有效的辦法就是同樣在所有電腦上寫 hosts。
https://segmentfault.com/q/1010000019527504
首先,虛構(gòu)一個(gè)CA認(rèn)證機(jī)構(gòu)出來
注:這個(gè)1024不是密碼,是 1024 bit long modulus?。≈苯?024就行,太長了會(huì)cpu跑滿!
# 生成CA認(rèn)證機(jī)構(gòu)的證書密鑰key
# 需要設(shè)置密碼,輸入兩次
openssl genrsa -des3 -out ca.key 1024
# 去除密鑰里的密碼(可選)
# 這里需要再輸入一次原來設(shè)的密碼
openssl rsa -in ca.key -out ca.key
# 用私鑰ca.key生成CA認(rèn)證機(jī)構(gòu)的證書ca.crt
# 其實(shí)就是相當(dāng)于用私鑰生成公鑰,再把公鑰包裝成證書
openssl req -new -x509 -key ca.key -out ca.crt -days 365
# 這個(gè)證書ca.crt有的又稱為"根證書",因?yàn)榭梢杂脕碚J(rèn)證其他證書
其次,才是生成網(wǎng)站的證書
用上面那個(gè)虛構(gòu)出來的CA機(jī)構(gòu)來認(rèn)證,不收錢!
# 生成自己網(wǎng)站的密鑰server.key
openssl genrsa -des3 -out server.key 1024
# 生成自己網(wǎng)站證書的請(qǐng)求文件
# 如果找外面的CA機(jī)構(gòu)認(rèn)證,也是發(fā)個(gè)請(qǐng)求文件給他們
# 這個(gè)私鑰就包含在請(qǐng)求文件中了,認(rèn)證機(jī)構(gòu)要用它來生成網(wǎng)站的公鑰,然后包裝成一個(gè)證書
openssl req -new -key server.key -out server.csr
# 使用虛擬的CA認(rèn)證機(jī)構(gòu)的證書ca.crt,來對(duì)自己網(wǎng)站的證書請(qǐng)求文件server.csr進(jìn)行處理,生成簽名后的證書server.crt
# 注意設(shè)置序列號(hào)和有效期(一般都設(shè)1年)
openssl x509 -trustout -req -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt -days 365
至此,私鑰server.key和證書server.crt已全部生成完畢,可以放到網(wǎng)站源代碼中去用了。
# *.crt *.key C:國家代碼 ST:省 L:市 O:組織名稱 OU:組織單位名稱 CN:域名
/C=CN/ST=js/L=nanjing/O=sss/OU=ssl/CN=ssl.com"
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:henan
Locality Name (eg, city) [Default City]:kaifeng
Organization Name (eg, company) [Default Company Ltd]:re^H^Htr
Organizational Unit Name (eg, section) []:tr
Common Name (eg, your name or your server's hostname) []:hadoop01
作者:奇奇烏布里 鏈接:https://www.jianshu.com/p/0e9ee7ed6c1d 來源:簡(jiǎn)書 著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
FTP
明文傳輸
文件傳輸協(xié)議(File Transfer Protocol,F(xiàn)TP)是用于在網(wǎng)絡(luò)上進(jìn)行文件傳輸?shù)囊惶讟?biāo)準(zhǔn)協(xié)議。FTP是ARPANet(阿帕網(wǎng))的標(biāo)準(zhǔn)文件傳輸協(xié)議。
FTP允許用戶以文件操作的方式(如文件的增、刪、改、查、傳送等)與另一主機(jī)相互通信。然而, 用戶并不真正登錄到自己想要存取的計(jì)算機(jī)上面而成為完全用戶, 可用FTP程序訪問遠(yuǎn)程資源, 實(shí)現(xiàn)用戶往返傳輸文件、目錄管理以及訪問電子郵件等等, 即使雙方計(jì)算機(jī)可能配有不同的操作系統(tǒng)和文件存儲(chǔ)方式。
FTP 的獨(dú)特的優(yōu)勢(shì)同時(shí)也是與其它客戶服務(wù)器程序最大的不同點(diǎn)就在于它在兩臺(tái)通信的主機(jī)之間使用了兩條 TCP 連接,一條是數(shù)據(jù)連接,用于數(shù)據(jù)傳送;另一條是控制連接,用于傳送控制信息(命令和響應(yīng)),這種將命令和數(shù)據(jù)分開傳送的思想大大提高了 FTP 的效率,而其它客戶服務(wù)器應(yīng)用程序一般只有一條 TCP 連接。
使用
centos上自帶的ftp工具真他娘的多啊,,反正平時(shí)用不到,現(xiàn)在都是用web的http傳文件,,了解即可
ftp
怎么搞服務(wù)器?
ftp [-dignv][主機(jī)名稱或IP地址]
參數(shù):
-d 詳細(xì)顯示指令執(zhí)行過程,便于排錯(cuò)或分析程序執(zhí)行的情形。-i 關(guān)閉互動(dòng)模式,不詢問任何問題。-g 關(guān)閉本地主機(jī)文件名稱支持特殊字符的擴(kuò)充特性。-n 不使用自動(dòng)登陸。-v 顯示指令執(zhí)行過程。
例如使用ftp命令匿名登錄ftp.kernel.org服務(wù)器,該服務(wù)是Linux 內(nèi)核的官方服務(wù)器,可以使用如下命令:
ftp ftp.kernel.org #發(fā)起鏈接請(qǐng)求
ncftp
Centos有自帶的ncftp客戶端軟件,可用于從ftp服務(wù)器接受文件。
Linux ncftp命令用于傳輸文件。
當(dāng)不指定用戶名時(shí),ncftp 命令會(huì)自動(dòng)嘗試使用匿名賬戶anonymous 去連接遠(yuǎn)程FTP 服 務(wù)器,不需要用戶輸入賬號(hào)和密碼。
語法:
ncftp [host]
ncftp [ftp://host.name/directory/]
不指定端口?是因?yàn)楣潭ǘ丝趩幔?有沒有上傳的方法? 這個(gè)似乎只能連接,不能設(shè)置為服務(wù)器
ncftp的命令基本上與ftp相同,例如可以使用"cd"命令切換在FTP服務(wù)器中的當(dāng)前目錄,使用"ls"命令列出當(dāng)前目錄內(nèi)容,使用"get 文件名"命令下載"/pub"目錄下的README文件、使用"quit"離開ncftp等
與ftp不同的是,ncftp此時(shí)會(huì)提示用戶是否將FTP服務(wù)器保存為書簽,以便于下次登錄,用戶可以進(jìn)行自定義書簽名等操作,如下所示:
You have not saved a bookmark for this site. #離開提示信息
Would you like to save a bookmark to:
ftp://ftp.kernel.org/pub/
Save? (yes/no) yes #確認(rèn)是否保存
Enter a name for this bookmark, or hit enter for "kernel": kernel #輸入書簽名
Bookmark "kernel" saved.
注:在ncftp的官網(wǎng)有一系列Ncftp軟件,覆蓋服務(wù)端、上傳下載等功能
tftp
tftp是簡(jiǎn)單的文字模式ftp程序,它所使用的指令和FTP類似。
tftp [主機(jī)名稱或IP地址]
其他相關(guān)命令
ftpwho #查詢當(dāng)前有哪些用戶正在登錄FTP服務(wù)器
ftpcount #查詢當(dāng)前FTP用戶的人數(shù)
ftpshut #指定時(shí)間關(guān)閉ftp服務(wù)器
tftp
TFTP(Trivial File Transfer Protocol,簡(jiǎn)單文件傳輸協(xié)議)是TCP/IP協(xié)議族中的一個(gè)用來在客戶機(jī)與服務(wù)器之間進(jìn)行簡(jiǎn)單文件傳輸?shù)膮f(xié)議,提供不復(fù)雜、開銷不大的文件傳輸服務(wù)。端口號(hào)為69。
此協(xié)議設(shè)計(jì)的時(shí)候是進(jìn)行小文件傳輸?shù)摹R虼怂痪邆渫ǔ5腇TP的許多功能,它只能從文件服務(wù)器上獲得或?qū)懭胛募?,不能列出目錄,不進(jìn)行認(rèn)證,它傳輸8位數(shù)據(jù)。傳輸中有三種模式:netascii,這是8位的ASCII碼形式,另一種是octet,這是8位源數(shù)據(jù)類型;最后一種mail已經(jīng)不再支持,它將返回的數(shù)據(jù)直接返回給用戶而不是保存為文件。
tftp [主機(jī)名稱或IP地址]
連接后使用命令和ftp類似:
connect:連接到遠(yuǎn)程tftp服務(wù)器mode:文件傳輸模式put:上傳文件get:下載文件quit:退出verbose:顯示詳細(xì)的處理信息trace:顯示包路徑status:顯示當(dāng)前狀態(tài)信息binary:二進(jìn)制傳輸模式ascii:ascii 傳送模式rexmt:設(shè)置包傳輸?shù)某瑫r(shí)時(shí)間timeout:設(shè)置重傳的超時(shí)時(shí)間help:幫助信息? :幫助信息
pop
明文傳輸
DNS
ping
ping應(yīng)用的底層,用的是網(wǎng)絡(luò)層的ICMP協(xié)議。
ping和tcp的區(qū)別
在 TCP 傳輸中創(chuàng)建的方式是 socket(AF_INET, SOCK_STREAM, 0);,其中 AF_INET 表示將使用 IPV4 里 host:port 的方式去解析待會(huì)你輸入的網(wǎng)絡(luò)地址。SOCK_STREAM 是指使用面向字節(jié)流的 TCP 協(xié)議,工作在傳輸層。
創(chuàng)建好了 socket 之后,就可以愉快的把要傳輸?shù)臄?shù)據(jù)寫到這個(gè)文件里。調(diào)用 socket 的sendto接口的過程中進(jìn)程會(huì)從用戶態(tài)進(jìn)入到內(nèi)核態(tài),最后會(huì)調(diào)用到 sock_sendmsg 方法。
然后進(jìn)入傳輸層,帶上TCP頭。網(wǎng)絡(luò)層帶上IP頭,數(shù)據(jù)鏈路層帶上 MAC頭等一系列操作后。進(jìn)入網(wǎng)卡的發(fā)送隊(duì)列 ring buffer ,順著網(wǎng)卡就發(fā)出去了。
回到 ping , 整個(gè)過程也基本跟 TCP 發(fā)數(shù)據(jù)類似,差異的地方主要在于,創(chuàng)建 socket 的時(shí)候用的是 socket(AF_INET,SOCK_RAW,IPPROTO_ICMP),SOCK_RAW 是原始套接字 ,工作在網(wǎng)絡(luò)層, 所以構(gòu)建ICMP(網(wǎng)絡(luò)層協(xié)議)的數(shù)據(jù),是再合適不過了。ping 在進(jìn)入內(nèi)核態(tài)后最后也是調(diào)用的 sock_sendmsg 方法,進(jìn)入到網(wǎng)絡(luò)層后加上ICMP和IP頭后,數(shù)據(jù)鏈路層加上MAC頭,也是順著網(wǎng)卡發(fā)出。因此 本質(zhì)上ping 跟 普通應(yīng)用發(fā)消息 在程序流程上沒太大差別。
為什么斷網(wǎng)了還能ping通127.0.0.1?
從應(yīng)用層到傳輸層再到網(wǎng)絡(luò)層。這段路徑跟ping外網(wǎng)的時(shí)候是幾乎是一樣的。到了網(wǎng)絡(luò)層,系統(tǒng)會(huì)根據(jù)目的IP,在路由表中獲取對(duì)應(yīng)的路由信息,而這其中就包含選擇哪個(gè)網(wǎng)卡把消息發(fā)出。
當(dāng)發(fā)現(xiàn)目標(biāo)IP是外網(wǎng)IP時(shí),會(huì)從"真網(wǎng)卡"發(fā)出。
當(dāng)發(fā)現(xiàn)目標(biāo)IP是回環(huán)地址時(shí),就會(huì)選擇本地網(wǎng)卡。
本地網(wǎng)卡,其實(shí)就是個(gè)**“假網(wǎng)卡”,它不像"真網(wǎng)卡"那樣有個(gè)ring buffer什么的,"假網(wǎng)卡"會(huì)把數(shù)據(jù)推到一個(gè)叫 input_pkt_queue 的 鏈表 中。這個(gè)鏈表,其實(shí)是所有網(wǎng)卡共享的,上面掛著發(fā)給本機(jī)的各種消息。消息被發(fā)送到這個(gè)鏈表后,會(huì)再觸發(fā)一個(gè)軟中斷**。
專門處理軟中斷的工具人**“ksoftirqd”** (這是個(gè)內(nèi)核線程),它在收到軟中斷后就會(huì)立馬去鏈表里把消息取出,然后順著數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層等層層往上傳遞最后給到應(yīng)用程序。
將數(shù)據(jù)插入到一個(gè)鏈表后就軟中斷通知 ksoftirqd 來進(jìn)行收數(shù)據(jù)的邏輯,
我們?cè)趍ac里執(zhí)行 ifconfig 。
$ ifconfig
lo0: flags=8049
inet 127.0.0.1 netmask 0xff000000
...
en0: flags=8863
inet 192.168.31.6 netmask 0xffffff00 broadcast 192.168.31.255
...
能看到 lo0,表示本地回環(huán)接口,對(duì)應(yīng)的地址,就是我們前面提到的 127.0.0.1 ,也就是回環(huán)地址。
和 eth0,表示本機(jī)第一塊網(wǎng)卡,對(duì)應(yīng)的IP地址是192.168.31.6,管它叫本機(jī)IP。
ping 本機(jī)IP 跟 ping 回環(huán)地址一樣,相關(guān)的網(wǎng)絡(luò)數(shù)據(jù),都是走的 lo0,本地回環(huán)接口,也就是前面提到的**“假網(wǎng)卡”**。
首先 localhost 就不叫 IP,它是一個(gè)域名,就跟 "baidu.com",是一個(gè)形式的東西,只不過默認(rèn)會(huì)把它解析為 127.0.0.1 ,當(dāng)然這可以在 /etc/hosts 文件下進(jìn)行修改。
所以默認(rèn)情況下,使用 localhost 跟使用 127.0.0.1 確實(shí)是沒區(qū)別的。
其次就是 0.0.0.0,執(zhí)行 ping 0.0.0.0 ,是會(huì)失敗的,因?yàn)樗贗PV4中表示的是無效的目標(biāo)地址。
但它還是很有用處的,回想下,我們啟動(dòng)服務(wù)器的時(shí)候,一般會(huì) listen 一個(gè) IP 和端口,等待客戶端的連接。
如果此時(shí) listen 的是本機(jī)的 0.0.0.0 , 那么它表示本機(jī)上的所有IPV4地址。
當(dāng)然, 客戶端 connect 時(shí),不能使用 0.0.0.0 。必須指明要連接哪個(gè)服務(wù)器IP。
web常用
WEB:World Wide Web,萬維網(wǎng)
在WebSocket之前,使用Comet可以實(shí)現(xiàn)全雙工通信。但是Comet存在TCP握手和HTTP頭的開銷,因此對(duì)于小消息來說效率很低。
comet百度百科
Alex Russell(Dojo Toolkit 的項(xiàng)目 Lead)稱這種基于 HTTP長連接、無須在瀏覽器端安裝插件的“服務(wù)器推”技術(shù)為“Comet”。目前已經(jīng)出現(xiàn)了一些成熟的 Comet 應(yīng)用以及各種開源框架;一些 Web 服務(wù)器如 Jetty 也在為支持大量并發(fā)的長連接進(jìn)行了很多改進(jìn)。關(guān)于 Comet 技術(shù)最新的發(fā)展?fàn)顩r請(qǐng)參考關(guān)于 Comet 的 wiki。
下面將介紹兩種 Comet 應(yīng)用的實(shí)現(xiàn)模型。
基于 AJAX 的長輪詢(long-polling)方式
使用 AJAX 實(shí)現(xiàn)“服務(wù)器推”與傳統(tǒng)的 AJAX 應(yīng)用不同之處在于:
服務(wù)器端會(huì)阻塞請(qǐng)求直到有數(shù)據(jù)傳遞或超時(shí)才返回。
客戶端JavaScript 響應(yīng)處理函數(shù)會(huì)在處理完服務(wù)器返回的信息后,再次發(fā)出請(qǐng)求,重新建立連接。
當(dāng)客戶端處理接收的數(shù)據(jù)、重新建立連接時(shí),服務(wù)器端可能有新的數(shù)據(jù)到達(dá);這些信息會(huì)被服務(wù)器端保存直到客戶端重新建立連接,客戶端會(huì)一次把當(dāng)前服務(wù)器端所有的信息取回。
基于長輪詢的服務(wù)器推模型
一些應(yīng)用及示例如 “Meebo”, “Pushlet Chat” 都采用了這種長輪詢的方式。相對(duì)于“輪詢”(poll),這種長輪詢方式也可以稱為“拉”(pull)。因?yàn)檫@種方案相對(duì)于 AJAX,具有以下一些優(yōu)點(diǎn):請(qǐng)求異步發(fā)出;無須安裝插件;IE、Mozilla FireFox 都支持 AJAX。
在這種長輪詢方式下,客戶端是在 XMLHttpRequest 的 readystate 為 4(即數(shù)據(jù)傳輸結(jié)束)時(shí)調(diào)用回調(diào)函數(shù),進(jìn)行信息處理。當(dāng) readystate 為 4 時(shí),數(shù)據(jù)傳輸結(jié)束,連接已經(jīng)關(guān)閉。Mozilla Firefox 提供了對(duì) Streaming AJAX 的支持, 即 readystate 為 3 時(shí)(數(shù)據(jù)仍在傳輸中),客戶端可以讀取數(shù)據(jù),從而無須關(guān)閉連接,就能讀取處理服務(wù)器端返回的信息。IE 在 readystate 為 3 時(shí),不能讀取服務(wù)器返回的數(shù)據(jù),目前 IE 不支持基于 Streaming AJAX。
基于 Iframe 及 htmlfile 的流(streaming)方式
iframe 是很早就存在的一種 HTML 標(biāo)記, 通過在 HTML 頁面里嵌入一個(gè)隱蔵幀,然后將這個(gè)隱蔵幀的 src屬性設(shè)為對(duì)一個(gè)長連接的請(qǐng)求,服務(wù)器端就能源源不斷地往客戶端輸入數(shù)據(jù)。
圖 3. 基于流方式的服務(wù)器推模型
上節(jié)提到的 AJAX 方案是在 JavaScript 里處理 XMLHttpRequest 從服務(wù)器取回的數(shù)據(jù),然后 Javascript 可以很方便的去控制 HTML 頁面的顯示。同樣的思路用在 iframe 方案的客戶端,iframe 服務(wù)器端并不返回直接顯示在頁面的數(shù)據(jù),而是返回對(duì)客戶端 Javascript 函數(shù)的調(diào)用,如“”。服務(wù)器端將返回的數(shù)據(jù)作為客戶端JavaScript 函數(shù)的參數(shù)傳遞;客戶端瀏覽器的 Javascript 引擎在收到服務(wù)器返回的 JavaScript 調(diào)用時(shí)就會(huì)去執(zhí)行代碼。
從 圖 3 可以看到,每次數(shù)據(jù)傳送不會(huì)關(guān)閉連接,連接只會(huì)在通信出現(xiàn)錯(cuò)誤時(shí),或是連接重建時(shí)關(guān)閉(一些防火墻常被設(shè)置為丟棄過長的連接, 服務(wù)器端可以設(shè)置一個(gè)超時(shí)時(shí)間, 超時(shí)后通知客戶端重新建立連接,并關(guān)閉原來的連接)。
使用 iframe 請(qǐng)求一個(gè)長連接有一個(gè)很明顯的不足之處:IE、Morzilla Firefox 下端的進(jìn)度欄都會(huì)顯示加載沒有完成,而且 IE 上方的圖標(biāo)會(huì)不停的轉(zhuǎn)動(dòng),表示加載正在進(jìn)行。Google 的天才們使用一個(gè)稱為“htmlfile”的 ActiveX 解決了在 IE 中的加載顯示問題,并將這種方法用到了 gmail+gtalk 產(chǎn)品中。Alex Russell 在 “What else is burried down in the depth’s of Google’s amazing JavaScript?”文章中介紹了這種方法。Zeitoun 網(wǎng)站提供的 comet-iframe.tar.gz,封裝了一個(gè)基于 iframe 和 htmlfile 的 JavaScript comet 對(duì)象,支持 IE、Mozilla Firefox 瀏覽器,可以作為參考。
websocket的消息格式
上面提到在完成協(xié)議升級(jí)之后,兩端就會(huì)用webscoket的數(shù)據(jù)格式進(jìn)行通信。
數(shù)據(jù)包在websocket中被叫做幀。
我們來看下它的數(shù)據(jù)格式長什么樣子。
websocket報(bào)文格式
這里面字段很多,但我們只需要關(guān)注下面這幾個(gè)。
opcode字段:這個(gè)是用來標(biāo)志這是個(gè)什么類型的數(shù)據(jù)幀。比如。
等于1時(shí)是指text類型(string)的數(shù)據(jù)包等于2是二進(jìn)制數(shù)據(jù)類型([]byte)的數(shù)據(jù)包等于8是關(guān)閉連接的信號(hào)
payload字段:存放的是我們真正想要傳輸?shù)臄?shù)據(jù)的長度,單位是字節(jié)。比如你要發(fā)送的數(shù)據(jù)是字符串"111",那它的長度就是3。
另外,可以看到,我們存放payload長度的字段有好幾個(gè),我們既可以用最前面的7bit, 也可以用后面的7+16bit或7+64bit。
那么問題就來了。
我們知道,在數(shù)據(jù)層面,大家都是01二進(jìn)制流。我怎么知道什么情況下應(yīng)該讀7bit,什么情況下應(yīng)該讀7+16bit呢?
websocket會(huì)用最開始的7bit做標(biāo)志位。不管接下來的數(shù)據(jù)有多大,都先讀最先的7個(gè)bit,根據(jù)它的取值決定還要不要再讀個(gè)16bit或64bit。
如果最開始的7bit的值是 0~125,那么它就表示了 payload 全部長度,只讀最開始的7個(gè)bit就完事了。
payload長度在0到125之間
如果是126(0x7E)。那它表示payload的長度范圍在 126~65535 之間,接下來還需要再讀16bit。這16bit會(huì)包含payload的真實(shí)長度。
payload長度在126到65535之間
如果是127(0x7F)。那它表示payload的長度范圍>=65536,接下來還需要再讀64bit。這64bit會(huì)包含payload的長度。這能放2的64次方byte的數(shù)據(jù),換算一下好多個(gè)TB,肯定夠用了。
payload長度大于等于65536的情況
payload data字段:這里存放的就是真正要傳輸?shù)臄?shù)據(jù),在知道了上面的payload長度后,就可以根據(jù)這個(gè)值去截取對(duì)應(yīng)的數(shù)據(jù)。
大家有沒有發(fā)現(xiàn)一個(gè)小細(xì)節(jié),websocket的數(shù)據(jù)格式也是 數(shù)據(jù)頭(內(nèi)含payload長度) + payload data 的形式。
TCP協(xié)議本身就是全雙工,但直接使用純裸TCP去傳輸數(shù)據(jù),會(huì)有粘包的"問題"。為了解決這個(gè)問題,上層協(xié)議一般會(huì)用消息頭+消息體的格式去重新包裝要發(fā)的數(shù)據(jù)。
而消息頭里一般含有消息體的長度,通過這個(gè)長度可以去截取真正的消息體。
SSE
服務(wù)器發(fā)送事件(Server-sent events),簡(jiǎn)稱SSE。
SSE它是基于HTTP協(xié)議的,我們知道一般意義上的HTTP協(xié)議是無法做到服務(wù)端主動(dòng)向客戶端推送消息的,但SSE是個(gè)例外,它變換了一種思路。
SSE在服務(wù)器和客戶端之間打開一個(gè)單向通道,服務(wù)端響應(yīng)的不再是一次性的數(shù)據(jù)包而是text/event-stream類型的數(shù)據(jù)流信息,在有數(shù)據(jù)變更時(shí)從服務(wù)器流式傳輸?shù)娇蛻舳恕?/p>
整體的實(shí)現(xiàn)思路有點(diǎn)類似于在線視頻播放,視頻流會(huì)連續(xù)不斷的推送到瀏覽器,你也可以理解成,客戶端在完成一次用時(shí)很長(網(wǎng)絡(luò)不暢)的下載。
SSE與WebSocket作用相似,都可以建立服務(wù)端與瀏覽器之間的通信,實(shí)現(xiàn)服務(wù)端向客戶端推送消息,但還是有些許不同:
SSE 是基于HTTP協(xié)議的,它們不需要特殊的協(xié)議或服務(wù)器實(shí)現(xiàn)即可工作;WebSocket需單獨(dú)服務(wù)器來處理協(xié)議。SSE 單向通信,只能由服務(wù)端向客戶端單向通信;webSocket全雙工通信,即通信的雙方可以同時(shí)發(fā)送和接受信息。SSE 實(shí)現(xiàn)簡(jiǎn)單開發(fā)成本低,無需引入其他組件;WebSocket傳輸數(shù)據(jù)需做二次解析,開發(fā)門檻高一些。SSE 默認(rèn)支持?jǐn)嗑€重連;WebSocket則需要自己實(shí)現(xiàn)。SSE 只能傳送文本消息,二進(jìn)制數(shù)據(jù)需要經(jīng)過編碼后傳送;WebSocket默認(rèn)支持傳送二進(jìn)制數(shù)據(jù)。
SSE 與 WebSocket 該如何選擇?
SSE好像一直不被大家所熟知,一部分原因是出現(xiàn)了WebSockets,這個(gè)提供了更豐富的協(xié)議來執(zhí)行雙向、全雙工通信。對(duì)于游戲、即時(shí)通信以及需要雙向近乎實(shí)時(shí)更新的場(chǎng)景,擁有雙向通道更具吸引力。
但是,在某些情況下,不需要從客戶端發(fā)送數(shù)據(jù)。而你只需要一些服務(wù)器操作的更新。比如:站內(nèi)信、未讀消息數(shù)、狀態(tài)更新、股票行情、監(jiān)控?cái)?shù)量等場(chǎng)景,SEE不管是從實(shí)現(xiàn)的難易和成本上都更加有優(yōu)勢(shì)。此外,SSE 具有WebSockets在設(shè)計(jì)上缺乏的多種功能,例如:自動(dòng)重新連接、事件ID和發(fā)送任意事件的能力。
前端只需進(jìn)行一次HTTP請(qǐng)求,帶上唯一ID,打開事件流,監(jiān)聽服務(wù)端推送的事件就可以了
服務(wù)端的實(shí)現(xiàn)更簡(jiǎn)單,創(chuàng)建一個(gè)SseEmitter對(duì)象放入sseEmitterMap進(jìn)行管理
private static Map
/**
* 創(chuàng)建連接
*
*/
public static SseEmitter connect(String userId) {
try {
// 設(shè)置超時(shí)時(shí)間,0表示不過期。默認(rèn)30秒
SseEmitter sseEmitter = new SseEmitter(0L);
// 注冊(cè)回調(diào)
sseEmitter.onCompletion(completionCallBack(userId));
sseEmitter.onError(errorCallBack(userId));
sseEmitter.onTimeout(timeoutCallBack(userId));
sseEmitterMap.put(userId, sseEmitter);
count.getAndIncrement();
return sseEmitter;
} catch (Exception e) {
log.info("創(chuàng)建新的sse連接異常,當(dāng)前用戶:{}", userId);
}
return null;
}
/**
* 給指定用戶發(fā)送消息
*
*/
public static void sendMessage(String userId, String message) {
if (sseEmitterMap.containsKey(userId)) {
try {
sseEmitterMap.get(userId).send(message);
} catch (IOException e) {
log.error("用戶[{}]推送異常:{}", userId, e.getMessage());
removeUser(userId);
}
}
}
Comet的優(yōu)缺點(diǎn)
優(yōu)點(diǎn): 實(shí)時(shí)性好(消息延時(shí)小);性能好(能支持大量用戶)
缺點(diǎn): 長期占用連接,喪失了無狀態(tài)高并發(fā)的特點(diǎn)。
HTTP
以rfc文檔為準(zhǔn)
依賴于tcp
HTTP,Hypertext Transfer Protocol,超文本傳輸協(xié)議
HTTP是一個(gè)基于“請(qǐng)求與響應(yīng)”模式的、無狀態(tài)的應(yīng)用層協(xié)議
HTTP協(xié)議采用URL作為定位網(wǎng)絡(luò)資源的標(biāo)識(shí),URL格式如下:
scheme://host.domain:port/path/filename 參數(shù)說明:
scheme - 定義因特網(wǎng)服務(wù)的類型。 也稱為協(xié)議名,如http\ftp - [ ] ftp雖然瀏覽器可以,但不算http吧?還是說url的類型不是http定義的?host - 定義域主機(jī)(http 的默認(rèn)主機(jī)是 www)domain - 定義因特網(wǎng)域名,比如 runoob.comport - 定義主機(jī)上的端口號(hào)(http 的默認(rèn)端口號(hào)是 80)path - 定義服務(wù)器上的路徑(如果省略,則文檔必須位于網(wǎng)站的根目錄中)。filename - 定義文檔/資源的名稱 這里叫”“文檔,是還沒出現(xiàn)cgi之類的動(dòng)態(tài)命令的時(shí)候?
URL 字符編碼
URL 只能使用 ASCII 字符集來通過因特網(wǎng)進(jìn)行發(fā)送。由于 URL 常常會(huì)包含 ASCII 集合之外的字符,URL 必須轉(zhuǎn)換為有效的 ASCII 格式(如通過base64系列編碼,將非ASCII字符轉(zhuǎn)換成"%" 其后跟隨兩位的十六進(jìn)制數(shù)。以這種格式的ASCII碼來表示一個(gè)非ASCII碼)。
URL 不能包含空格。URL 編碼通常使用 + 來替換空格。
如jwt中需要base64URL來進(jìn)行轉(zhuǎn)碼
只能ASCII 是因?yàn)榫W(wǎng)絡(luò)都這樣嘛?
HTTP七種請(qǐng)求方法(method)
方法說明GET請(qǐng)求獲取URL位置的資源HEAD請(qǐng)求獲取URL位置資源的響應(yīng)消息報(bào)告,即獲得該資源的頭部信息POST請(qǐng)求向URL位置的資源后附加新的數(shù)據(jù)PUT請(qǐng)求向URL位置存儲(chǔ)一個(gè)資源,覆蓋原URL位置的資源PATCH請(qǐng)求局部更新URL位置的資源,即改變?cè)撎庂Y源的部分內(nèi)容DELETE請(qǐng)求刪除URL位置存儲(chǔ)的資源
什么是長連接、短連接?
在HTTP/1.0中默認(rèn)使用短連接。也就是說,客戶端和服務(wù)器每進(jìn)行一次HTTP操作,就建立一次連接,任務(wù)結(jié)束就中斷連接。當(dāng)客戶端瀏覽器訪問的某個(gè)HTML或其他類型的Web頁中包含有其他的Web資源(如JavaScript文件、圖像文件、CSS文件等),每遇到這樣一個(gè)Web資源,瀏覽器就會(huì)重新建立一個(gè)HTTP會(huì)話。
而從HTTP/1.1起,默認(rèn)使用長連接,用以保持連接特性。使用長連接的HTTP協(xié)議,會(huì)在響應(yīng)頭加入這行代碼:
Connection:keep-alive
在使用長連接的情況下,當(dāng)一個(gè)網(wǎng)頁打開完成后,客戶端和服務(wù)器之間用于傳輸HTTP數(shù)據(jù)的TCP連接不會(huì)關(guān)閉,客戶端再次訪問這個(gè)服務(wù)器時(shí),會(huì)繼續(xù)使用這一條已經(jīng)建立的連接。Keep-Alive不會(huì)永久保持連接,它有一個(gè)保持時(shí)間,可以在web服務(wù)器?http服務(wù)器?(如Apache)中設(shè)定這個(gè)時(shí)間。當(dāng)然,實(shí)現(xiàn)長連接需要客戶端和服務(wù)端都支持長連接。
HTTP協(xié)議的長連接和短連接,實(shí)質(zhì)上是TCP協(xié)議的長連接和短連接。(關(guān)不關(guān)tcp連接的區(qū)別,畢竟是用tcp傳輸?shù)???/p>
由于瀏覽器中的頁面每次需要全部刷新才能從服務(wù)器端獲得最新的數(shù)據(jù)或向服務(wù)器傳送數(shù)據(jù),這樣產(chǎn)生的延遲所帶來的視覺感受非常糟糕。因此很多的桌面應(yīng)用為了獲得更友好的界面放棄了Web技術(shù),或者采用瀏覽器的插件技術(shù)(ActiveX、Applet、Flash等)。但是瀏覽器插件技術(shù)本身又有許多問題,例如跨平臺(tái)問題和插件版本兼容性問題。
把 IFrame 嵌在“htmlfile“的 ActiveX 組件中可以解決 IE 的加載顯示問題
http Header
https://www.cnblogs.com/gotodsp/p/6366163.html
如果是默認(rèn)消息頭名稱,消息頭格式已經(jīng)固定,即便輸入的大小寫有誤,也會(huì)給你翻譯成默認(rèn)的寫法,如果自己定義的,會(huì)自動(dòng)給你翻譯成小寫,所以傳參數(shù)的名稱都用小寫字母即可,否則可能取不到值,
曾經(jīng)建議以X開頭的是拓展的Header信息,后來懶得管了doge
我見到的Http Header信息
代理(nginx為例)
key描述例子/描述解釋X-Forwarded-For表示 HTTP 路過的ipX-Forwarded-For: clientIP, proxy1IP, proxy2IP由后一個(gè)代理追加前一代理的信息Remote Address當(dāng)前HTTP請(qǐng)求的源地址tcp連接必須知道源地址,若偽造則無法收到消息可用于獲取最后一個(gè)代理服務(wù)器的ipX-Real-IP沒啥規(guī)定通常被代理用來表示與它產(chǎn)生 TCP 連接的設(shè)備 IP也就是設(shè)置為Remote Address
注:
Remote Address 和 代理服務(wù)器追加的X-Forwarded-For(前一個(gè)代理或請(qǐng)求端)IP通常是一樣的,都是向他發(fā)起連接請(qǐng)求端的IP
盡管在nginx中不是直接將X-Forwarded-For設(shè)置為Remote Address,因?yàn)樾枰白芳印倍皇歉采w 服務(wù)端沒必要再追加X-Forwarded-For,,它可以通過Remote Address獲取不可偽造的上一個(gè)代理的ip
對(duì)于偽造與隱藏:
如對(duì)于真實(shí)ip為114.248.238.236的機(jī)器發(fā)出的請(qǐng)求,(nginx代理一次,設(shè)置x-real-ip為Remote Address,X-Forwarded-For追加 )
curl http://t1.imququ.com:9009/ -H 'X-Forwarded-For: 1.1.1.1' -H 'X-Real-IP: 2.2.2.2'
# nginx代理后服務(wù)端輸出
remoteAddress: 127.0.0.1
x-forwarded-for: 1.1.1.1, 114.248.238.236
x-real-ip: 114.248.238.236
偽造:可以在發(fā)請(qǐng)求時(shí)偽造一些header信息,但由于Remote Address 不能偽造,所以x-real-ip和追加的x-forwarded-for是真實(shí)ip,之前的部分可以偽造
隱藏:源ip被隱藏在了x-forwarded-for的一個(gè)ip中(如果中間沒有被改寫或是一開始就偽造的話,且可以主動(dòng)隱藏),其他信息已經(jīng)被替換成代理服務(wù)器的(我主動(dòng)配置的)
事實(shí)上在這種常用設(shè)置下,第一臺(tái)代理還是知道真實(shí)的請(qǐng)求端地址的,服務(wù)端可能只知道上一次請(qǐng)求的真實(shí)地址;
偽造后,x-forwarded-for是以為前面還有一臺(tái)機(jī)器(1,1,1,1),,
那為什么要real ip呢?用偷偷藏起來的自定義ip偷偷保存真實(shí)地址么?
看來代理是多進(jìn)行了幾次http連接,作為中介,請(qǐng)求端和服務(wù)端的http連接都沒有聯(lián)系對(duì)方。
這也是為什么在nginx上配置跨域比較簡(jiǎn)單,和請(qǐng)求端溝通始終都是統(tǒng)一個(gè)源(代理),他從不同的源獲取東西再統(tǒng)一返回。
升級(jí)為websocket
key描述例子/描述解釋/補(bǔ)充UpgradewebsocketConnectionUpgrade/keep-alive/close1.1版本出現(xiàn),也可用于保持長連接
據(jù)說是為了向下兼容,HTTP1.1才正式規(guī)范Connection頭,之前的協(xié)議無法升級(jí);如果是HTTP1.0,不認(rèn)識(shí)Conncetion頭,就不升級(jí)?
linux系統(tǒng)中有一個(gè)httpd程序,是Apache HTTP服務(wù)器程序。直接執(zhí)行程序可啟動(dòng)服務(wù)器的服務(wù),在沒有自己安裝web服務(wù)器軟件時(shí)可以臨時(shí)用一下。
安全問題
GET POST方法以外的安全問題 這個(gè)是歷史問題。 開啟了WebDAV 的 IIS 允許匿名訪問者直接通過 PUT 往服務(wù)器上上傳文件,曾經(jīng)導(dǎo)致了很多嚴(yán)重的漏洞,具體可以搜下 IS put 。 此外 apache 默認(rèn)允許 trace,又導(dǎo)致了一批XSS。
圖解HTTP:“PUT 方法自身不帶驗(yàn)證機(jī)制,任何人都可以上傳文件 , 存在安全性問題,因此一般 的 Web 網(wǎng)站不使用該方法” 那 POST 方法自身又帶了什么安全驗(yàn)證機(jī)制呢? https://cloud.tencent.com/developer/article/1472910 webshell入侵↑
http與WEB
XHR/AJAX
在漸進(jìn)式 Web 應(yīng)用、單頁應(yīng)用和基于框架的應(yīng)用中,通常會(huì)使用 HTML 表單來發(fā)送數(shù)據(jù),而不會(huì)在收到響應(yīng)數(shù)據(jù)時(shí)加載新文檔。讓我們先來談?wù)劄槭裁催@需要一種不同的方法。
獲得整體界面的控制
如前一篇文章所述,標(biāo)準(zhǔn) HTML 表單提交會(huì)加載發(fā)送數(shù)據(jù)的 URL,這意味著瀏覽器窗口會(huì)以全頁面加載的方式進(jìn)行導(dǎo)航。避免全頁面加載可以避免網(wǎng)絡(luò)延遲和可能出現(xiàn)的視覺問題(如閃爍),從而提供更流暢的體驗(yàn)。
許多現(xiàn)代用戶界面只使用 HTML 表單來收集用戶輸入,而不是用于數(shù)據(jù)提交。當(dāng)用戶嘗試發(fā)送數(shù)據(jù)時(shí),應(yīng)用程序會(huì)控制并在后臺(tái)異步傳輸數(shù)據(jù),只更新用戶界面中需要更改的部分。
表單
文件傳輸
用 HTML 表單發(fā)送文件是一個(gè)特殊的例子。文件是二進(jìn)制數(shù)據(jù)。由于 HTTP 是一種文本協(xié)議,所以處理二進(jìn)制數(shù)據(jù)有特殊的要求。
enctype 屬性
該屬性允許你指定在提交表單時(shí)所生成的請(qǐng)求中的Content-Type的 HTTP 數(shù)據(jù)頭的值。這個(gè)數(shù)據(jù)頭非常重要,因?yàn)樗嬖V服務(wù)器正在發(fā)送什么樣的數(shù)據(jù)。默認(rèn)情況下,它的值是application/x-www-form-urlencoded。它的意思是:“這是已編碼為 URL 參數(shù)的表單數(shù)據(jù)?!?/p>
如果你想要發(fā)送文件,你需要額外的三個(gè)步驟:
將method屬性設(shè)置為POST,因?yàn)槲募?nèi)容不能放入 URL 參數(shù)中。將enctype的值設(shè)置為multipart/form-data,因?yàn)閿?shù)據(jù)將被分成多個(gè)部分,每個(gè)文件單獨(dú)占用一個(gè)部分,表單正文中包含的文本數(shù)據(jù)(如果文本也輸入到表單中)占用一個(gè)部分。
處理二進(jìn)制數(shù)據(jù)](https://developer.mozilla.org/zh-CN/docs/Learn/Forms/Sending_forms_through_JavaScript#處理二進(jìn)制數(shù)據(jù))
如果你使用一個(gè)含有 組件的 FormData 表單對(duì)象,數(shù)據(jù)會(huì)被自動(dòng)處理。但是要手動(dòng)發(fā)送二進(jìn)制數(shù)據(jù)的話,還有額外的工作要做。
在現(xiàn)代網(wǎng)絡(luò)上,二進(jìn)制數(shù)據(jù)有很多來源:例如 FileReader、Canvas、WebRTC,等等。不幸的是,一些過時(shí)的瀏覽器無法訪問二進(jìn)制數(shù)據(jù),或是需要非常復(fù)雜的工作環(huán)境。這些遺留問題已經(jīng)超出了本文的涵蓋范圍。如果你想了解更多關(guān)于 FileReader API 的知識(shí),參見如何在 web 應(yīng)用程序中使用文件。
Fetch API
TCP隊(duì)頭阻塞
我們知道,TCP傳輸過程中會(huì)把數(shù)據(jù)拆分為一個(gè)個(gè)按照順序排列的數(shù)據(jù)包,這些數(shù)據(jù)包通過網(wǎng)絡(luò)傳輸?shù)搅私邮斩?,接收端再按照順序?qū)⑦@些數(shù)據(jù)包組合成原始數(shù)據(jù),這樣就完成了數(shù)據(jù)傳輸。
但是如果其中的某一個(gè)數(shù)據(jù)包沒有按照順序到達(dá),接收端會(huì)一直保持連接等待數(shù)據(jù)包返回,這時(shí)候就會(huì)阻塞后續(xù)請(qǐng)求。這就發(fā)生了TCP隊(duì)頭阻塞。
一個(gè)是發(fā)送窗口的隊(duì)頭阻塞,另外一個(gè)是接收窗口的隊(duì)頭阻塞。
1、發(fā)送窗口的隊(duì)頭阻塞。
TCP 發(fā)送出去的數(shù)據(jù),都是需要按序確認(rèn)的,只有在數(shù)據(jù)都被按順序確認(rèn)完后,發(fā)送窗口才會(huì)往前滑動(dòng)。舉個(gè)例子,比如下圖的發(fā)送方把發(fā)送窗口內(nèi)的數(shù)據(jù)全部都發(fā)出去了,可用窗口的大小就為 0 了,表明可用窗口耗盡,在沒收到 ACK 確認(rèn)之前是無法繼續(xù)發(fā)送數(shù)據(jù)了。
接著,當(dāng)發(fā)送方收到對(duì)第 32~36 字節(jié)的 ACK 確認(rèn)應(yīng)答后,則滑動(dòng)窗口往右邊移動(dòng) 5 個(gè)字節(jié),因?yàn)橛?5 個(gè)字節(jié)的數(shù)據(jù)被應(yīng)答確認(rèn),接下來第 52~56 字節(jié)又變成了可用窗口,那么后續(xù)也就可以發(fā)送 52~56 這 5 個(gè)字節(jié)的數(shù)據(jù)了。
但是如果某個(gè)數(shù)據(jù)報(bào)文丟失或者其對(duì)應(yīng)的 ACK 報(bào)文在網(wǎng)絡(luò)中丟失,會(huì)導(dǎo)致發(fā)送方無法移動(dòng)發(fā)送窗口,這時(shí)就無法再發(fā)送新的數(shù)據(jù),只能超時(shí)重傳這個(gè)數(shù)據(jù)報(bào)文,直到收到這個(gè)重傳報(bào)文的 ACK,發(fā)送窗口才會(huì)移動(dòng),繼續(xù)后面的發(fā)送行為。
舉個(gè)例子,比如下圖,客戶端是發(fā)送方,服務(wù)器是接收方。
客戶端發(fā)送了第 5~9 字節(jié)的數(shù)據(jù),但是第 5 字節(jié)的 ACK 確認(rèn)報(bào)文在網(wǎng)絡(luò)中丟失了,那么即使客戶端收到第 6~9 字節(jié)的 ACK 確認(rèn)報(bào)文,發(fā)送窗口也不會(huì)往前移動(dòng)。
此時(shí)的第 5 字節(jié)相當(dāng)于“隊(duì)頭”,因?yàn)闆]有收到“隊(duì)頭”的 ACK 確認(rèn)報(bào)文,導(dǎo)致發(fā)送窗口無法往前移動(dòng),此時(shí)發(fā)送方就無法繼續(xù)發(fā)送后面的數(shù)據(jù),相當(dāng)于按下了發(fā)送行為的暫停鍵,這就是發(fā)送窗口的隊(duì)頭阻塞問題。
2、接收窗口的隊(duì)頭阻塞。
接收方收到的數(shù)據(jù)范圍必須在接收窗口范圍內(nèi),如果收到超過接收窗口范圍的數(shù)據(jù),就會(huì)丟棄該數(shù)據(jù),比如下圖接收窗口的范圍是 32 ~ 51 字節(jié),如果收到第 52 字節(jié)以上數(shù)據(jù)都會(huì)被丟棄。
接收窗口什么時(shí)候才能滑動(dòng)?當(dāng)接收窗口收到有序數(shù)據(jù)時(shí),接收窗口才能往前滑動(dòng),然后那些已經(jīng)接收并且被確認(rèn)的「有序」數(shù)據(jù)就可以被應(yīng)用層讀取。
但是,當(dāng)接收窗口收到的數(shù)據(jù)不是有序的,比如收到第 33~40 字節(jié)的數(shù)據(jù),由于第 32 字節(jié)數(shù)據(jù)沒有收到, 接收窗口無法向前滑動(dòng),那么即使先收到第 33~40 字節(jié)的數(shù)據(jù),這些數(shù)據(jù)也無法被應(yīng)用層讀取的。只有當(dāng)發(fā)送方重傳了第 32 字節(jié)數(shù)據(jù)并且被接收方收到后,接收窗口才會(huì)往前滑動(dòng),然后應(yīng)用層才能從內(nèi)核讀取第 32~40 字節(jié)的數(shù)據(jù)。
好了,至此發(fā)送窗口和接收窗口的隊(duì)頭阻塞問題都說完了,這兩個(gè)問題的原因都是因?yàn)?TCP 必須按序處理數(shù)據(jù),也就是 TCP 層為了保證數(shù)據(jù)的有序性,只有在處理完有序的數(shù)據(jù)后,滑動(dòng)窗口才能往前滑動(dòng),否則就停留。
停留「發(fā)送窗口」會(huì)使得發(fā)送方無法繼續(xù)發(fā)送數(shù)據(jù)。停留「接收窗口」會(huì)使得應(yīng)用層無法讀取新的數(shù)據(jù)。
HTTP/1.1的管道化持久連接也是使得同一個(gè)TCP鏈接可以被多個(gè)HTTP使用,但是HTTP/1.1中規(guī)定一個(gè)域名可以有6個(gè)TCP連接。而HTTP/2中,同一個(gè)域名只是用一個(gè)TCP連接。
所以,在HTTP/2中,TCP隊(duì)頭阻塞造成的影響會(huì)更大,因?yàn)镠TTP/2的多路復(fù)用技術(shù)使得多個(gè)請(qǐng)求其實(shí)是基于同一個(gè)TCP連接的,那如果某一個(gè)請(qǐng)求造成了TCP隊(duì)頭阻塞,那么多個(gè)請(qǐng)求都會(huì)受到影響。
我們都知道TCP的可靠連接是基于三次握手與四次揮手實(shí)現(xiàn)的。但是問題是三次握手是需要消耗時(shí)間的。
TCP三次握手的過程客戶端和服務(wù)器之間需要交互三次,那么也就是說需要額外消耗1.5 RTT。
RTT:網(wǎng)絡(luò)延遲(Round Trip Time)。他是指一個(gè)請(qǐng)求從客戶端瀏覽器發(fā)送一個(gè)請(qǐng)求數(shù)據(jù)包到服務(wù)器,再從服務(wù)器得到響應(yīng)數(shù)據(jù)包的這段時(shí)間。RTT 是反映網(wǎng)絡(luò)性能的一個(gè)重要指標(biāo)。
RTT很有意思,把概念抽象出來使用計(jì)算,而不是用具體的時(shí)間
即使升級(jí)協(xié)議,中間硬件設(shè)備也難以更換,只能棄用
于是,HTTP/3.0在基于UDP+迪菲赫爾曼算法(Diffie–Hellman)之上實(shí)現(xiàn)了QUIC協(xié)議(Quick UDP Internet Connections)。
QUIC協(xié)議有以下特點(diǎn):
基于UDP的傳輸層協(xié)議:它使用UDP端口號(hào)來識(shí)別指定機(jī)器上的特定服務(wù)器。可靠性:雖然UDP是不可靠傳輸協(xié)議,但是QUIC在UDP的基礎(chǔ)上做了些改造,使得他提供了和TCP類似的可靠性。它提供了數(shù)據(jù)包重傳、擁塞控制、調(diào)整傳輸節(jié)奏以及其他一些TCP中存在的特性。實(shí)現(xiàn)了無序、并發(fā)字節(jié)流:QUIC的單個(gè)數(shù)據(jù)流可以保證有序交付,但多個(gè)數(shù)據(jù)流之間可能亂序,這意味著單個(gè)數(shù)據(jù)流的傳輸是按序的,但是多個(gè)數(shù)據(jù)流中接收方收到的順序可能與發(fā)送方的發(fā)送順序不同!快速握手:QUIC提供0-RTT和1-RTT的連接建立使用TLS 1.3傳輸層安全協(xié)議:與更早的TLS版本相比,TLS 1.3有著很多優(yōu)點(diǎn),但使用它的最主要原因是其握手所花費(fèi)的往返次數(shù)更低,從而能降低協(xié)議的延遲。
HTTP/2 的隊(duì)頭阻塞
HTTP/2 通過抽象出 Stream 的概念,實(shí)現(xiàn)了 HTTP 并發(fā)傳輸,一個(gè) Stream 就代表 HTTP/1.1 里的請(qǐng)求和響應(yīng)。
在 HTTP/2 連接上,不同 Stream 的幀是可以亂序發(fā)送的(因此可以并發(fā)不同的 Stream ),因?yàn)槊總€(gè)幀的頭部會(huì)攜帶 Stream ID 信息,所以接收端可以通過 Stream ID 有序組裝成 HTTP 消息,而同一 Stream 內(nèi)部的幀必須是嚴(yán)格有序的。
但是 HTTP/2 多個(gè) Stream 請(qǐng)求都是在一條 TCP 連接上傳輸,這意味著多個(gè) Stream 共用同一個(gè) TCP 滑動(dòng)窗口,那么當(dāng)發(fā)生數(shù)據(jù)丟失,滑動(dòng)窗口是無法往前移動(dòng)的,此時(shí)就會(huì)阻塞住所有的 HTTP 請(qǐng)求,這屬于 TCP 層隊(duì)頭阻塞。
郵件協(xié)議
POP\IMAP負(fù)責(zé)收,SMTP負(fù)責(zé)發(fā)
平時(shí)我們主要使用網(wǎng)頁郵箱,而通過開啟IMAP/SMTP服務(wù)或POP3/SMTP服務(wù),就可以在其他客戶端收發(fā)該郵箱的郵件
在網(wǎng)頁郵箱的設(shè)置中可以看到POP\IMAP\SMTP的服務(wù)器地址(也可以直接百度),將其配置到客戶端軟件即可(如電腦或手機(jī)自帶的“郵件”應(yīng)用)。
郵箱協(xié)議也可以使用SSL加密
騰訊企業(yè)郵箱:
POP3/SMTP協(xié)議
接收郵件服務(wù)器:pop.exmail.qq.com (端口 110),使用SSL,端口號(hào)995 發(fā)送郵件服務(wù)器:smtp.exmail.qq.com (端口 25),使用SSL,端口號(hào)465
IMAP協(xié)議
接收郵件服務(wù)器:imap.exmail.qq.com (端口 143),使用SSL,端口號(hào)993 發(fā)送郵件服務(wù)器:smtp.exmail.qq.com (端口 25),使用SSL,端口號(hào)465
我們學(xué)校教育郵箱用的騰訊企業(yè)郵箱,默認(rèn)開啟了IMAP/SMTP服務(wù),將服務(wù)區(qū)和端口設(shè)置到windows的郵件應(yīng)用即可使用。
IMAP是什么? IMAP,即Internet Message Access Protocol(互聯(lián)網(wǎng)郵件訪問協(xié)議),您可以通過這種協(xié)議從郵件服務(wù)器上獲取郵件的信息、下載郵件等。IMAP與POP類似,都是一種郵件獲取協(xié)議。
IMAP和POP有什么區(qū)別?
POP允許電子郵件客戶端下載服務(wù)器上的郵件,但是您在電子郵件客戶端的操作(如:移動(dòng)郵件、標(biāo)記已讀等),這是不會(huì)反饋到服務(wù)器上的,比如:您通過電子郵件客戶端收取了QQ郵箱中的3封郵件并移動(dòng)到了其他文件夾,這些移動(dòng)動(dòng)作是不會(huì)反饋到服務(wù)器上的,也就是說,QQ郵箱服務(wù)器上的這些郵件是沒有同時(shí)被移動(dòng)的 。但是IMAP就不同了,電子郵件客戶端的操作都會(huì)反饋到服務(wù)器上,您對(duì)郵件進(jìn)行的操作(如:移動(dòng)郵件、標(biāo)記已讀等),服務(wù)器上的郵件也會(huì)做相應(yīng)的動(dòng)作。也就是說,IMAP是“雙向”的。
同時(shí),IMAP可以只下載郵件的主題,只有當(dāng)您真正需要的時(shí)候,才會(huì)下載郵件的所有內(nèi)容。
其他
MQTT
MQTT 全稱(Message Queue Telemetry Transport):一種基于發(fā)布/訂閱(publish/subscribe)模式的輕量級(jí)通訊協(xié)議,通過訂閱相應(yīng)的主題來獲取消息,是物聯(lián)網(wǎng)(Internet of Thing)中的一個(gè)標(biāo)準(zhǔn)傳輸協(xié)議。
該協(xié)議將消息的發(fā)布者(publisher)與訂閱者(subscriber)進(jìn)行分離,因此可以在不可靠的網(wǎng)絡(luò)環(huán)境中,為遠(yuǎn)程連接的設(shè)備提供可靠的消息服務(wù),使用方式與傳統(tǒng)的MQ有點(diǎn)類似。
MQTT 協(xié)議位于應(yīng)用層,MQTT 協(xié)議構(gòu)建于TCP/IP協(xié)議上
MQTT協(xié)議為什么在物聯(lián)網(wǎng)(IOT)中如此受偏愛?而不是其它協(xié)議,比如我們更為熟悉的 HTTP協(xié)議呢?
首先HTTP協(xié)議它是一種同步協(xié)議,客戶端請(qǐng)求后需要等待服務(wù)器的響應(yīng)。而在物聯(lián)網(wǎng)(IOT)環(huán)境中,設(shè)備會(huì)很受制于環(huán)境的影響,比如帶寬低、網(wǎng)絡(luò)延遲高、網(wǎng)絡(luò)通信不穩(wěn)定等,顯然異步消息協(xié)議更為適合IOT應(yīng)用程序。HTTP是單向的,如果要獲取消息客戶端必須發(fā)起連接,而在物聯(lián)網(wǎng)(IOT)應(yīng)用程序中,設(shè)備或傳感器往往都是客戶端,這意味著它們無法被動(dòng)地接收來自網(wǎng)絡(luò)的命令。通常需要將一條命令或者消息,發(fā)送到網(wǎng)絡(luò)上的所有設(shè)備上。HTTP要實(shí)現(xiàn)這樣的功能不但很困難,而且成本極高。
附錄
其他命令行網(wǎng)絡(luò)工具/無對(duì)應(yīng)協(xié)議
有些在命令行常用的網(wǎng)絡(luò)工具,使用的協(xié)議和命令名沒有關(guān)聯(lián)。
curl
curl ip:端口
可用于測(cè)試端口是否聯(lián)通
ping
Linux ping 命令用于檢測(cè)主機(jī)。
執(zhí)行 ping 指令會(huì)使用 ICMP 傳輸協(xié)議,發(fā)出要求回應(yīng)的信息,若遠(yuǎn)端主機(jī)的網(wǎng)絡(luò)功能沒有問題,就會(huì)回應(yīng)該信息,因而得知該主機(jī)運(yùn)作正常。
平行的形容詞
實(shí)時(shí)雙工?(通信方式?)有狀態(tài)?下次登錄時(shí)有上次的狀態(tài),這叫有狀態(tài);每次都和新的一樣,叫無狀態(tài)連接?
##命令行網(wǎng)絡(luò)工具
curl
發(fā)送、接收http請(qǐng)求,可以追蹤。也可和wget一樣下載東西
也支持ftp協(xié)議,也可以登錄用戶
ping
起源于聲納。ping-pong就是一來一回
ping ip或主機(jī)名
發(fā)送ping數(shù)據(jù)包,基于icmp,ttl
telnet
在命令行實(shí)現(xiàn)遠(yuǎn)程登錄、控制。遠(yuǎn)程登錄需要用戶名和密碼,登陸后可以遠(yuǎn)程執(zhí)行命令。屬于C/S模型的服務(wù)
明文傳輸主要傳輸字符串(命令與輸出),不適合傳文件
telnet ip 端口
可用于測(cè)試端口是否連通、占用。
Telnet遠(yuǎn)程登錄服務(wù)分為以下4個(gè)過程:
1)本地與遠(yuǎn)程主機(jī)建立連接。該過程實(shí)際上是建立一個(gè)TCP連接,用戶必須知道遠(yuǎn)程主機(jī)的Ip地址或域名;
2)將本地終端上輸入的用戶名和口令及以后輸入的任何命令或字符以NVT(Net Virtual Terminal)格式傳送到遠(yuǎn)程主機(jī)。該過程實(shí)際上是從本地主機(jī)向遠(yuǎn)程主機(jī)發(fā)送一個(gè)IP數(shù)據(jù)包;
3)將遠(yuǎn)程主機(jī)輸出的NVT格式的數(shù)據(jù)轉(zhuǎn)化為本地所接受的格式送回本地終端,包括輸入命令回顯和命令執(zhí)行結(jié)果;
4)最后,本地終端對(duì)遠(yuǎn)程主機(jī)進(jìn)行撤消連接。該過程是撤銷一個(gè)TCP連接。
適應(yīng)異構(gòu)-NVT
類似虛擬機(jī)的思想。太復(fù)雜就加一層!
為了使多個(gè)操作系統(tǒng)間的Telnet交互操作成為可能,就必須詳細(xì)了解異構(gòu)計(jì)算機(jī)和操作系統(tǒng)。比如,一些操作系統(tǒng)需要每行文本用ASCⅡ回車控制符(CR)結(jié)束,另一些系統(tǒng)則需要使用換行符(LF),還有一些系統(tǒng)需要用兩個(gè)字符的序列回車-換行(CR-LF);再比如,大多數(shù)操作系統(tǒng)為用戶提供了一個(gè)中斷程序運(yùn)行的快捷鍵,但這個(gè)快捷鍵在各個(gè)系統(tǒng)中有可能不同(一些系統(tǒng)使用CTRL+C,而另一些系統(tǒng)使用ESCAPE)。
為了適應(yīng)異構(gòu)環(huán)境,Telnet協(xié)議定義了數(shù)據(jù)和命令在Internet上的傳輸方式,此定義被稱作網(wǎng)絡(luò)虛擬終端NVT(Net Virtual Terminal)。它的應(yīng)用過程如下: 對(duì)于發(fā)送的數(shù)據(jù):客戶機(jī)軟件把來自用戶終端的按鍵和命令序列轉(zhuǎn)換為NVT格式,并發(fā)送到服務(wù)器,服務(wù)器軟件將收到的數(shù)據(jù)和命令,從NVT格式轉(zhuǎn)換為遠(yuǎn)地系統(tǒng)需要的格式; 對(duì)于返回的數(shù)據(jù):遠(yuǎn)地服務(wù)器將數(shù)據(jù)從遠(yuǎn)地機(jī)器的格式轉(zhuǎn)換為NVT格式,而本地客戶機(jī)將接收到的NVT格式數(shù)據(jù)再轉(zhuǎn)換為本地的格式。
win2000中的telnet默認(rèn)僅以NTLM方式驗(yàn)證身份,這就讓我們不得不關(guān)注NTLM這個(gè)東東,那么什么是NTLM呢?
早期的SMB協(xié)議在網(wǎng)絡(luò)上明文傳輸口令,后來出現(xiàn)了"LAN Manager Challenge/Response"驗(yàn)證機(jī)制,簡(jiǎn)稱LM,它十分簡(jiǎn)單以至很容易被破解,微軟隨后提出了WindowsNT挑戰(zhàn)/響應(yīng)驗(yàn)證機(jī)制,即NTLM?,F(xiàn)在已經(jīng)有了更新的NTLMv2以及Kerberos驗(yàn)證體系??纯窗俣萾elnet
GUI網(wǎng)絡(luò)工具
請(qǐng)求頭特征
HTTP 必須是 1.1 GET 請(qǐng)求 HTTP Header 中 Connection 字段的值必須為 Upgrade HTTP Header 中 Upgrade 字段必須為 websocket Sec-WebSocket-Key 字段的值是采用 base64 編碼的隨機(jī) 16 字節(jié)字符串 Sec-WebSocket-Protocol 字段的值記錄使用的子協(xié)議,比如 binary base64 Origin 表示請(qǐng)求來源
響應(yīng)頭特征
狀態(tài)碼是 101 表示 Switching Protocols Upgrade / Connection / Sec-WebSocket-Protocol 和請(qǐng)求頭一致 Sec-WebSocket-Accept 是通過請(qǐng)求頭的 Sec-WebSocket-Key 生成
二、短連接輪詢、長連接、Websocket 橫向?qū)Ρ?/p>
1. 短連接輪詢
很耗費(fèi) TCP 連接 每次連接導(dǎo)致 Header 重復(fù)發(fā)送 通過宏任務(wù)發(fā)起,受限于 Event Loop,無法保證及時(shí)性 Event Loop? 無效請(qǐng)求會(huì)很多
2. 長連接
HTTP keep-alive 開啟后雖然 TCP 可以復(fù)用,但是 Header 重復(fù)的問題并沒有解決 同時(shí) HTTP keep-alive 還有一個(gè)有效期,有效期結(jié)束后服務(wù)端會(huì)發(fā)偵查幀探查 TCP 是否有效
題外話: HTTP keep-alive 的作用是,告知服務(wù)端持久化當(dāng)前的 TCP 連接,不要立即斷開,以便后續(xù)的 HTTP 請(qǐng)求復(fù)用它,也就是我們所說的「長連接」
HTTP 的 keep-alive 是為了讓 TCP 活久一點(diǎn),而 TCP 本身也有一個(gè) keepalive(同http的keep-alive相比沒有橫杠)機(jī)制。這是 TCP 的一種檢測(cè)連接狀況的?;顧C(jī)制,keepalive 是 TCP ?;疃〞r(shí)器:TCP 建立后,如果閑置沒用,服務(wù)器不可能白等下去,閑置一段時(shí)間[可設(shè)置]后,服務(wù)器就會(huì)嘗試向客戶端發(fā)送偵測(cè)包,來判斷 TCP 連接狀況,如果沒有收到對(duì)方的回答(ACK包),就會(huì)過一會(huì)[可設(shè)置]再偵測(cè)一次,如果多次[可設(shè)置]都沒回答,就會(huì)丟棄這個(gè) TCP 連接
(TCP keepalive ?;钍疽鈭D)
消息推送方式
短輪詢浪費(fèi)帶寬和服務(wù)器資源。
長輪詢是對(duì)上邊短輪詢的一種改進(jìn)版本,在盡可能減少對(duì)服務(wù)器資源浪費(fèi)的同時(shí),保證消息的相對(duì)實(shí)時(shí)性。長輪詢?cè)谥虚g件中應(yīng)用的很廣泛,比如Nacos和apollo配置中心,消息隊(duì)列kafka、RocketMQ中都有用到長輪詢。
Nacos配置中心交互模型是push還是pull?一文中詳細(xì)介紹過Nacos長輪詢的實(shí)現(xiàn)原理。
這次我使用apollo配置中心實(shí)現(xiàn)長輪詢的方式,應(yīng)用了一個(gè)類DeferredResult,它是在servelet3.0后經(jīng)過Spring封裝提供的一種異步請(qǐng)求機(jī)制,直意就是延遲結(jié)果。
DeferredResult可以允許容器線程快速釋放占用的資源,不阻塞請(qǐng)求線程,以此接受更多的請(qǐng)求提升系統(tǒng)的吞吐量,然后啟動(dòng)異步工作線程處理真正的業(yè)務(wù)邏輯,處理完成調(diào)用DeferredResult.setResult(200)提交響應(yīng)結(jié)果。
下邊我們用長輪詢來實(shí)現(xiàn)消息推送。
因?yàn)橐粋€(gè)ID可能會(huì)被多個(gè)長輪詢請(qǐng)求監(jiān)聽,所以我采用了guava包提供的Multimap結(jié)構(gòu)存放長輪詢,一個(gè)key可以對(duì)應(yīng)多個(gè)value。一旦監(jiān)聽到key發(fā)生變化,對(duì)應(yīng)的所有長輪詢都會(huì)響應(yīng)。前端得到非請(qǐng)求超時(shí)的狀態(tài)碼,知曉數(shù)據(jù)變更,主動(dòng)查詢未讀消息數(shù)接口,更新頁面數(shù)據(jù)。
@Controller
@RequestMapping("/polling")
public class PollingController {
// 存放監(jiān)聽某個(gè)Id的長輪詢集合
// 線程同步結(jié)構(gòu)
public static Multimap
/**
* 設(shè)置監(jiān)聽
*/
@GetMapping(path = "watch/{id}")
@ResponseBody
public DeferredResult
// 延遲對(duì)象設(shè)置超時(shí)時(shí)間
DeferredResult
// 異步請(qǐng)求完成時(shí)移除 key,防止內(nèi)存溢出
deferredResult.onCompletion(() -> {
watchRequests.remove(id, deferredResult);
});
// 注冊(cè)長輪詢請(qǐng)求
watchRequests.put(id, deferredResult);
return deferredResult;
}
/**
* 變更數(shù)據(jù)
*/
@GetMapping(path = "publish/{id}")
@ResponseBody
public String publish(@PathVariable String id) {
// 數(shù)據(jù)變更 取出監(jiān)聽ID的所有長輪詢請(qǐng)求,并一一響應(yīng)處理
if (watchRequests.containsKey(id)) {
Collection
for (DeferredResult
deferredResult.setResult("我更新了" + new Date());
}
}
return "success";
}
當(dāng)請(qǐng)求超過設(shè)置的超時(shí)時(shí)間,會(huì)拋出AsyncRequestTimeoutException異常,這里直接用@ControllerAdvice全局捕獲統(tǒng)一返回即可,前端獲取約定好的狀態(tài)碼后再次發(fā)起長輪詢請(qǐng)求,如此往復(fù)調(diào)用。
@ControllerAdvice
public class AsyncRequestTimeoutHandler {
@ResponseStatus(HttpStatus.NOT_MODIFIED)
@ResponseBody
@ExceptionHandler(AsyncRequestTimeoutException.class)
public String asyncRequestTimeoutHandler(AsyncRequestTimeoutException e) {
System.out.println("異步請(qǐng)求超時(shí)");
return "304";
}
}
我們來測(cè)試一下,首先頁面發(fā)起長輪詢請(qǐng)求/polling/watch/10086監(jiān)聽消息更變,請(qǐng)求被掛起,不變更數(shù)據(jù)直至超時(shí),再次發(fā)起了長輪詢請(qǐng)求;緊接著手動(dòng)變更數(shù)據(jù)/polling/publish/10086,長輪詢得到響應(yīng),前端處理業(yè)務(wù)邏輯完成后再次發(fā)起請(qǐng)求,如此循環(huán)往復(fù)。
長輪詢相比于短輪詢?cè)谛阅苌咸嵘撕芏啵廊粫?huì)產(chǎn)生較多的請(qǐng)求,這是它的一點(diǎn)不完美的地方。
SSE
服務(wù)端向客戶端推送消息,除了可以用WebSocket外,還有一種服務(wù)器發(fā)送事件(Server-sent events)SSE。
SSE基于HTTP協(xié)議。SSE在服務(wù)器和客戶端之間打開一個(gè)單向通道,服務(wù)端響應(yīng)的不再是一次性的數(shù)據(jù)包而是text/event-stream類型的數(shù)據(jù)流信息,在有數(shù)據(jù)變更時(shí)從服務(wù)器流式傳輸?shù)娇蛻舳恕?/p>
整體的實(shí)現(xiàn)思路有點(diǎn)類似于在線視頻播放,視頻流會(huì)連續(xù)不斷的推送到瀏覽器,你也可以理解成,客戶端在完成一次用時(shí)很長(網(wǎng)絡(luò)不暢)的下載。
SSE與WebSocket的不同:
SSE 是基于HTTP協(xié)議的,它們不需要特殊的協(xié)議或服務(wù)器實(shí)現(xiàn)即可工作;WebSocket需單獨(dú)服務(wù)器來處理協(xié)議。SSE 單向通信,只能由服務(wù)端向客戶端單向通信;webSocket全雙工通信,即通信的雙方可以同時(shí)發(fā)送和接受信息。SSE 實(shí)現(xiàn)簡(jiǎn)單開發(fā)成本低,無需引入其他組件;WebSocket傳輸數(shù)據(jù)需做二次解析,開發(fā)門檻高一些。SSE 默認(rèn)支持?jǐn)嗑€重連;WebSocket則需要自己實(shí)現(xiàn)。SSE 只能傳送文本消息,二進(jìn)制數(shù)據(jù)需要經(jīng)過編碼后傳送;WebSocket默認(rèn)支持傳送二進(jìn)制數(shù)據(jù)。
SSE 與 WebSocket 該如何選擇?
SSE好像一直不被大家所熟知,一部分原因是出現(xiàn)了WebSockets,這個(gè)提供了更豐富的協(xié)議來執(zhí)行雙向、全雙工通信。對(duì)于游戲、即時(shí)通信以及需要雙向近乎實(shí)時(shí)更新的場(chǎng)景,擁有雙向通道更具吸引力。
但是,在某些情況下,不需要從客戶端發(fā)送數(shù)據(jù)。而你只需要一些服務(wù)器操作的更新。比如:站內(nèi)信、未讀消息數(shù)、狀態(tài)更新、股票行情、監(jiān)控?cái)?shù)量等場(chǎng)景,SEE不管是從實(shí)現(xiàn)的難易和成本上都更加有優(yōu)勢(shì)。此外,SSE 具有WebSockets在設(shè)計(jì)上缺乏的多種功能,例如:自動(dòng)重新連接、事件ID和發(fā)送任意事件的能力。
前端只需進(jìn)行一次HTTP請(qǐng)求,帶上唯一ID,打開事件流,監(jiān)聽服務(wù)端推送的事件就可以了
服務(wù)端的實(shí)現(xiàn)更簡(jiǎn)單,創(chuàng)建一個(gè)SseEmitter對(duì)象放入sseEmitterMap進(jìn)行管理
private static Map
/**
* 創(chuàng)建連接
*
*/
public static SseEmitter connect(String userId) {
try {
// 設(shè)置超時(shí)時(shí)間,0表示不過期。默認(rèn)30秒
SseEmitter sseEmitter = new SseEmitter(0L);
// 注冊(cè)回調(diào)
sseEmitter.onCompletion(completionCallBack(userId));
sseEmitter.onError(errorCallBack(userId));
sseEmitter.onTimeout(timeoutCallBack(userId));
sseEmitterMap.put(userId, sseEmitter);
count.getAndIncrement();
return sseEmitter;
} catch (Exception e) {
log.info("創(chuàng)建新的sse連接異常,當(dāng)前用戶:{}", userId);
}
return null;
}
/**
* 給指定用戶發(fā)送消息
*/
public static void sendMessage(String userId, String message) {
if (sseEmitterMap.containsKey(userId)) {
try {
sseEmitterMap.get(userId).send(message);
} catch (IOException e) {
log.error("用戶[{}]推送異常:{}", userId, e.getMessage());
removeUser(userId);
}
}
}
我們模擬服務(wù)端推送消息,看下客戶端收到了消息,和我們預(yù)期的效果一致。
注意: SSE不支持IE瀏覽器,對(duì)其他主流瀏覽器兼容性做的還不錯(cuò)。
MQTT
什么是 MQTT協(xié)議?
MQTT 全稱(Message Queue Telemetry Transport):一種基于發(fā)布/訂閱(publish/subscribe)模式的輕量級(jí)通訊協(xié)議,通過訂閱相應(yīng)的主題來獲取消息,是物聯(lián)網(wǎng)(Internet of Thing)中的一個(gè)標(biāo)準(zhǔn)傳輸協(xié)議。
該協(xié)議將消息的發(fā)布者(publisher)與訂閱者(subscriber)進(jìn)行分離,因此可以在不可靠的網(wǎng)絡(luò)環(huán)境中,為遠(yuǎn)程連接的設(shè)備提供可靠的消息服務(wù),使用方式與傳統(tǒng)的MQ有點(diǎn)類似。
TCP協(xié)議位于傳輸層,MQTT 協(xié)議位于應(yīng)用層,MQTT 協(xié)議構(gòu)建于TCP/IP協(xié)議上,也就是說只要支持TCP/IP協(xié)議棧的地方,都可以使用MQTT協(xié)議。
為什么要用 MQTT協(xié)議?
MQTT協(xié)議為什么在物聯(lián)網(wǎng)(IOT)中如此受偏愛?而不是其它協(xié)議,比如我們更為熟悉的 HTTP協(xié)議呢?
首先HTTP協(xié)議它是一種同步協(xié)議,客戶端請(qǐng)求后需要等待服務(wù)器的響應(yīng)。而在物聯(lián)網(wǎng)(IOT)環(huán)境中,設(shè)備會(huì)很受制于環(huán)境的影響,比如帶寬低、網(wǎng)絡(luò)延遲高、網(wǎng)絡(luò)通信不穩(wěn)定等,顯然異步消息協(xié)議更為適合IOT應(yīng)用程序。HTTP是單向的,如果要獲取消息客戶端必須發(fā)起連接,而在物聯(lián)網(wǎng)(IOT)應(yīng)用程序中,設(shè)備或傳感器往往都是客戶端,這意味著它們無法被動(dòng)地接收來自網(wǎng)絡(luò)的命令。通常需要將一條命令或者消息,發(fā)送到網(wǎng)絡(luò)上的所有設(shè)備上。HTTP要實(shí)現(xiàn)這樣的功能不但很困難,而且成本極高。
具體的MQTT協(xié)議介紹和實(shí)踐,這里我就不再贅述了,大家可以參考我之前的兩篇文章,里邊寫的也都很詳細(xì)了。
MQTT協(xié)議的介紹
我也沒想到 springboot + rabbitmq 做智能家居,會(huì)這么簡(jiǎn)單
MQTT實(shí)現(xiàn)消息推送
未讀消息(小紅點(diǎn)),前端 與 RabbitMQ 實(shí)時(shí)消息推送實(shí)踐,賊簡(jiǎn)單~
柚子快報(bào)激活碼778899分享:arm開發(fā) 應(yīng)用層網(wǎng)絡(luò)協(xié)議
文章鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。