国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

學習 NodeJS 第八天:Socket 通訊實例

2019-11-19 18:21:09
字體:
來源:轉載
供稿:網友

前言

一般來講,HTTP 是基于文本的“單向”通訊機制。這里所謂的“單向”,乃相對于“雙向”而言,因為 HTTP 服務器只需根據請求返還恰當的 HTML 給客戶端即可,不涉及客戶端向服務端的通訊。這種單向的機制比較簡單,對網絡質量要求也不高。而更多的場景則是需要可靠、穩定的端到端連接。一般這種服務是實時的、有態的而且是長連接,長連接則暗示兩段須達致相向通訊的能力,也就說是服務端客戶端兩者間能夠實時地相互間通信。毫無疑問,能夠實時通信的服務器正是我們對服務器基本要求之一。區別于 HTTP 服務器以 HTTP 為通訊協議, 實時服務器一般采用較為底層的 TCP/IP 為協議通訊,實現了“套字節 Socket”的雙向機制。

Socket 是根據博克萊 (U.C.Berkley) 大學早期發展的 Socket 概念寫成的,其設計理念是是將網絡傳輸類比成文件的讀取與寫入 (傳送的動作被視為是寫入/接收的動作被視為是讀取),如此、傳送與接收就簡化為編程人員比較容易懂的 讀取與寫入,降低了網絡編程的學習困難度。

聊天室服務器

聊天室的實時連接基于底層的 TCP 直接連接,為此我們須調用 Node 的 TCP 模塊。如果不太熟悉所謂 TCP 網絡編程?太底層了是不是?沒關系,我也不熟悉,邊學邊做嘛,只不過千萬不必因為遇到陌生的詞匯而害怕,其實這樣原理并不深奧,而且下面的例子也十分的簡單易懂!咱們就從最簡單的開始吧,下面代碼僅僅十行,它的作用是服務器向客戶端輸出一段文本,完成 Sever --> Client 的單向通訊。

// Sever --> Client 的單向通訊 var net = require('net');  var chatServer = net.createServer();  chatServer.on('connection', function(client) {  client.write('Hi!/n'); // 服務端向客戶端輸出信息,使用 write() 方法  client.write('Bye!/n');   client.end(); // 服務端結束該次會話 });  chatServer.listen(9000); 

客戶端可以是系統自帶的 Telnet:

telnet 127.0.0.1 9000 

執行 telnet 后,與服務點連接,反饋 Hi! Bye! 的字符,并立刻結束服務端程序終止連接。如果我們要服務端接到到客戶端的信息?可以監聽 server.data 事件并且不要中止連接(否則會立刻結束無法接受來自客戶端的消息):

// 在前者的基礎上,實現 Client --> Sever 的通訊,如此一來便是雙向通訊 var net = require('net'); var chatServer = net.createServer(),   clientList = [];   chatServer.on('connection', function(client) {  // JS 可以為對象自由添加屬性。這里我們添加一個 name 的自定義屬性,用于表示哪個客戶端(客戶端的地址+端口為依據)  client.name = client.remoteAddress + ':' + client.remotePort;  client.write('Hi ' + client.name + '!/n');  clientList.push(client);  client.on('data', function(data) {    broadcast(data, client);// 接受來自客戶端的信息  }); }); function broadcast(message, client) {  for(var i=0;i<clientList.length;i+=1) {    if(client !== clientList[i]) {    clientList[i].write(client.name + " says " + message);    }  } } chatServer.listen(9000); 

這里要說明一下的是,不不同操作系統對端口范圍的限制不一樣,有可能是隨機的。

那么上面是不是一個完整功能的代碼呢?我們說還有一個問題沒有考慮進去:那就是一旦某個客戶端退出,卻仍保留在 clientList 里面,這明顯是一個空指針(NullPoint)。如果是在這樣的話我們寫程序太脆弱了,能不能更健壯一些?――請接著看。

首先我們簡單地把 client 從數組 clientList 中移除掉。完成這工作一點都不困難。Node TCP API 已經為我們提供了 end 事件,即客戶端中止與服務端連接的時候發生。移除 client 對象的代碼如下:

