HTTP長連線和短連線 + Websocket
HTTP協議與TCP/IP協議的關係
HTTP的長連線和短連線本質上是TCP長連線和短連線。HTTP屬於應用層協議,在傳輸層使用TCP協議,在網路層使用IP協議。IP協議主要解決網路路由和定址問題,TCP協議主要解決如何在IP層之上可靠的傳遞資料包,使在網路上的另一端收到發端發出的所有包,並且順序與發出順序一致。TCP有可靠,面向連線的特點。
長連線短連線操作過程
短連線的操作步驟是:
建立連線——資料傳輸——關閉連線…建立連線——資料傳輸——關閉連線
長連線的操作步驟是:
建立連線——資料傳輸 …(保持連線)… 資料傳輸——關閉連線
長連線和短連線的優點和缺點
長連線可以省去較多的TCP建立和關閉的操作,減少浪費,節約時間。但是如果連線數比較多,會給伺服器造成比較大壓力。
短連線對於伺服器來說管理較為簡單,存在的連線都是有用的連線,不需要額外的控制手段。但如果客戶請求頻繁,將在TCP的建立和關閉操作上浪費時間和頻寬。
什麼時候用長連線,短連線
長連線多用於操作頻繁,點對點的通訊,而且連線數不能太多情況。例如:資料庫的連線用長連線, 如果用短連線頻繁的通訊會造成socket錯誤,而且頻繁的socket 建立也是對資源的浪費。
而像WEB網站的http服務一般都用短連結,因為長連線對於服務端來說會耗費一定的資源,而像WEB網站這麼頻繁的成千上萬甚至上億客戶端的連線用短連線會更省一些資源,如果用長連線,而且同時有成千上萬的使用者,如果每個使用者都佔用一個連線的話,那可想而知吧。所以併發量大,但每個使用者無需頻繁操作情況下需用短連好。
推送服務
推送技術,又名反向AJAX,指的是一種基於Internet,將由中心或釋出者發出訊息傳輸給使用者的技術。與之相對的是拉取(參見AJAX),這種情況下請求是由使用者或客戶端主動發起的。
所有的推送功能都是基於長連線的基礎上的。
維護任何一個長連線都需要心跳機制,客戶端傳送一個心跳給伺服器,伺服器給客戶端一個心跳應答,這樣就形成客戶端伺服器的一次完整的握手,這個握手是讓雙方都知道他們之間的連線是沒有斷開,客戶端是線上的。如果超過一個時間的閾值,客戶端沒有收到伺服器的應答,或者伺服器沒有收到客戶端的心跳,那麼對客戶端來說則斷開與伺服器的連線重新建立一個連線,對伺服器來說只要斷開這個連線即可。
在智慧手機上的長連線心跳和在Internet上的長連線心跳有什麼不同的目的呢?原因就在於智慧手機使用的是移動無線網路,那麼我們在講長連線之前我們首先要了解無線行動網路的特點。
簡單來說就是由於網路運營商為了節省通道資源,會在一臺移動終端一段時間沒有通訊時關閉其關的鏈路。為了應對這種情況,移動應用不得不以遠高於正常頻率來發送心跳用以維護推送的長連線。
Android系統的推送和iOS的推送
ios長連線是由系統來維護的,iOS上的所有應用上的推送都是先將訊息推送到蘋果的伺服器然後將蘋果伺服器通過這個系統級別的長連結推送到手機終端上,這樣做的好處是:
- 在手機終端始終只要維護一個長連線即可,而且由於這個長連結是系統級別的不會出現被殺死而無法推送的情況。
- 省電,不會出現每個應用都各自維護一個自己的長連線。
- 安全,只有在蘋果註冊的開發者才能夠進行推送,等等。
Android的長連線是由每個應用各自維護的,而谷歌退出的和蘋果類似的推送框架在國內無法使用的。
推送的常見方式
- 輪詢(polling)
- 持久連結(binding)
- SMS方式
WebSocket
WebSocket API是下一代客戶端-伺服器的非同步通訊方法。該通訊取代了單個的TCP套接字,使用ws或wss協議,可用於任意的客戶端和伺服器程式。
WebSocket API最偉大之處在於伺服器和客戶端可以在給定的時間範圍內的任意時刻,相互推送資訊。WebSocket並不限於以Ajax(或XHR)方式通訊,因為Ajax技術需要客戶端發起請求,而WebSocket伺服器和客戶端可以彼此相互推送資訊;XHR受到域的限制,而WebSocket允許跨域通訊。
WebSocket的客戶端API
// 建立一個Socket例項,ws表示WebSocket協議
var socket = new WebSocket('ws://localhost:8080');
// 開啟socket
socket.onopen = function(event){
// 傳送一個初始化訊息
socket.send('hello socket')
// 監聽訊息
socket.onmessage = function(event){
console.log('client reveived a meessage', event)
}
// 監聽Socket的關閉
socket.oncolse = function(event){
console.log('Client notified socket has closed', event)
}
// 關閉Socket
socket.closd()
}
一個模擬聊天室websocket的例子:
在實際應用中,socket伺服器端的程式碼可以是Python,node.js,java,php。在這裡使用http://www.websocket.org/網站提供的,socket
服務端。協議地址為:ws://echo.websocket.org/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Websocket</title>
<style>
* {
margin: 0;
padding: 0;
}
#outer {
box-sizing: border-box;
width: 60%;
height: 400px;
padding: 20px 200px;
background: black;
font-size: 20px;
color: #F8F8F8;
overflow-y: auto;
}
#message {
width: 300px;
height: 20px;
margin-top: 10px;
margin-left: 100px;
margin-right: 50px;
}
p {
margin: 10px 0;
}
button {
background: none;
outline: none;
border: 1px solid #0b9ce1;
padding: 5px 15px;
margin: 0 8px;
border-radius: 5px;
}
</style>
</head>
<body>
<div id="outer">
<p>123</p>
<p>123</p>
</div>
<label><input id="message"></label>
<button id="send">傳送</button>
<button id="quite">斷開</button>
</body>
<script src="../lib/jquery.min.js"></script>
<script>
function log(msg) {
$('#outer').append('<p>' + msg + '</p>').scrollTop(500)
}
function send() {
var text = $('#message').val();
if (!text) {
alert('Message can not be empty!');
return;
}
try {
socket.send(text);
log('Send : ' + text);
$('#message').val('').focus()
}
catch (e) {
log(e)
}
}
function quit() {
log("Bye!");
socket.close();
socket = null;
}
var socket = null;
function init() {
//宣告host注意:是ws協議
var host = "ws://echo.websocket.org/";
try {
//新建立一個socket物件
socket = new WebSocket(host);
//將連線的狀態資訊顯示在log
log('WebSocket - status ' + socket.readyState);
//監聽開啟連線
socket.onopen = function (msg) {
log("Welcome - status " + this.readyState);
};
//監聽當接收資訊時觸發匿名函式
socket.onmessage = function (msg) {
log("Received : " + msg.data);
};
//關閉連線
socket.onclose = function (msg) {
log("Disconnected - status " + this.readyState);
};
}
catch (e) {
log(ex)
}
$('#message').focus();
}
$(function () {
init();
$("#message").keypress(function (e) {
if (event.keyCode === 13) {
send();
}
});
$('#send').click(function () {
send();
});
$('#quite').click(function () {
quite();
});
$("#outer").scroll(function(){
$(this)
})
})
</script>
</html>
更多關於socket io等的例子和方案看這裡。