Nodejs学习笔记之NET模块


Posted in NodeJs onJanuary 13, 2015

一,开篇分析

从今天开始,我们来深入具体的模块学习,这篇文章是这个系列文章的第三篇,前两篇主要是以理论为主,相信大家在前两篇的学习中,

对NodeJS也有一个基本的认识,没事!!!趁热打铁,让我们继续将NodeJS进行到底,好了废话不多说,直接进入今天的主题 “Net模块” ,那么”Net“应该如何理解那?

它是做什么用的那?(Net模块可用于创建Socket服务器或Socket客户端。NodeJS 的数据通信,最基础的两个模块是 Net 和 Http,前者是基于 Tcp 的封装,后者本质还是 Tcp 层,只不过做了比较多的数据封装,我们视为表现层)。

这里参考一下NodeJS “http.js” 中的源码:

Nodejs学习笔记之NET模块

从图中不难看出 HttpServer继承了Net类,具有了相关的通信能力,做了比较多的数据封装,我们视为更高级的表现层。

扩展知识(以下是“inherits”的源码):

exports.inherits = function(ctor, superCtor) {

  ctor.super_ = superCtor;

  ctor.prototype = Object.create(superCtor.prototype, {

    constructor: {

      value: ctor,

      enumerable: false,

      writable: true,

      configurable: true

    }

  });

};

功能是实现继承复用。

刚才做了一个简要的概述,里面有一些常用的概念,这里做个简短的概念普及介绍:

(1),TCP/IP------TPC/IP协议是传输层协议,主要解决数据如何在网络中传输。

(2),Socket------socket则是对TCP/IP协议的封装和应用(程序层面)。

(3),Http------HTTP是应用层协议,主要解决如何包装数据。

(4),网络七层模型------物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

总结一下:Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。

从而形成了我们知道的一些最基本的函数接口,比如Create、Listen、Connect、Accept、Send、Read和Write等等。

TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口

实际上,传输层的TCP是基于网络层的IP协议的,而应用层的HTTP协议又是基于传输层的TCP协议的,而Socket本身不算是协议,就像上面所说,它只是提供了一个针对TCP或者UDP编程的接口。

二,体验一把

好了,概念我们也有了,来个例子:

1,建立server.js

var net = require('net') ;

var server = net.createServer(function(c) { // Connection监听器

  console.log("服务器已连接") ;

  c.on("end", function() {

    console.log("服务器已断开") ;

  }) ;

  c.write("Hello,Bigbear !\r\n") ;

  c.pipe(c) ;

}) ;

server.listen(8124, function() { // Listening监听器

  console.log("服务器已绑定") ;

}) ;

2,建立client.js

var net = require('net') ;

var client = net.connect({

    port: 8124

},function(){ // connect监听器

  console.log("客户端已连接") ;

  client.write('Hello,Baby !\r\n') ;

});

client.on("data", function(data) {

  console.log(data.toString()) ;

  client.end() ;

});

client.on("end", function(){

  console.log("客户端断开连接") ;

}) ;

分析一下:

服务端------net.createServer创建一个 TCP 服务,这个服务绑定(server.listen)在 8124 这个端口上,创建 Server 后我们看到有一个回调函数,

在调用上面函数的时候传入一个参数,这个参数也是函数,并且接受了 socket ,这个由其他方法构造的一个管道(pipe),他的作用就是用来数据交互的。

pipe 是需要 Client 跟 Server 打招呼才能建立的,如果此刻没有客户端访问 Server,这个 socket 就不会存在了。

客户端------net.connect顾名思义,就是连接到服务端,第一个参数是对象,设置端口(port)为 8124,也就是我们服务器监听的端口,由于没有设置 host 参数,那默认就是 localhost (本地)。

在 Server 中,socket 是管道的一端,而在 client 中,client 本身就是管道的一端,如果是多个客户端连接 Server,Server 会新建多个 socket,每个 socket 对应一个 client。

运行结果:

Nodejs学习笔记之NET模块

三,案例引入

(1),下面代码仅仅是服务器向客户端输出一段文本,完成服务端到客户端的单向通讯。

//  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测试一下:telnet127.0.0.1:9000

执行 telnet后,与服务点连接,反馈 Hi! Bye! 的字符,并立刻结束服务端程序终止连接。

如果我们要服务端接到到客户端的信息?

可以监听 server.data 事件并且不要中止连接(否则会立刻结束无法接受来自客户端的消息)。

(2),监听 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里面,这明显是一个空指针。

(3),处理clientList

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); // 删除数组中的制定元素。

  })

})

NodeTCPAPI已经为我们提供了 end 事件,即客户端中止与服务端连接的时候发生。

(4),优化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)

  }

}

注意的是一旦 “end” 没有被触发,会出现异常,所以才做优化工作。