chatServer.on('connection', function(client) {  client.name = client.remoteAddress + ':' + client.remotePort  client.write('Hi ' + client.name + '!/n');   clientList.push(client)   client.on('data', function(data) {  broadcast(data, client)  })   client.on('end', function() {  clientList.splice(clientList.indexOf(client), 1); // 刪除數組中的制定元素。這是 JS 基本功哦~  }) }) 

但是我們還不敢說上述代碼很健壯,因為一旦 end 沒有被觸發,異常仍然存在著。下面我們看看解決之道:重寫 broadcast():

function broadcast(message, client) {  var cleanup = []  for(var i=0;i<clientList.length;i+=1) {  if(client !== clientList[i]) {    if(clientList[i].writable) { // 先檢查 sockets 是否可寫   clientList[i].write(client.name + " says " + message)   } else {   cleanup.push(clientList[i]) // 如果不可寫,收集起來銷毀。銷毀之前要 Socket.destroy() 用 API 的方法銷毀。   clientList[i].destroy()   }   }  } //Remove dead Nodes out of write loop to avoid trashing loop index  for(i=0;i<cleanup.length;i+=1) {  clientList.splice(clientList.indexOf(cleanup[i]), 1)  } } 

TCP API 中還提供一個 error 事件,用于捕捉客戶端的異常:

client.on('error', function(e) {  console.log(e); }); 

Node 網絡編程的 API 還豐富,此次僅僅是個入門,更多的內容請接著看,關于瀏覽器 Socket 應用。

Socket.IO

前面說到,瀏覽器雖然也屬于客戶端的一種,但僅支持“單工”的 HTTP 通訊。有見及此,HTML5 新規范中推出了基于瀏覽器的 WebSocket,開發了底層的接口,允許我們能進行 更強大的操作,超越以往的 XHR。

如第一個例子那般,我們無須第三方框架就可以直接與 Node TCP 服務器 進行 Socket  通訊。

但我們又要認清一個事實,不是每個瀏覽器都可以順利支持 WebSocket 的。于是 Socket.IO (http://socket.io)出現了,它提供了不支持 WebSocket 時候的降級支持,同時使得一些舊版本的瀏覽器也可以“全雙工”地工作。優先使用的順序如下:

  • WebSocket
  • Socket over Flash API
  • XHR Polling 長連接
  • XHR Multipart Streaming
  • Forever Iframe
  • JSONP Polling

經過封裝,我們可以不探究客戶端使用上述哪一種技術達致“全雙工”;而我們編寫代碼時,亦無論考慮哪種放法,因為 Socket.IO 給我們的 API 只有一套。了解 Socket.IO 其用法就可以了。

先在瀏覽器部署 Socket.IO 的前端代碼:

<!DOCTYPE html> <html>  <body>  <script src="/socket.io/socket.io.js"></script>  <script>   var socket = io.connect('http://localhost:8080');   // 當服務端發送一條消息到客戶端,message 事件即被觸發。我們把消息在控制臺打印出來   socket.on('message', function(data){ console.log(data) })  </script>  </body> </html> 

服務端 Node 代碼:

var http = require('http'), io = require('socket.io'), fs = require('fs');  // 雖然我們這里使用了同步的方法,那會阻塞 Node 的事件循環,但是這是合理的,因為 readFileSync() 在程序周期中只執行一次,而且更重要的是,同步方法能夠避免異步方法所帶來的“與 SocketIO 之間額外同步的問題”。當 HTML 文件讀取完畢,而且服務器準備好之后,如此按照順序去執行就能讓客戶端馬上得到 HTML 內容。 var sockFile = fs.readFileSync('socket.html');  // Socket 服務器還是構建于 HTTP 服務器之上,因此先調用 http.createServer() server = http.createServer(); server.on('request', function(req, res){  // 一般 HTTP 輸出的格式  res.writeHead(200, {'content-type': 'text/html'});  res.end(sockFile); });  server.listen(8080);  var socket = io.listen(server); // 交由 Socket.io 接管  // Socket.io 真正的連接事件 socket.on('connection', function(client){  console.log('Client connected');  client.send('Welcome client ' + client.sessionId); // 向客戶端發送文本 }); 

當客戶端連接時,服務端會同時出發兩個事件:server.onRequest 和 Socket.onConnection。它們之間有什么區別呢?區別在于 Socket 的是持久性的。

多個 Socket 連接,先是客戶端代碼:

<!DOCTYPE html> <html>  <body>  <script src="/socket.io/socket.io.js"></script>  <script>   var upandrunning = io.connect('http://localhost:8080/upandrunning');   var weather = io.connect('http://localhost:8080/weather');   upandrunning.on('message', function(data){   document.write('<br /><br />Node: Up and Running Update<br />');   document.write(data);   });   weather.on('message', function(data){   document.write('<br /><br />Weather Update<br />');   document.write(data);   });  </script>  </body> </html> 

服務端代碼:

var sockFile = fs.readFileSync('socket.html');  server = http.createServer(); server.on('request', function(req, res){  res.writeHead(200, {'content-type': 'text/html'});  res.end(sockFile); });  server.listen(8080);  var socket = io.listen(server);  socket.of('/upandrunning')  .on('connection', function(client){  console.log('Client connected to Up and Running namespace.');  client.send("Welcome to 'Up and Running'"); });  socket.of('/weather')  .on('connection', function(client){  console.log('Client connected to Weather namespace.');  client.send("Welcome to 'Weather Updates'"); }); 

 如上代碼,我們可以劃分多個命名空間,分別是 upandrunning 和 weather。

關于 Express 中使用 Soclet.io,可以參考《Node:Up and Ruuning》一書的 7.2.2 小節。

今晚時間的關系,涉及 Socket.io 許多方面還沒有談,容小弟我日后再了解。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 旬邑县| 肥城市| 瑞丽市| 普洱| 前郭尔| 蕲春县| 广宁县| 曲麻莱县| 望谟县| 诸暨市| 罗田县| 谷城县| 云阳县| 普兰县| 信宜市| 长寿区| 望奎县| 屯留县| 图木舒克市| 平塘县| 万年县| 当雄县| 华池县| 余干县| 普安县| 镇巴县| 土默特左旗| 张掖市| 吉木萨尔县| 色达县| 富川| 乌兰县| 铜梁县| 铜鼓县| 福安市| 巴林左旗| 潮州市| 周口市| 夹江县| 龙州县| 南川市|