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实现的一个简单聊天室功能分享
Dec 06 NodeJs
Nodejs爬虫进阶教程之异步并发控制
Feb 15 NodeJs
简单实现nodejs上传功能
Jan 14 NodeJs
进阶之初探nodeJS
Jan 24 NodeJs
Express与NodeJs创建服务器的两种方法
Feb 06 NodeJs
Nodejs 获取时间加手机标识的32位标识实现代码
Mar 07 NodeJs
nodejs 图片预览和上传的示例代码
Sep 30 NodeJs
详解NODEJS基于FFMPEG视频推流测试
Nov 17 NodeJs
nodejs使用redis作为缓存介质实现的封装缓存类示例
Feb 07 NodeJs
NodeJs之word文件生成与解析的实现代码
Apr 01 NodeJs
监控Nodejs的性能实例代码
Jul 02 NodeJs
nodejs中使用worker_threads来创建新的线程的方法
Jan 22 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
微盾PHP脚本加密专家php解密算法
2020/09/13 PHP
PHP FATAL ERROR: CALL TO UNDEFINED FUNCTION BCMUL()解决办法
2014/05/04 PHP
CodeIgniter开发实现支付宝接口调用的方法示例
2016/11/14 PHP
Laravel中如何轻松容易的输出完整的SQL语句
2020/07/26 PHP
JavaScript 获取用户客户端操作系统版本
2009/08/25 Javascript
javascript学习笔记(十八) 获得页面中的元素代码
2012/06/20 Javascript
javaScript让文本框内的最后一个文字的后面获得焦点实现代码
2013/01/06 Javascript
JavaScript中圆括号()和方括号[]的特殊用法疑问解答
2013/08/06 Javascript
jQuery实现类似滑动门切换效果的层切换
2013/09/23 Javascript
JavaScript中的object转换函数toString()与valueOf()介绍
2014/12/31 Javascript
JavaScript资源预加载组件和滑屏组件的使用推荐
2016/03/10 Javascript
javascript函数命名的三种方式及区别介绍
2016/03/22 Javascript
Angular懒加载机制刷新后无法回退的快速解决方法
2016/08/30 Javascript
Bootstrap中glyphicons-halflings-regular.woff字体报404错notfound的解决方法
2017/01/19 Javascript
JavaScript基础之AJAX简单的小demo
2017/01/29 Javascript
jQuery实现的粘性滚动导航栏效果实例【附源码下载】
2017/10/19 jQuery
jQuery实现的页面弹幕效果【测试可用】
2018/08/17 jQuery
前端js中的事件循环eventloop机制详解
2019/05/15 Javascript
原生JS实现列表内容自动向上滚动效果
2019/05/22 Javascript
nodejs中各种加密算法的实现详解
2019/07/11 NodeJs
layui清除radio的选中状态实例
2019/11/14 Javascript
简单的通用表达式求10乘阶示例
2014/03/03 Python
Python中optionParser模块的使用方法实例教程
2014/08/29 Python
Python 确定多项式拟合/回归的阶数实例
2018/12/29 Python
Python pandas对excel的操作实现示例
2020/07/21 Python
Python文件夹批处理操作代码实例
2020/07/21 Python
Python 常用日期处理 -- calendar 与 dateutil 模块的使用
2020/09/02 Python
销售顾问的岗位职责
2013/11/13 职场文书
教师校本培训方案
2014/02/26 职场文书
教师节慰问信
2015/02/15 职场文书
2015年预备党员自我评价
2015/03/04 职场文书
反四风问题学习心得体会
2016/01/22 职场文书
演讲稿:态度决定一切
2019/04/02 职场文书
JavaScript使用canvas绘制坐标和线
2021/04/28 Javascript
python Django框架快速入门教程(后台管理)
2021/07/21 Python
Java由浅入深通关抽象类与接口(上篇)
2022/04/26 Java/Android