学习 NodeJS 第八天:Socket 通讯实例_node.js

前言

一般来讲,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 许多方面还没有谈,容小弟我日后再了解。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索socket
, nodejs
, socket通信
socket.io
,以便于您获取更多的相关知识。

时间: 2024-09-11 21:10:27

学习 NodeJS 第八天:Socket 通讯实例_node.js的相关文章

使用DNode实现php和nodejs之间通信的简单实例_node.js

一.安装DNode 1, for nodejs, 执行 复制代码 代码如下: $ sudo npm install dnode 2, for php, 利用composer来安装DNode php 执行下列语句下载composer 复制代码 代码如下: $ wget http://getcomposer.org/composer.phar 创建一个文件composer.json,然后填入如下语句, 复制代码 代码如下: {     "require": {         "

NodeJS学习笔记之Connect中间件应用实例_node.js

一,开篇分析 大家好哦,大熊君又来了,昨天因为有点个人的事没有写博客,今天又出来了一篇,这篇主要是写一个记事本的小应用,前面的文章, 我也介绍过"Connect"中间件的使用以及"Mongodb"的用法,今天就结合这两个中间件,写个实际的例子,不断完善和重构,已达到 充分学习的目的.好了,废话不说了,直接进入主题. 二,需求分析 (1),用户注册,登录功能(没有涉及很复杂的交互场景,注册时会有用户判断是否已存在). (2),用户登录成功,进入笔记管理系统的后台(笔记

nodejs中实现sleep功能实例_node.js

nodejs最让人不爽的就是其单线程特性,很多事情没法做,对CPU密集型的场景,性能也不够强劲.很长一段时间,我想在javascript语言框架下寻求一些解决方案,解决无法操作线程.性能差的问题.曾经最让我印象深刻的方案是fibers,不过fibers也好,其他方案也好,在线程操作上还是很别扭,太过依赖辅助线程,本末倒置:就fiber而言,javascript固有的低性能问题并不能解决:最别扭的是在javascript语言框架下,线程间的消息传递常常很受限制,经常无法真正地共享对象. nodej

使用upstart把nodejs应用封装为系统服务实例_node.js

一.nodejs应用普通部署方式介绍 终于要把nodejs的应用程序部署上线了, 把源代码通过git复制到目录下面 复制代码 代码如下: /root/deploy/movie 然后搞命令: 复制代码 代码如下: ~ cd /root/deploy/movienode ./app.js 上面的方式,nodejs程序会在当前的console界面中运行,一旦console结束,应用也会停止.我们改一下命令,让程序在后台运行 复制代码 代码如下: ~ node ./app.js &[1] 21333[2

学习 NodeJS 第八天:Socket 通讯

前言 一般来讲,HTTP 是基于文本的"单向"通讯机制.这里所谓的"单向",乃相对于"双向"而言,因为 HTTP 服务器只需根据请求返还恰当的 HTML 给客户端即可,不涉及客户端向服务端的通讯.这种单向的机制比较简单,对网络质量要求也不高.而更多的场景则是需要可靠.稳定的端到端连接.一般这种服务是实时的.有态的而且是长连接,长连接则暗示两段须达致相向通讯的能力,也就说是服务端客户端两者间能够实时地相互间通信.毫无疑问,能够实时通信的服务器正是我

nodejs开发微博实例_node.js

之前一直执着于前端开发,最近几天,开始学起了nodejs.作为一名前端开发者,见到这样一门用javascript写的后台自然是很激动的.但是,后台毕竟不同于前端,在学习的过程中,还是会遇到不少问题. 为了开始学习nodejs,一开始选择了<深入浅出nodejs>这本书,看了几章之后,得出一个结论是:真是一本好书,但是还是不会写nodejs!然后选择了另外一本教材<nodejs开发指南>,由于看过了<深入浅出nodejs>,直接跳过了这本书的前几章,写起了该书第五章的微博

node.js中的Socket.IO使用实例_node.js

1. 简介 首先是Socket.IO的官方网站:http://socket.io 官网非常简洁,甚至没有API文档,只有一个简单的"How to use"可以参考.因为Socket.IO就跟官网一样简洁好用易上手. 那么Socket.IO到底是什么呢?Socket.IO是一个WebSocket库,包括了客户端的js和服务器端的nodejs,它的目标是构建可以在不同浏览器和移动设备上使用的实时应用.它会自动根据浏览器从WebSocket.AJAX长轮询.Iframe流等等各种方式中选择最

NodeJS Web应用监听sock文件实例_node.js

像 NodeJS 写的 TCP 服务可以监听在某个 sock 文件(Domain Socket) 上,它的 HTTP 服务也能这么干.虽然作为 HTTP 服务连接某个 sock 文件的意义不大,所以这里只算是一个纯粹的尝试. TCP 服务是这样写 复制代码 代码如下: var net = require('net'); net.createServer(function (socket) {   socket.on('data', function (data) {     socket.wri

Nodejs中调用系统命令、Shell脚本和Python脚本的方法和实例_node.js

每种语言都有自己的优势,互相结合起来各取所长程序执行起来效率更高或者说哪种实现方式较简单就用哪个,nodejs是利用子进程来调用系统命令或者文件,文档见http://nodejs.org/api/child_process.html,NodeJS子进程提供了与系统交互的重要接口,其主要API有: 标准输入.标准输出及标准错误输出的接口.   NodeJS 子进程提供了与系统交互的重要接口,其主要 API 有:   标准输入.标准输出及标准错误输出的接口 child.stdin 获取标准输入 ch