SSE && WebSockets

SSE && WebSockets

參考
http://www.bitscn.com/school/HTMLCSS/201402/194940.htmljavascript

WebSockets 定義了一個全雙工的通訊信道,只需Web上的一個 Socket便可進行通訊,能減小沒必要要的網絡流量並下降網絡延遲。php

大部分是圍繞輪詢和其餘服務器端推送技術展開的,其中最著名的是Comet。Comet技術可讓服務器主動以異步方式向客戶端推送數據。html

Comet

Comet是服務器端的推送,實現Comet有兩種方式, 長輪詢和流。html5

  • 長輪詢
    長輪詢是短輪詢的一個翻版, 短輪詢是瀏覽器定時向服務端發送請求看看有沒有數據更新。長輪詢則是瀏覽器發送了一個請求以後, 服務端一直保持鏈接打開狀態,直到有數據能夠相應,發送完數據後關閉鏈接。以後瀏覽器再發起請求。java

  • Http流
    流不一樣於輪詢 流在頁面整個生命週期只內只有一個鏈接, 瀏覽器向服務器發送請求, 而服務器保持鏈接打開, 而後週期性的向瀏覽器發送數據。node

SSE 服務器發送事件

參考
http://www.cnblogs.com/wintersun/p/3735160.html
http://www.cnblogs.com/goody9807/p/4257192.html
http://www.w3school.com.cn/html5/html_5_serversentevents.aspweb

簡單說,所謂SSE,就是瀏覽器向服務器發送一個HTTP請求,而後服務器不斷單向地向瀏覽器推送「信息」(message)。這種信息在格式上很簡單,就是「信息」加上前綴「data: 」,而後以「\n\n」結尾。
(對於多行數據 只有最後一行是\n\n 其餘是\n)
好比這樣跨域

$ curl http://example.com/dates
data: 1394572346452
data: 1394572347457
data: 1394572348463

SSE對象有三個事件:open、error和message瀏覽器

SSE是單向通道, 只能服務端向瀏覽器發送數據
使用SSE, 瀏覽器不用頻繁的發送請求, 實際上在Network中就看不到請求卻能夠獲得服務端的數據安全

var source = new EventSource('sse.php');

source.onmessage = function(e){
  console.log(e.data);
};

服務端

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
while(true){
  echo "data: start\n";
  echo "data:".date("Y-m-d H:i:s")."\n\n";
  // @ob_flush();
  // @flush();
  ob_flush();
  flush();
  sleep(1);
}
?>

PS SSE有跨域限制

接下來用NodeJS 來實現SSE
以前提到了SSE不能跨域, 所以咱們的這個NodeJS服務器不單單要完成SSE的處理, 還要有完成頁面的請求

http://www.cnblogs.com/wintersun/p/3735160.html

var http = require("http"), fs = require("fs");
var port = parseInt( process.argv[2] || 1234 );
http.createServer(function(request, response){
  console.log("Client connected:" + request.url);
  if(request.url!="/sse"){
    fs.readFile("sseNode.html", function(err,file){
      response.writeHead(200, { 'Content-Type': 'text/html' });
      var s = file.toString();  //file is a buffer
      s = s.replace("basic_sse.php","sse");
      response.end(s);
      });
    return;
    }
  //Below is to handle SSE request. It never returns.
  response.writeHead(200, { "Content-Type": "text/event-stream" });
  var timer = setInterval(function(){
    var content = "data:" + new Date().toISOString() + "\n\n";
    var b = response.write(content);
    if(!b)console.log("Data got queued in memory (content=" + content + ")");
    else console.log("Flushed! (content=" + content + ")");
    },1000);
  request.connection.on("close", function(){
    response.end();
    clearInterval(timer);
    console.log("Client closed connection. Aborting.");
    });
  }).listen(port);
console.log("Server running at http://localhost:" + port);

WebScockets

爲了創建WebSocket通訊,客戶端和服務器在初始握手時,將HTTP協議升級到WebSocket協議。一旦鏈接創建成功,就能夠在全雙工模式下在客戶端和服務器之間來回傳送WebSocket消息。

