一,开篇分析

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

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

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

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

从图中不难看出 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。

运行结果:

三,案例引入

(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测试一下:telnet 127.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); // 删除数组中的制定元素。
})
})

Node TCP API 已经为我们提供了 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),Net API 中还提供一个 error 事件,用于捕捉客户端的异常

client.on('error', function(e) {
console.log(e);
});

四,总结一下

1,理解开篇的相关概念

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

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

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

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

        哈哈哈,本篇结束,未完待续,希望和大家多多交流够沟通,共同进步。。。。。。呼呼呼……(*^__^*)            

大熊君大话NodeJS之------Net模块的更多相关文章

  1. 大熊君大话NodeJS之------Stream模块

    一,开篇分析 流是一个抽象接口,被 Node 中的很多对象所实现.比如对一个 HTTP 服务器的请求是一个流,stdout 也是一个流.流是可读,可写或兼具两者的. 最早接触Stream是从早期的un ...

  2. 大熊君大话NodeJS之------Http模块

    一,开篇分析 首先“Http”这个概念大家应该比较熟悉了,它不是基于特定语言的,是一个通用的应用层协议,不同语言有不同的实现细节,但是万变不离其宗,思想是相同的, NodeJS作为一个宿主运行环境,以 ...

  3. 大熊君大话NodeJS之------MongoDB模块(额外篇)

    一,开篇分析 这篇属于扩展知识篇,因为在下面的文章中会用到数据库操作,所以今天就来说说它(Mongodb模块). (1),简介 MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为 ...

  4. 大熊君大话NodeJS之------Buffer模块

    一,开篇分析 所谓缓冲区Buffer,就是 "临时存贮区" 的意思,是暂时存放输入输出数据的一段内存. JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一 ...

  5. 大熊君大话NodeJS之------Connect中间件模块(第一季)

    一,开篇分析 截止到今天来说,NodeJS系列文章已经有将近十篇了,让我们回顾一下: (1),大熊君大话NodeJS之开篇------Why NodeJS(将Javascript进行到底) (2),大 ...

  6. 大熊君大话NodeJS之------基于Connect中间件的小应用(Bigbear记事本应用之第一篇)

    一,开篇分析 大家好哦,大熊君又来了,昨天因为有点个人的事没有写博客,今天又出来了一篇,这篇主要是写一个记事本的小应用,前面的文章, 我也介绍过“Connect”中间件的使用以及“Mongodb”的用 ...

  7. 大熊君大话NodeJS之开篇------Why NodeJS(将Javascript进行到底)

    一,开篇分析 大家好啊,大熊君又来啦(*^__^*) 嘻嘻……,之前我写过一系列关于JS(OOP与设计模式)方面的文章,反响还好,其实这也是对我本人最大的鼓励,于是我决定我要将JavaScript进行 ...

  8. 大熊君大话NodeJS之 ------ Connect中间件第二季(源码分析)

    一,开篇分析 大家好,大熊君又回来了,今天这篇文章主要是对"Connect"中间件以及相关辅助中间件,做一个源码分析系列,我想上一篇文章大家也看了, 介绍了使用方式及用途,而这篇也 ...

  9. 大熊君大话NodeJS之------FS文件模块

    一,开篇分析 文件系统模块是一个简单包装的标准 POSIX 文件 I/O 操作方法集.可以通过调用 require("fs") 来获取该模块.文件系统模块中的所有方法均有异步和同步 ...

随机推荐

  1. wordpress /wp-content/plugins/wp-symposium/server/php/UploadHandler.php File Arbitrary Upload Vul

    catalog . 漏洞描述 . 漏洞触发条件 . 漏洞影响范围 . 漏洞代码分析 . 防御方法 . 攻防思考 1. 漏洞描述 Relevant Link:2. 漏洞触发条件3. 漏洞影响范围4. 漏 ...

  2. struts2 CVE-2013-1965 S2-012 Showcase app vulnerability allows remote command execution

    catalog . Description . Effected Scope . Exploit Analysis . Principle Of Vulnerability . Patch Fix 1 ...

  3. Integrated Circuit Intro

    1.Noise Margin 不管是TTL还是CMOS Logic Level,都会有门限.如,5V CMOS的VoutH > 4.4V, VoutL <0.33V;而它对应的输入电平Vi ...

  4. shell命令bc

    简介 bc支持浮点数的精度运算(Bash不支持浮点数运算) 运行方式 一.CLI 二.PIPE 示例 一.浮点数运算 变量scale:设置小数点后面的位数  # 默认scale=0 echo &quo ...

  5. 数组、ArraryList和List三者的区别

    在C#中数组,ArrayList,List都能够存储一组对象,那么这三者到底有什么样的区别呢. 数组 数组在C#中最早出现的.在内存中是连续存储的,所以它的索引速度非常快,而且赋值与修改元素也很简单. ...

  6. 用DOS命令配置服务开机自启动

    2016-08-19 15:01 Create 使用命令  sc  config 参考博客:http://blog.csdn.net/it1988888/article/details/7992626 ...

  7. Linux DHCP通过OPTION43为H3C的AP下发AC地址(总结)

    对于DHCP服务,可以在很多平台上进行设置.那么这里我们就主要讲解一下在Linux DHCP服务器上通过option 43实现H3C的AP自动联系AC注册的相关内容.原来的DHCP Server是放在 ...

  8. Mysql学习笔记(七)mysql编程基础之自定义函数。

    delimiter $$ create function fn_liangzifunction() returns int no sql begin ; return @row_no; end; $$ ...

  9. 高性能JavaScript笔记二(算法和流程控制、快速响应用户界面、Ajax)

    循环 在javaScript中的四种循环中(for.for-in.while.do-while),只有for-in循环比其它几种明显要慢,另外三种速度区别不大 有一点需要注意的是,javascript ...

  10. 算法与设计模式系列1之Python实现常见算法

    preface 常见的算法包括: 递归算法 二分法查找算法 冒泡算法 插入排序 快速排序 二叉树排序 下面就开始挨个挨个的说说原理,然后用Python去实现: 递归算法 一个函数(或者程序)直接或者间 ...