示例代码托管在:http://www.github.com/dashnowords/blogs
博客园地址:《大史住在大前端》原创博文目录
华为云社区地址:【你要的前端打怪升级指南】
一. net模块简介
net
模块是nodejs
通讯功能实现的基础,nodejs
中最常用的功能就是作为WebServer使用,建立服务器时使用的http.createServer
就是在net.createServer
方法的基础上建立的。前端最熟悉的http
协议属于应用层协议,应用层的内容想要发送出去,还需要将消息逐层下发,通过传输层(tcp
,udp
),网际层(ip
)和更底层的网络接口后才能被传输出去。net
模块就是对分层通讯模型的实现。
net
模块中有两大主要抽象概念——net.Server
和net.Socket
。《deep-into-node》一书中对Socket
概念进行了解释:
Socket 是对 TCP/IP 协议族的一种封装,是应用层与TCP/IP协议族通信的中间软件抽象层。它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Socket 还可以认为是一种网络间不同计算机上的进程通信的一种方法,利用三元组(ip地址,协议,端口)就可以唯一标识网络中的进程,网络中的进程通信可以利用这个标志与其它进程进行交互。
简单地说,net.Server
实例可以监听一个端口(用于实现客户端TCP
连接通讯)或者地址(用于实现IPC
跨进程通讯),net.Socket
实例可以建立一个套接字实例,它可以用来和server
建立连接,连接建立后,就可以实现通讯了。你可以将socket
想象成手机,把server
想象成基站,虽然不是很贴切,但可以降低理解难度。net
相关API可以直接查看中文文档【net模块文档】。
二. Client-Server的通讯
2.1 server的建立
Server
类的定义非常精简,也很容易看懂:
可以看到构造函数基本上只是初始化了一些属性,然后添加了对connection
事件的响应。服务器是net.Server
类的实例,通过net.createServer([options][,onConnection] )
方法建立,如果传入一个函数,则这个函数会作为connection
事件的回调函数,当一个socket
实例连接到server
时,connection
事件就会触发,回调函数中的形参就指向了发起连接的socket
实例。server
实例并不能独立工作,作为网络服务器使用时需要需要调用listen
方法来监听一个地址,示例如下:
const net = require('net');
const { StringDecoder } = require('string_decoder');
let decoder = new StringDecoder('utf8');
let server = net.createServer(socket=>{
console.log('接收连接');
socket.on('data',data=>{
console.log('收到来自客户端的消息:',decoder.write(data));
});
socket.on('end',function(){
console.log('socket从客户端被关闭了');
});
});
server.listen(12315);
socket上以流的形式发送数据,所以需要调用string_decoder
模块进行解码才能够看到内容,否则看到的就是原始的字节信息。上面的实例监听了12315端口。
2.2 Socket的建立
前文已经提及Socket
是对TCP/IP
协议族的一种封装。客户端通讯套接字是net.Socket
的实例,通过调用实例方法socket.connect(args)
来和服务器建立连接,作为客户端通讯套接字时需要监听端口号,建立连接后,客户端server
通过connection
事件的回调函数就可以拿到发起连接的socket
实例,这样客户端和服务器就可以通讯了,其中一方通过socket.write()
方法写入数据,另一方注册的监听器socket.on('data',onData)
回调函数就会收到信息。socket
实例化示例如下:
const net = require('net');
let socket = new net.Socket();
socket.connect(12315);
//连接服务器
socket.on('connect',c=>{
console.log('成功建立和12315的连接')
setTimeout(()=>{
console.log('建立连接1s后发送消息');
socket.write('SN:1231512315','utf8',function(){
console.log('消息已发送');
});
},1000);
});
socket.on('data',function(resp){
console.log('收到服务器返回消息:',resp);
});
socket.on('end',function(){
console.log('socket从客户端被关闭了');
})
客户端connect
连接服务器的动作,就好比打电话前要先拨号一样,等接通以后,你说的话(也就是socket.write( )
写入的data)才能被发送过去。【代码仓的示例DEMO】中提供了相对完整的示例,分别放在server.js
和client.js
中,你可以通过控制台打印的信息来观察每条语句执行的先后顺序,熟悉从通信建立到消息收到再到服务器关闭的整个过程,记得要先起服务器,后起客户端。
Tips:你可以使用postman向这个server发一个GET请求,看看是什么样子,对理解
http
和tcp/ip
的关系有很大帮助,它非常直观,反正我是第一次见。
三. IPC通讯
IPC
通讯是指Inter Process Communication,也就是跨进程通讯,上一节在提到cluster
时已经介绍过进程之间是资源隔离的,所以跨进程通讯也需要通过net
模块来建立消息管道。它的用法比较简单,只需要将server.listen( )
和socket.connect( )
的参数从端口号换成地址字符串就可以了。示例代码如下:
const net = require('net');
const cluster = require('cluster');
const path = require('path');
const { StringDecoder } = require('string_decoder');
let serverForIPC;//作为子进程的server
if (cluster.isMaster) {
//主进程执行逻辑
setupMaster();
cluster.fo