ws://和wss://前綴分別表示WebSocket鏈接和安全的WebSocket鏈接。

事件 處理程序 說明
open Socket.onopen z此事件發生在套接字創建鏈接。
message Socket.onmessage 此事件發生時,客戶端收到來自服務器的數據。
error Socket.onerror 此事件發生時有任何通訊錯誤。
close Socket.onclose 此事件發生在鏈接關閉。

須要知道的是socket都是創建在一個HTTP服務的基礎上的,所以建立一個socket以前須要建立一個HTTP

一個簡單的例子
服務端部分(NodeJS)

來自http://my.oschina.net/fuckboogie/blog/201615

var http = require('http');
 var io = require('socket.io');
 var yourserver = http.createServer(function (request, response) {
         response.writeHead(250, { 'Content-Type': 'text/html' });
         response.end('Your WebSocket server is running');
     }).listen(1234);
var yoursocket = io.listen(yourserver);
yoursocket.on('connection', function (client) {
    client.on('message', function (data) {
        console.log('Client Message: ', data);
        var current = new Date().getTime();
        client.emit('YourMessageResponse', data + '->' + current);
    });
    client.on('disconnect', function () {
        console.log('Your Client disconnected');
    });
});

客戶端部分
客戶端須要引入socket.io.js 這個文件纔有io對象, 這個文件在node_modules的socket.io中 以前咱們啓動了一個端口是1234的服務器 服務器會自動尋找這個文件
<script src='http://localhost:1234/socket.io/socket.io.js'></script>

var yoursocket = null;
    yoursocket = io.connect('http://localhost:1234');
    yoursocket.on('connect', function() {
        console.log('You are connected to Server');
        yoursocket.send(new Date());
    });
    yoursocket.on('YourMessageResponse', function(data) {
        console.log('Server Response:  ' + data + '<br />');
        setTimeout(function(){
            yoursocket.send(data + new Date());
        },1000);
    });
    yoursocket.on('disconnect', function() {
        console.log('You are disconnected from Server<br />');
    });

不用socket.io 就用W3C的socket

參考
http://codular.com/node-web-sockets
服務端

var WebSocketServer = require('websocket').server;
var http = require('http');

var server = http.createServer(function(request, response) {
    console.log((new Date()) + ' Received request for ' + request.url);
    response.writeHead(404);
    response.end();
});
server.listen(8080, function() {
    console.log((new Date()) + ' Server is listening on port 8080');
});

wsServer = new WebSocketServer({
    httpServer: server,
    // You should not use autoAcceptConnections for production
    // applications, as it defeats all standard cross-origin protection
    // facilities built into the protocol and the browser.  You should
    // *always* verify the connection's origin and decide whether or not
    // to accept it.
    autoAcceptConnections: false
});

function originIsAllowed(origin) {
  // put logic here to detect whether the specified origin is allowed.
  return true;
}

wsServer.on('request', function(request) {
    if (!originIsAllowed(request.origin)) {
      // Make sure we only accept requests from an allowed origin
      request.reject();
      console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
      return;
    }
    var connection = request.accept('echo-protocol', request.origin);
    console.log((new Date()) + ' Connection accepted.');
    connection.sendUTF('first message from server');
    connection.on('message', function(message) {
        if (message.type === 'utf8') {
            console.log('Received Message: ' + message.utf8Data);
            connection.sendUTF(message.utf8Data);
        }
        else if (message.type === 'binary') {
            console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
            connection.sendBytes(message.binaryData);
        }
    });
    connection.on('close', function(reasonCode, description) {
        console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
    });
});

客戶端(原生的websocket)

var soc = null;
    var websocket =  new WebSocket('ws://localhost:8080','echo-protocol');
    websocket.onopen = function(e){
        console.log(e);
        // setInterval(function(){
        //     websocket.send('hehe')
        // }, 1000);
    }
    websocket.onmessage =  function(e){
        console.log(e);
        setTimeout(function(){
            websocket.send('client');
        },1000)
    }
相關文章
相關標籤/搜索