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 22 NodeJs
nodejs爬虫抓取数据乱码问题总结
Jul 03 NodeJs
浅析nodejs实现Websocket的数据接收与发送
Nov 19 NodeJs
nodejs学习笔记之路由
Mar 27 NodeJs
nodejs入门教程一:概念与用法简介
Apr 24 NodeJs
nodejs处理图片的中间件node-images详解
May 08 NodeJs
nodejs 搭建简易服务器的图文教程(推荐)
Jul 18 NodeJs
nodejs简单实现TCP服务器端和客户端的聊天功能示例
Jan 04 NodeJs
nodejs实现连接mongodb数据库的方法示例
Mar 15 NodeJs
Nodejs调用Dll模块的方法
Sep 17 NodeJs
Nodejs实现的操作MongoDB数据库功能完整示例
Feb 02 NodeJs
5分钟教你用nodeJS手写一个mock数据服务器的方法
Sep 10 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
Zend Framework基于Command命令行建立ZF项目的方法
2017/02/18 PHP
PHP ob缓存以及ob函数原理实例解析
2020/11/13 PHP
javascript suggest效果 自动完成实现代码分享
2012/02/17 Javascript
关于js中alert弹出窗口文本换行问题简单详细说明
2012/12/11 Javascript
Javascript学习笔记之 函数篇(二) : this 的工作机制
2014/06/24 Javascript
javascript实现校验文件上传控件实例
2015/04/20 Javascript
JS中传递参数的几种不同方法比较
2017/01/20 Javascript
利用babel将es6语法转es5的简单示例
2017/12/01 Javascript
Postman模拟发送带token的请求方法
2018/03/31 Javascript
JavaScript实现数字前补“0”的五种方法示例
2019/01/03 Javascript
使用vue2.6实现抖音【时间轮盘】屏保效果附源码
2019/04/24 Javascript
解决Vue使用bus总线时,第一次路由跳转时数据没成功传递问题
2020/07/28 Javascript
在vue项目中封装echarts的步骤
2020/12/25 Vue.js
python检测lvs real server状态
2014/01/22 Python
用Python和WordCloud绘制词云的实现方法(内附让字体清晰的秘笈)
2019/01/08 Python
python根据txt文本批量创建文件夹
2020/12/08 Python
python GUI库图形界面开发之PyQt5单行文本框控件QLineEdit详细使用方法与实例
2020/02/27 Python
pycharm工具连接mysql数据库失败问题
2020/04/01 Python
用python按照图像灰度值统计并筛选图片的操作(PIL,shutil,os)
2020/06/04 Python
用 Django 开发一个 Python Web API的方法步骤
2020/12/03 Python
python正则表达式re.match()匹配多个字符方法的实现
2021/01/27 Python
分享CSS3中必须要知道的10个顶级命令
2012/04/26 HTML / CSS
常用的HTML5列表标签
2017/06/20 HTML / CSS
墨西哥网上超市:Superama
2018/07/10 全球购物
旅游管理专业生自荐信范文
2014/01/02 职场文书
银行演讲稿范文
2014/01/03 职场文书
石油大学毕业生自荐信
2014/01/28 职场文书
理财投资建议书
2014/03/12 职场文书
英文演讲稿
2014/05/15 职场文书
家长学校培训材料
2014/08/20 职场文书
大学学生个人总结
2015/02/15 职场文书
《秋思》教学反思
2016/02/23 职场文书
解除合同协议书范本
2016/03/21 职场文书
详解MySQL的半同步
2021/04/22 MySQL
详解JavaScript中的执行上下文及调用堆栈
2021/04/29 Javascript
Python序列化与反序列化相关知识总结
2021/06/08 Python