vivatoviva / Interview-Frontend-2020

欢迎star、在对应的ussues沉淀知识
17 stars 2 forks source link

net #41

Open vivatoviva opened 5 years ago

vivatoviva commented 5 years ago

net

net 模块用于创建基于流的 TCP 或 IPC 的服务器(net.createServer())与客户端(net.createConnection())。

文档部分

net 模块在 Windows 上支持命名管道 IPC,在其他操作系统上支持 UNIX 域套接字。

有两个类,一个是net.Server 类, 一个是net.Socket 类型

(1) net.Server类

(2) net.Socket类

此类是 TCP 套接字或流式 IPC 端点的抽象(在 Windows 上使用命名管道,否则使用 UNIX 域套接字)。 net.Socket 也是双工流,因此它既可读也可写,也是一个 EventEmitter

net.Socket 可以由用户创建并直接用于与服务器交互。 例如,它由 net.createConnection() 返回,因此用户可以使用它与服务器通信。

它也可以由 Node.js 创建,并在收到连接时传给用户。 例如,它被传给 net.Server 上触发的 'connection' 事件的监听器,因此用户可以使用它与客户端进行交互。

(4) net上的方法

理解部分

(1)网络模型

NET

(1) 什么是socket

Socket又称之为“套接字”,是系统提供的用于网络通信的方法。它的实质并不是一种协议,没有规定计算机应当怎么样传递消息,只是给程序员提供了一个发送消息的接口,程序员使用这个接口提供的方法,发送与接收消息。

Socket描述了一个IP、端口对。它简化了程序员的操作,知道对方的IP以及PORT就可以给对方发送消息,再由服务器端来处理发送的这些消息。所以,Socket一定包含了通信的双发,即客户端(Client)与服务端(server)。这个net模块中涉及到的socket是基于TCP的,基于UDP的scoket在下一节,也就是dgram模块中

每一个应用或者说服务,都有一个端口。比如DNS的53端口,http的80端口。我们能由DNS请求到查询信息,是因为DNS服务器时时刻刻都在监听53端口,当收到我们的查询请求以后,就能够返回我们想要的IP信息。所以,从程序设计上来讲,应该包含以下步骤:

1)服务端利用Socket监听端口;

2)客户端发起连接;

3)服务端返回信息,建立连接,开始通信;

4)客户端,服务端断开连接。

201038101759106

(2) 粘包问题

一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据。TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的值往往小于在应用处理的消息数据,所以就会引发一次接收的数据无法满足消息的需要,导致粘包的存在。处理粘包的唯一方法就是制定应用层的数据通讯协议,通过协议来规范现有接收的数据是否满足消息数据的需要。

TCP粘包通常在流传输中出现,UDP则不会出现粘包,因为UDP有消息边界。使用TCP协议发送数据段需要等待缓冲区满了才将数据发送出去,当满的时候有可能不是一条消息而是几条消息存在于缓冲区内,为了优化性能(Nagle算法),TCP会将这几个小数据包合并为一个大的数据包,造成粘包;另外在接收数据端,如果没能及时接收缓冲区的包,也会造成缓冲区多包合并接收,这也是粘包。

(3) 举例实现

实现一个TCP连接通信的客户端和服务器端:

// client.ts
import net from 'net';

const PORT = 8000;

const client = new net.Socket();
client.connect(PORT, () => {
    client.write('I am Chuck Norris!');
});

client.on('data', (data) => {
  console.log('客户端接受到信息');
});

client.on('end', () => {
  console.log('Connection end')
})

client.on('close', () => {
    console.log('Connection closed');
});
// server.ts
import net, { Socket, Server } from 'net';

const server = net.createServer();

var PORT = 8000;

server.on('connection', (socket) => {
  console.log('CONNECTED: ' + socket.remoteAddress +':'+ socket.remotePort);
  // 设置连接时间
  socket.setTimeout(3000);
  // 监听socket的发送
  socket.on('data', (data) => {
    console.log(data.toString());
  })
  // socket结束
  socket.on('end', () => {
    console.log('服务器端一次连接完成');
  })
  // 超时关闭
  socket.on('timeout', () => {
    console.log('客户端无响应,自动关闭');
    socket.end();
  })
});

server.listen(PORT, () => {
  console.log('服务器端创建完成');
});