HTML5中的webSocket、ajax、http

本文原連接:https://cloud.tencent.com/developer/article/1115496javascript

https://cloud.tencent.com/developer/article/1193011php

webSocket與ajax、web

先看一個有道釋義:html

其實釋義的挺形象的,下面我來一一解釋哈:前端

一、聊天室:webSocket有名的應用就是聊天室了;java

二、服務:webSocket提供客戶端請求的服務器和服務;node

三、套接字:源IP地址和目的IP地址以及源端口號和目的端口號的組合叫套接字,webSocket就是服務端和客戶端的結合;jquery

四、協議:webSocket是基於TCP的一種新的網絡協議。web

1、webSocket與ajax

做爲一個碼了還算久代碼的前端,提及webSocket,腦子裏最早閃現的固然就是ajax ajax ajax......ajax是啥,ajax剛出來時,可謂轟動一時,讓咱們愉快地告別那種提交一個表單必須得填完全部信息,而後再把數據轉給服務器驗證,結果發現有一個小小的輸入框裏輸錯了信息,而後又改掉從新提交走着重複的路的痛苦時代,因此它最大的貢獻就是局部刷新。固然,不是說有了webSocket,它就out了,ajax如今依舊好用。下面稍微比較了下ajax和webSocket:ajax

一、ajax

(1)瀏覽器主動發送消息給服務器;

(2)非實時數據交互(異步,局部刷新)。

原生寫法:express

四部曲:ajax對象、創建鏈接、發送請求、獲取相應

更通俗的用打電話來比喻,那就是:電話、撥號、說話、聽到對方迴應demo