(5),NetAPI中还提供一个 error 事件,用于捕捉客户端的异常

client.on('error', function(e) {

  console.log(e);

});

四,总结一下

1,理解开篇的相关概念

2,认识Http与Net模块之间的关系

3,结合本文的例子,查阅相关api去实践

4,socket客户端与服务器端之间的通信思想

5,有兴趣可以完善一下那个聊天室的例子

NodeJs 相关文章推荐
nodejs教程之制作一个简单的文章发布系统
Nov 21 NodeJs
基于html5和nodejs相结合实现websocket即使通讯
Nov 19 NodeJs
详解nodejs异步I/O和事件循环
Jun 07 NodeJs
nodejs body-parser 解析post数据实例
Jul 26 NodeJs
Nodejs调用WebService的示例代码
Sep 29 NodeJs
Nodejs下使用gm圆形裁剪并合成图片的示例
Feb 22 NodeJs
nodejs实现连接mongodb数据库的方法示例
Mar 15 NodeJs
详解Nodejs内存治理
May 13 NodeJs
关于NodeJS中的循环引用详解
Jul 23 NodeJs
nodejs语言实现验证码生成功能的示例代码
Oct 13 NodeJs
nodejs开发一个最简单的web服务器实例讲解
Jan 02 NodeJs
使用nodejs实现JSON文件自动转Excel的工具(推荐)
Jun 24 NodeJs
Nodejs学习笔记之Global Objects全局对象
Jan 13 #NodeJs
Nodejs为什么选择javascript为载体语言
Jan 13 #NodeJs
NodeJS中Buffer模块详解
Jan 07 #NodeJs
Nodejs中读取中文文件编码问题、发送邮件和定时任务实例
Jan 01 #NodeJs
Nodejs中调用系统命令、Shell脚本和Python脚本的方法和实例
Jan 01 #NodeJs
nodejs中实现路由功能
Dec 29 #NodeJs
NodeJS制作爬虫全过程(续)
Dec 22 #NodeJs
You might like
Win2000+Apache+MySql+PHP4+PERL安装使用小结
2006/10/09 PHP
有关JSON以及JSON在PHP中的应用
2010/04/09 PHP
深入分析PHP引用(&amp;)
2014/09/04 PHP
PHP带节点操作的无限分类实现方法详解
2016/11/09 PHP
学习thinkphp5.0验证类使用方法
2017/11/16 PHP
Thinkphp框架使用list_to_tree 实现无限级分类列出所有节点示例
2020/04/04 PHP
JS option location 页面跳转实现代码
2008/12/27 Javascript
JavaScript实现找出数组中最长的连续数字序列
2014/09/03 Javascript
JS实现按比例缩放图片的方法(附C#版代码)
2015/12/08 Javascript
jQuery实现打开页面渐现效果示例
2016/07/27 Javascript
js canvas仿支付宝芝麻信用分仪表盘
2016/11/16 Javascript
利用D3.js实现最简单的柱状图示例代码
2016/12/09 Javascript
js实现下一页页码效果
2017/03/07 Javascript
jquery submit()不能提交表单的解决方法
2017/04/24 jQuery
使用jQuery.Pin垂直滚动时固定导航
2017/05/24 jQuery
利用CDN加速react webpack打包后的文件详解
2018/02/22 Javascript
Vue下滚动到页面底部无限加载数据的示例代码
2018/04/22 Javascript
js如何找出字符串中的最长回文串
2018/06/04 Javascript
vue  directive定义全局和局部指令及指令简写
2018/11/20 Javascript
vue 双向数据绑定的实现学习之监听器的实现方法
2018/11/30 Javascript
JavaScript实现shuffle数组洗牌操作示例
2019/01/03 Javascript
layui多图上传实现删除功能的例子
2019/09/23 Javascript
JS数组降维的实现Array.prototype.concat.apply([], arr)
2020/04/28 Javascript
Python编程中的for循环语句学习教程
2015/10/14 Python
Python基于回溯法子集树模板实现8皇后问题
2017/09/01 Python
Python queue队列原理与应用案例分析
2019/09/27 Python
Python hashlib常见摘要算法详解
2020/01/13 Python
Django contrib auth authenticate函数源码解析
2020/11/12 Python
HTML5中使用postMessage实现Ajax跨域请求的方法
2016/04/19 HTML / CSS
基于 HTML5 的 WebGL 3D 版俄罗斯方块的示例代码
2018/05/28 HTML / CSS
最常使用的求职信
2014/05/25 职场文书
高三英语复习计划
2015/01/19 职场文书
七一慰问简报
2015/07/20 职场文书
安全事故隐患排查治理制度
2015/08/05 职场文书
社区服务活动感想
2015/08/11 职场文书
解决vue自定义组件@click点击失效问题
2022/04/30 Vue.js