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进程管理模块forever详解
Jun 01 NodeJs
轻松创建nodejs服务器(5):事件处理程序
Dec 18 NodeJs
NodeJS学习笔记之Connect中间件应用实例
Jan 27 NodeJs
PHP和NodeJs开发的应用如何共用Session
Apr 16 NodeJs
使用nodejs中httpProxy代理时候出现404异常的解决方法
Aug 15 NodeJs
用nodejs搭建websocket服务器
Jan 23 NodeJs
NodeJS配置HTTPS服务实例分享
Feb 19 NodeJs
nodejs个人博客开发第六步 数据分页
Apr 12 NodeJs
nodejs入门教程四:URL相关模块用法分析
Apr 24 NodeJs
nodejs动态创建二维码的方法
Aug 12 NodeJs
用Nodejs实现在终端中炒股的实现
Oct 18 NodeJs
nodejs利用readline提示输入内容实例代码
Jul 15 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
Codeigniter整合Tank Auth权限类库详解
2014/06/12 PHP
PHP进程同步代码实例
2015/02/12 PHP
初识laravel5
2015/03/02 PHP
Javascript中的Split使用方法与技巧
2007/03/09 Javascript
js parseInt(&quot;08&quot;)未指定进位制问题
2010/06/19 Javascript
Javascript中的Callback方法浅析
2015/03/15 Javascript
JS添加或修改控件的样式(Class)实现方法
2016/10/15 Javascript
浅谈EasyUi ComBotree树修改 父节点选择的问题
2016/11/07 Javascript
Bootstrap导航条可点击和鼠标悬停显示下拉菜单
2016/11/25 Javascript
easyui-datagrid开发实践(总结)
2017/08/02 Javascript
详解微信小程序获取当前时间及日期的方法
2019/04/28 Javascript
nodejs搭建本地服务器并访问文件操作示例
2019/05/11 NodeJs
js实现随机div颜色位置 类似满天星效果
2019/10/24 Javascript
vue大型项目之分模块运行/打包的实现
2020/09/21 Javascript
[04:41]2014DOTA2国际邀请赛 Liquid顺利突围晋级正赛
2014/07/09 DOTA
Python实现命令行通讯录实例教程
2016/08/18 Python
Python正则简单实例分析
2017/03/21 Python
python 中split 和 strip的实例详解
2017/07/12 Python
对Python 文件夹遍历和文件查找的实例讲解
2018/04/26 Python
使用Python 统计高频字数的方法
2019/01/31 Python
python正则-re的用法详解
2019/07/28 Python
python之PyQt按钮右键菜单功能的实现代码
2019/08/17 Python
Python print不能立即打印的解决方式
2020/02/19 Python
解决python 执行sql语句时所传参数含有单引号的问题
2020/06/06 Python
python 带时区的日期格式化操作
2020/10/23 Python
牦牛毛户外探险服装:Kora
2019/02/08 全球购物
英国知名小木屋定制网站:Tiger Sheds
2020/03/06 全球购物
输入一行文字,找出其中大写字母、小写字母、空格、数字、及其他字符各有多少
2016/04/15 面试题
师范大学音乐表演专业求职信
2013/10/23 职场文书
读书活动实施方案
2014/03/10 职场文书
文化建设工作方案
2014/05/12 职场文书
担保书范文
2015/01/20 职场文书
2016年优秀党务工作者先进事迹材料
2016/02/29 职场文书
redis不能访问本机真实ip地址的解决方案
2021/07/07 Redis
利用Python实现Picgo图床工具
2021/11/23 Python
Android开发之WECHAT微信小程序路由跳转的两种形式
2022/04/12 Java/Android