//建立一個ajax對象(想打電話,首先得有電話這個對象)
var XHR = null; if (window.XMLHttpRequest) { // 非IE內核 XHR = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE內核,早期IE的版本寫法不一樣 XHR = new ActiveXObject("Microsoft.XMLHTTP"); } else { XHR = null; } 
if(XHR){ //創建鏈接(撥號) XHR.open("GET", "ajaxServer.action",true); //發送請求(說話) XHR.send(); //獲取響應(聽到對方迴應) XHR.onreadystatechange = function () { // readyState值說明 // 0,初始化,XHR對象已經建立,還未執行open // 1,載入,已經調用open方法,可是還沒發送請求 // 2,載入完成,請求已經發送完成 // 3,交互,能夠接收到部分數據 
// 4,交互完畢 // status值說明 // 200:成功 // 404:沒有發現文件、查詢或URl // 500:服務器產生內部錯誤 if (XHR.readyState == 4 && XHR.status == 200) { // 這裏能夠對返回的內容作處理 // 通常會返回JSON或XML數據格式 console.log(XHR.responseText); // 主動釋放,JS自己也會回收的 XHR = null; } }; }

JQuery寫法(so easy,媽媽不再用擔憂個人學習啦):

$.ajax({ type:"post", url:url, async:true, data:params, dataType:"json", success:function(res){ console.log(res); }, error:function(jqXHQ){ alert("發生錯誤:"+jqXHQ.status); } });

 

二、webSocket

(1)實現了瀏覽器與服務器全雙工(full-duplex)通訊——容許服務器主動發送信息給客戶端;

(2)實時數據交互。

WebSocket 握手過程
WebSocket 協議本質上是一個基於 TCP 的協議,WebSocket

鏈接與 TCP 鏈接的創建過程相似[4]。但與傳統的基於 TCP 連 接的協議有所不一樣的是 WebSocket 協議須要從 HTTP 協議「過 渡」而來,而這個「過分」過程也被稱爲 WebSocket 協議的握手 過程。所以想要創建基於 WebSocket 的 Web 應用必須首先了解 WebSocket 協議的握手機制,如圖 2 給出了 WebSocket 協議握手 過程的示意圖。

首先由瀏覽器客戶端向服務器發起 WebSocket 握手請求報 文,這個報文是基於 HTTP 協議的,它告訴服務器客戶端想要升 級當前的 HTTP 協議爲 WebSocket 協議。服務器收到客戶端的 WebSocket 握手請求報文以後會對報頭進行解析,若是服務器 理解客戶端握手請求報頭而且知足升級爲 WebSocket 協議的條 件,便會向客戶端發送握手應答報文,這個應答報文一樣是基於 HTTP 協議的。客戶端收到服務器的應答報文後會對該報文進 行一次驗證,驗證成功以後便會成功升級爲 WebSocket 協議,如 果驗證失敗客戶端將會主動斷開鏈接。創建了 WebSocket 鏈接 以後,雙方即可以進行全雙工通訊, 

WebSocket 握手過程與 TCP 握手過程相似,可是 WebSocket 協議握手採用了更加簡潔的方式。

相對於傳統的 TCP 握手,WebSocket 握手協議有如下明顯 的特色: 首先,WebSocket 整個握手過程只須要兩次握手,相對 於 TCP 的三次握手,WebSocket 簡化而且加入了本身的規則。 其次,WebSocket 握手協議是基於 HTTP 協議的,相對於字節流 的解析,ASCII 序列解析起來更加簡便。最後,WebSocket 握手 協議引入了隨機序列認證機制,易於實現。

兩次握手成功預示着雙方接下來將升級當前的 HTTP 協議 爲 WebSocket 協議。WebSocket 握手請求報文的報頭部分除了 必須包含必要的 HTTP 字段[5],還須遵循通用消息格式[RFC 822],同時又要包含和 WebSocket 緊密聯繫的字段[6],如 Web- Socket 協議的版本信息、客戶端隨即生成的 Key、必要的 GET 請 求方法等。 

 

// Create WebSocket connection.
var socket = new WebSocket('ws://localhost:8080'); //建立一個webSocket實例 // Connection opened socket.addEventListener('open', function (event) { //一旦服務端響應WebSocket鏈接請求,就會觸發open事件 socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { //當消息被接受會觸發消息事件 console.log('Message from server', event.data); });

 

2、webSocket API

既然上面寫了一部分代碼,那不如把API全都貼出來,哈哈哈。

首先,建立一個webSocket實例:

var socket = new WebSocket('ws://localhost:8080'); 

而後再看下面的的API。

一、事件

(1)open

一個用於鏈接打開事件的事件監聽器。當readyState的值變爲 OPEN 的時候會觸發該事件。該事件代表這個鏈接已經準備好接受和發送數據。這個監聽器會接受一個名爲"open"的事件對象。

socket.onopen = function(e) { console.log("Connection open..."); };

或者:

socket.addEventListener('open', function (event) { console.log("Connection open..."); });

(2)message

一個用於消息事件的事件監聽器,這一事件當有消息到達的時候該事件會觸發。這個Listener會被傳入一個名爲"message"的 MessageEvent 對象。

socket.onmessage = function(e) { console.log("message received", e, e.data); };

(3)error

當錯誤發生時用於監聽error事件的事件監聽器。會接受一個名爲「error」的event對象。

socket.onerror = function(e) { console.log("WebSocket Error: " , e); };

(4)close

用於監聽鏈接關閉事件監聽器。當 WebSocket 對象的readyState 狀態變爲 CLOSED 時會觸發該事件。這個監聽器會接收一個叫close的 CloseEvent 對象。

socket.onclose = function(e) { console.log("Connection closed", e); };

二、方法

(1)send

經過WebSocket鏈接向服務器發送數據。

一旦在服務端和客戶端創建了全雙工的雙向鏈接,可使用send方法去發送消息,當鏈接是open的時候send()方法傳送數據,當鏈接關閉或獲取不到的時候回拋出異常。

一個一般的錯誤是人們喜歡在鏈接open以前發送消息。以下所示:

// 這將不會工做
var socket= new WebSocket("ws://localhost:8080") socket.send("Initial data");

應該等待open事件觸發後再發送消息,正確的姿式以下:

var socket= new WebSocket("ws://localhost:8080") socket.onopen = function(e) { socket.send("Initial data"); }

(2)close

關閉WebSocket鏈接或中止正在進行的鏈接請求。若是鏈接的狀態已是closed,這個方法不會有任何效果。

使用close方法來關閉鏈接,若是鏈接已經關閉,這方法將什麼也不作。調用close方法後,將不能再發送數據。close方法能夠傳入兩個可選的參數,code(numerical)和reason(string),以告訴服務端爲何終止鏈接。

socket.close(1000, "Closing normally"); //1000是狀態碼,表明正常結束。

 

三、屬性

屬性名

類型

描述

binaryType

DOMString

一個字符串表示被傳輸二進制的內容的類型。取值應當是"blob"或者"arraybuffer"。 "blob"表示使用DOMBlob 對象,而"arraybuffer"表示使用 ArrayBuffer 對象。

bufferedAmount

unsigned long

調用 send() 方法將多字節數據加入到隊列中等待傳輸,可是還未發出。該值會在全部隊列數據被髮送後重置爲 0。而當鏈接關閉時不會設爲0。若是持續調用send(),這個值會持續增加。只讀。

extensions

DOMString

服務器選定的擴展。目前這個屬性只是一個空字符串,或者是一個包含全部擴展的列表。

protocol

DOMString

一個代表服務器選定的子協議名字的字符串。這個屬性的取值會被取值爲構造器傳入的protocols參數。

readyState

unsigned short

鏈接的當前狀態。取值是 Ready state constants之一。只讀。

url

DOMString

傳入構造器的URL。它必須是一個絕對地址的URL。只讀。

四、常量

Ready state 常量

常量

描述

CONNECTING

0

鏈接還沒開啓。

OPEN

1

鏈接已開啓並準備好進行通訊。

CLOSING

2

鏈接正在關閉的過程當中。

CLOSED

3

鏈接已經關閉,或者鏈接沒法創建。

3、webSocket與HTTP

webSocket和http同爲協議,你們內心確定會想它倆之間有什麼聯繫,固然,我也好奇,因此就有了下面的研究結果,呵呵呵呵~~

你們都知道,webSocket是H5的一種新協議(這樣看來和http是沒什麼關係),本質是經過http/https協議進行握手後建立一個用於交換數據的TCP鏈接,服務端與客戶端經過此TCP鏈接進行實時通訊。也就是說,webSocket是http協議上的一種補充。

相對於HTTP這種非持久的協議來講,Websocket是一個持久化的協議。

以php的生命週期爲例:

在http1.0中,一個request,一個response,一個週期就結束了。

在http1.1中,有了keep-alive,能夠發送多個Request,接收多個Response。但在http中永遠是一個request對應一個response。並且這個response是被動的,不能主動發起。

這時候webSocket就派上用場了。

4、webSocket原理

首先,先來看一張http的Request Headers:

再看一張webSocket的:

以及webSocket的Response Headers:

I guess,不管熟不熟悉http,想必都看出了區別,哈哈哈。接下來就要對這些東西進行講解啦:

(1)Upgrade和Connection

Upgrade: websocket
Connection: Upgrade

這個就是webSocket的核心,告訴Apache、ngix等服務器:注意啦,我發起的是webSocket協議,快點幫我找到對應的助理處理~ 不是那個老土的http。

(2)Sec-WebSocket-Key、Sec-WebSocket-Extensions和Sec-WebSocket-Version

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Extensions: chat, superchat Sec-WebSocket-Version: 13

這個很好理解啦,首先,Sec-WebSocket-Key是一個Base64 encode的值,這個是瀏覽器隨機生成的,告訴服務器:尼好,我是webSocket,這是個人ID卡,讓我過去吧。

而後,Sec-WebSocket-Extensions:協議擴展, 某類協議可能支持多個擴展,經過它能夠實現協議加強

最後,Sec-WebSocket-Version是告訴服務器所使用的webSocket Draft(協議版本)。喏,我是小喵4.1版本哆啦A夢,哈哈哈哈哈哈哈哈。

而後只要服務器返回了上面我放的那一系列balabala的東西,就表明已經接受請求,webSocket創建成功啦!

(3)Sec-WebSocket-Accept和Sec-WebSocket-Extensions

請求時,webSocket會自帶加密過的ID卡過來讓服務端驗證;

對應的,接受請求以後,服務端也得搞一個安全卡(Accept頭域的值就是Key的值,是由瀏覽器發過來的Sec-WebSocket-Key生成的)來證實是我贊成你經過的,而不是什麼肯蒙拐騙的壞銀->

就這樣,原理部分就說完啦,握手成功!

5、webSocket的做用

說webSocket以前,先說一下ajax輪詢和long poll。

一、ajax輪詢:

ajax輪詢很簡單,就是讓瀏覽器隔個幾秒就發送一次請求,詢問服務器是否有新信息。

客戶端:hello hello,有沒有新信息(Request) 服務端:沒有(Response) 客戶端:hello hello,有沒有新信息(Request) 服務端:沒有。。(Response) 客戶端:hello hello,有沒有新信息(Request) 服務端:你好煩啊,沒有啊。。(Response) 客戶端:hello hello,有沒有新消息(Request) 服務端:有啦有啦,here you are(Response) 客戶端:hello hello,有沒有新消息(Request) 服務端:。。沒。。。。沒。。。沒。。。。(Response)

 

二、long poll

long poll和ajax輪詢原理很像,不過long poll是阻塞模型,簡單來講,就是一直給你打電話,直到你接聽爲止。

客戶端:hello hello,有沒有新信息,沒有的話就等有了再返回給我吧(Request)

服務端:額。。。     (。。。。等待到有消息的時候。。。。)     有了,給你(Response)

很明顯,ajax輪詢和long poll弊大於利:

(1)被動性

上面這兩種方式都是客戶端先主動消息給服務端,而後等待服務端應答,要知道,等待老是難熬的,若是服務端能主動發消息多好,這也就是缺點之一:被動性。

(2)很是消耗資源

ajax輪詢 須要服務器有很快的處理速度和資源(速度);

long poll 須要有很高的併發,也就是說同時接待客戶的能力(場地大小)。

so,當ajax輪詢和long poll碰上503(啊啊啊啊啊,game over)

這時候,神奇的webSocket又派上用場了。

 

三、webSocket

(1)被動性

首先,解決被動性:

客戶端:hello hello,我要創建webSocket協議,擴展服務:chat,Websocket,協議版本:17(HTTP Request)

服務端:ok,確認,已升級爲webSocket協議(HTTP Protocols Switched)

客戶端:麻煩你有信息的時候推送給我噢。。

服務端:ok,有的時候會告訴你的。

服務端:balabalabalabala

服務端:balabalabalabala

服務端:哈哈哈哈哈啊哈哈哈哈

服務端:笑死我了哈哈哈哈哈哈哈

就這樣,只須要一次http請求,就會有源源不斷的信息傳送了,是否是很方便。

(2)消耗資源問題

首先,瞭解一下,咱們所用的程序是要通過兩層代理的,即http協議在Nginx等服務器的解析下,而後再傳送給相應的Handler(PHP等)來處理。簡單地說,咱們有一個很是快速的接線員(Nginx),他負責把問題轉交給相應的客服(Handler) 。

自己接線員基本上速度是足夠的,可是每次都卡在客服(Handler)了,老有客服處理速度太慢,致使客服不夠。

webSocket就解決了這樣一個難題,創建後,能夠直接跟接線員創建持久鏈接,有信息的時候客服想辦法通知接線員,而後接線員再統一轉交給客戶。

這樣就能夠解決客服處理速度過慢的問題了。

同時,在傳統的方式上,要不斷的創建,關閉HTTP協議,因爲HTTP是非狀態性的,每次都要從新傳輸鑑別信息,來告訴服務端你是誰。

雖然接線員很快速,可是每次都要聽這麼一堆,效率也會有所降低的,同時還得不斷把這些信息轉交給客服,不但浪費客服的處理時間,並且還會在網路傳輸中消耗過多的流量/時間。

可是webSocket只須要一次http握手,因此說整個通信過程是創建在一次鏈接/狀態中,也就避免了http的非狀態性,服務端會一直知道你的信息,直到你關閉請求,這樣就解決了接線員要反覆解析http協議,還要查看identity info的信息。

6、Socket.io

既然說到了webSocket,就不免扯到socket.io。

有人說socket.io就是對webSocket的封裝,而且實現了webSocket的服務端代碼。能夠這樣說,但不徹底正確。

在webSocket沒有出現以前,實現與服務端的實時通信能夠經過輪詢來完成任務。Socket.io將webSocket和輪詢(Polling)機制以及其它的實時通訊方式封裝成了通用的接口,而且在服務端實現了這些實時機制的相應代碼。也就是說,webSocket僅僅是Socket.io實現實時通訊的一個子集。

下面直接上一個用socket.io作的小小聊天室吧。

(1)首先你得有node,而後安裝socket.io。

$ npm install socket.io

(2)服務器端(index.js)

'use strict'; module.exports = require('./lib/express'); var app = require('express')(); var http = require('http').Server(app); var io = require('socket.io')(http); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); }); io.on('connection', function(socket){ socket.on('message',function(msg){ console.log(msg); socket.broadcast.emit('chat',msg); //廣播消息 }) }); http.listen(3000);

(3)客戶端

先引入js文件:

<script src="/socket.io/socket.io.js"></script>

交互代碼(index.html):

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>聊天室</title> <style> body,div,ul,li{margin: 0;padding: 0;list-style: none;} .auto{margin: auto;} .l{text-align: left;} .r{text-align: right;} .flex{display: box;display: -webkit-box;display: -moz-box;display: -ms-flexbox;display: -webkit-flex;display: flex;-webkit-box-pack: center;-webkit-justify-content: center;-moz-justify-content: center;-ms-justify-content: center;-o-justify-content: center;justify-content: center;-webkit-box-align: center;-webkit-align-items: center;-moz-align-items: center;-ms-align-items: center;-o-align-items: center;align-items: center;} .chat-box{background: #f1f1f1;width: 56vw;padding:2vw;height:36vw;border:1px solid #ccc;margin-top: 2vw;} .chat-li{display:inline-block;margin-top: 5px;background: #5CB85C;border-radius: 5px;padding: 3px 10px;color: #fff;} .other-chat-li{background: #fff;color: #333;} .send-box{width: 60vw;border:1px solid #ccc;justify-content: space-between;border-top: 0;} .send-text{width: 50vw; border: none; padding: 10px;outline:0;} .send{width: 10vw;background: #5cb85c; border: none; padding: 10px;color: #fff;cursor: pointer;} .chat-name{color: #f00;} .other-box,.self-box{width: 50%;height:100%;} </style> </head> <body> <div class="chat-box auto flex"> <ul class="other-box l"></ul> <ul class="self-box r"></ul> </div> <div class="flex send-box auto"> <input class="send-text" type="text"> <button class="send" type="button">發送</button> </div> </body> <script src="/socket.io/socket.io.js"></script> <script src="https://code.jquery.com/jquery-1.11.1.js"></script> <script> $(function(){ var socket = io(); $(".send").click(function(){ var msg = $(".send-text").val(); if(msg != ""){ socket.send(msg); $('.self-box').append('<li class="chat-li">'+ msg +'<li>'); $(".send-text").val(""); }else{ return false; } }) $(".send-text").keydown(function(event){ if(event.keyCode == 13){ var msg = $(".send-text").val(); if(msg != ""){ socket.send(msg); $('.self-box').append('<li class="chat-li">'+ msg +'<li>'); $(".send-text").val(""); }else{ return false; } } }) socket.on("chat",function(msg){ $('.other-box').append('<li class="other-chat-li chat-li">'+ msg +'<li>'); }) }) </script> </html>

(4)運行代碼:

$ node index.js

而後打開兩個瀏覽器頁面(http://localhost:3000/),就能夠聊天啦,至於聊天名稱呀、聊天頭像呀什麼的,能夠本身去研究羅~~~

下面是效果圖:

到底爲止啦,感受好像裹腳布,so long~~~~~~~

相關文章
相關標籤/搜索