Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dgram #45

Open
Genluo opened this issue Sep 6, 2019 · 0 comments
Open

dgram #45

Genluo opened this issue Sep 6, 2019 · 0 comments
Labels
Node Node相关

Comments

@Genluo
Copy link
Owner

Genluo commented Sep 6, 2019

dgram

dgram 模块提供了 UDP 数据包 socket 的实现。

文档部分

(1) dgram.Socket类

  • 事件

    • close
    • error
    • listening
    • message
  • 方法

    • addMembership:多播,将此socket添加到组播组中
    • address
    • bind: 不传callback函数为同步执行,传递为异步执行

    在配合cluster模块使用dgram.Socket对象时, options对象可能包含一个附加的exclusive属性。当exclusive被设为false(默认值)时,集群工作单元会使用相同的 socket 句柄来共享连接处理作业。当exclusive被设为true时,该句柄将不会被共享,而尝试共享端口则会造成错误。

    • close
    • dropMembership: 多播,将此socket从多播组中删除

    引导内核通过IP_DROP_MEMBERSHIP这个 socket 选项删除multicastAddress指定的多路传送集合。当 socket 被关闭或进程被终止时,该方法会被内核自动调用,所以大多数的应用都不用自行调用该方法。

    • getRecvBufferSize
    • getSendBufferSize
    • ref / unref: socket运行时阻止Node进程退出,socket.unref() 方法用于将 socket 从维持 Node.js 进程的引用列表中解除。socket.ref() 方法用于将 socket 重新添加到这个引用列表中,并恢复其默认行为。
    • send
    • setBroadcast

    设置或清除 SO_BROADCAST socket 选项。当设置为 true, UDP包可能会被发送到一个本地接口的广播地址

    • setMulticastInterface:多播发送消息

    • setMulticastLoopback: 设置为true,发送的多播数据包也将在本地端口进行接收

    • setMulticastTTL:设置发送的多播数据包的生存时间(TTL)

    • setTTL

    • setRecvBufferSize

    • setBufferSize

(2) dgram上的方法

  • createSocket:创建dgram.Socket实例的工厂函数

理解部分

(1) UDP和TCP的区别(基于Node提供的API)

  • 多个UDP类型的socket可以监听同一个端口,但是TCP类型的Socket不能复用,只能唯一确定一个
  • UDP的socket是无连接,所以不能知道连接方的socket的信息,通常都是通过message回调函数传入的
  • TCP的socket是建立连接的,所以连接后之知道连接方的socket信息,可以直接听过实例方法取得
  • 基于TCP的Socket不能监听端口,必须借用Sever来监听端口,当发现请求的时候会生成一个基于tcp的Socket进行连接(为什么基于tcp的socket不能绑定端口,必须要借助Server类实例)
  • 只有基于UDP协议的socket才支持多播

(2) 单播、广播、多播

这是网络中出现的三种通信方式,组播是出现最晚的,但是他结合单播和广播的优点,最具有发展潜力

1.单播:网络节点之间的通信就好像是人们之间的对话一样。如果一个人对另外一个人说话,那么用网络技术的术语来描述就是“单播”,此时信息的接收和传递只在两个节点之间进行。单播在网络中得到了广泛的应用,网络上绝大部分的数据都是以单播的形式传输的,只是一般网络用户不知道而已。例如,你在收发电子邮件、浏览网页时,必须与邮件服务器、Web服务器建立连接,此时使用的就是单播数据传输方式。但是通常使用“点对点通信”(Point to Point)代替“单播”,因为“单播”一般与“多播”和“广播”相对应使用。

2.多播:“多播”也可以称为“组播”,在网络技术的应用并不是很多,网上视频会议、网上视频点播特别适合采用多播方式。因为如果采用单播方式,逐个节点传输,有多少个目标节点,就会有多少次传送过程,这种方式显然效率极低,是不可取的;如果采用不区分目标、全部发送的广播方式,虽然一次可以传送完数据,但是显然达不到区分特定数据接收对象的目的。采用多播方式,既可以实现一次传送所有目标节点的数据,也可以达到只对特定对象传送数据的目的。   IP网络的多播一般通过多播IP地址来实现。多播IP地址就是D类IP地址,即224.0.0.0至239.255.255.255之间的IP地址。Windows 2000中的DHCP管理器支持多播IP地址的自动分配。

3.广播:“广播”在网络中的应用较多,如客户机通过DHCP自动获得IP地址的过程就是通过广播来实现的。但是同单播和多播相比,广播几乎占用了子网内网络的所有带宽。拿开会打一个比方吧,在会场上只能有一个人发言,想象一下如果所有的人同时都用麦克风发言,那会场上就会乱成一锅粥。集线器由于其工作原理决定了不可能过滤广播风暴,一般的交换机也没有这一功能,不过现在有的网络交换机(如全向的QS系列交换机)也有过滤广播风暴功能了,路由器本身就有隔离广播风暴的作用。   广播风暴不能完全杜绝,但是只能在同一子网内传播,就好像喇叭的声音只能在同一会场内传播一样,因此在由几百台甚至上千台电脑构成的大中型局域网中,一般进行子网划分,就像将一个大厅用墙壁隔离成许多小厅一样,以达到隔离广播风暴的目的。   在IP网络中,广播地址用IP地址“255.255.255.255”来表示,这个IP地址代表同一子网内所有的IP地址。

在node中实现了组播(多播),接受方的socket有一个核心的过程就是加入组播组中,这样的话这个socket才能接受到后面发送的组播信息。

为什么组播很重要

自从上世纪末长城宽带壮烈的宽带推广运动以来,宽带网一直面临种种问题,但这些问题归结起来就是一个问题,那就是客户端得不到与其接入带宽相称的足够的数据流。

最早的长城宽带面临的是“宽带无内容”的问题,客户得不到其承诺的视频点播等宽带娱乐,于是投诉、退户甚至诉诸法律。

电信凭借其雄厚的财力和电话线资源后来居上,但很快又面临网速慢、缺内容的投诉,电信网站上的视频点播似乎总是无尽的等待和缓冲。后来P2P软件的出现使得某些比较专业的用户似乎看到了希望,他们用BT、电驴等软件互传电影等娱乐信息也凑合了。没多久电信和网通就高举着和他们没什么关系的版权大旗封杀了BT、电驴等软件。什么是组播 - Liuqz - liuqz926 的博客

所有这些都是源于现在宽带网的“上下非对称”的金字塔结构,也就是网络主干的带宽远远小于所有用户带宽之和,但现在网络使用的单播通讯协议却要求网络主干的带宽等于或接近所有用户带宽之和。现在的状况是一个城市或省的网络出口主干的带宽大约相当于其所有客户带宽之和的5%,也就是说假如有5%的客户用BT软件通过网络全速传输数据,那其余95%的客户就不要玩了。现在电信主干上的流量的75%都是P2P应用的流量,已经超过了电信所能承受的极限。

那么采用CDN技术,将网络内容在城域网内就近缓冲行不行呢?答案是:技术上可行经济上行不通。其需要的服务器是一个巨大的天文数字。现在的大中城市的宽带网用户数量都在20万以上,以此数量来计算光购置CDN服务器就需要2亿元左右!这就是为什么电信不用CDN技术来满足客户需求的原因。所以在服务器的服务能力和客户机的需求上也存在着严重的上下非对称结构。

那么这个死结是不是没法解开呢?当然不是,组播协议的数据流特点就是“上下非对称”的,也就是说,在网络主干上的一条数据流通过每层交换机的复制可以变成无数客户端的数据流,形成客户端数据流之和远大于主干数据流的金字塔结构。这一特点正好与现在的网络结构相符。所以说,基于组播协议的流媒体宽带娱乐可以解决这一问题。

举例来说,使用基于组播协议的直播系统可以用一台服务器支持数万客户收看一个或几个频道的网上电视直播。假设一共提供100个频道的电视节目,每个频道是1M的MPEG4高清晰码流,则无论有1万客户还是100万客户,其占用的网络主干都是100M,而3~5台服务器硬件的投资不到100万。

​ 如果采用我们专利技术的基于组播的VOD系统,客户还可以享受到廉价的点播服务。由于其采用的是组播协议,无论对于网络主干还是VOD服务器的压力都很小。

组播协议的优势在于当需要将大量相同的数据传输到不通主机时:

  1. 能节省发送数据的主机的系统资源和带宽;

  2. 组播是有选择地复制给又要求的主机;

  3. 组播可以穿越公网广泛传播,而广播则只能在局域网或专门的广播网内部传播;

  4. 组播能节省网络主干的带宽;

(3) MTU

通信术语上的最大传输单元,是指一种通信协议上的某一层上面 所能通过的最大 数据包大小,大小根据字节而定

因为协议数据单元的包头和包尾的长度是固定的,MTU越大,则一个协议数据单元的承载的有效数据就越长,通信效率也越高。MTU越大,传送相同的用户数据所需的数据包个数也越低。

MTU也不是越大越好,因为MTU越大, 传送一个数据包的延迟也越大;并且MTU越大,数据包中 bit位发生错误的概率也越大。

MTU越大,通信效率越高而传输延迟增大,所以要权衡通信效率和传输延迟选择合适的MTU。

以以太网传送IPv4报文为例。MTU表示的长度包含IP包头的长度,如果IP层以上的协议层发送的数据报文的长度超过了MTU,则在发送者的IP层将对数据报文进行分片,在接收者的IP层对接收到的分片进行重组。分片的过程如下:

  1. 首先计算最大的IP包中IP净荷的长度 =MTU-IP包头长度=1500-20= 1480 bytes。

  2. 然后把3008 bytes按照1480 bytes的长度分片,将要分为3片,3008= 1480+1480+48。

  3. 最后发送者将为3个分片分别添加IP包头,组成3个IP包后再发送,3个IP包的长度分别为1500 bytes、1500 bytes和 68 bytes。

(4) UDP4 与 UDP6

包大小

IPv4/v6数据包的最大尺寸取决于MTU(Maximum Transmission Unit, 最大传输单元)与Payload Length字段大小。

  • Payload Length字段有16 位宽,指一个超过 64K 的包含 IP 头部和数据的负载 (65,507 字节 = 65,535 − 8 字节 UDP 头 − 20 字节 IP 头部);通常对于环回地址来说是这样,但这个长度的数据包对于大多数的主机和网络来说不切实际。

  • MTU指的是数据链路层为数据包提供的最大大小。对于任意链路, IPv4所托管的MTU最小为68个字节,推荐为576(典型地,作为拨号上网应用的推荐值),无论它们是完整地还是分块地抵达。

    对于IPv6MTU的最小值是1280个字节,然而,受托管的最小的碎片重组缓冲大小为1500个字节。现今大多数的数据链路层技术(如以太网),都有1500MTU最小值,因而68个字节显得非常小。

要提前知道数据包可能经过的每个链路的 MTU 是不可能的。发送大于接受者MTU大小的数据包将不会起作用,因为数据包会被静默地丢失,而不会通知发送者该包未抵达目的地。

(5) 举例

心跳检测

// ping.ts
import dgram, { createSocket, Socket }  from 'dgram';

const PingBuffer = Buffer.from('ping');

const socket = createSocket('udp4');

socket.bind(8001, () => {
  socket.on('error', (error) => {
    console.log(error);
  })

  socket.on('message', (bufferData, remoteInfo)  => {
    if (bufferData.toString() === 'pang') {
      console.log('pang')
      setTimeout(() => {
        socket.send(PingBuffer, remoteInfo.port);
      }, 1000);
    }
  })

  // 首先发送一个ping进行启动
  socket.send(PingBuffer, 8000);
})
//pang.ts
import dgram, { createSocket, Socket }  from 'dgram';

// 同步创建socket实例
const socket = createSocket('udp4');

const PangBuffer = Buffer.from('pang');

// 绑定8000 端口
socket.bind(8000, () => {

  socket.on('error', (error) => {
    if (error) {
      console.log(error);
    }
  });

  socket.on('message', (bufferData, remoteInfo) => {
    if (bufferData.toString() === 'ping') {
      console.log('ping');
      setTimeout(() => {
        socket.send(PangBuffer, remoteInfo.port);
      }, 1000);
    };
  });

  socket.on('error', (error) => {
    console.log(error);
  })
});

多播通信

//  接受多播消息

import dgram, { createSocket, Socket }  from 'dgram';

const multicastAddr = '224.0.0.114';
const multicastPort = 9034;

const socket = createSocket({
  type: 'udp4',
  reuseAddr: true,
});

socket.on('message', (bufferData) => {
  console.log('多播收到信息', bufferData.toString());
});

// 绑定端口,接受多播信息
socket.bind(multicastPort, () => {
  socket.addMembership(multicastAddr);
});


// 发送消息

import dgram, { createSocket, Socket }  from 'dgram';

const socket = dgram.createSocket('udp4'); //创建udp服务器
const multicastAddr = '224.0.0.114';
const multicastPort = 9034;
const messageBuffer = Buffer.from('大家好啊,我发了一个消息');

socket.on('close',()=>{
    console.log('socket已关闭');
});

socket.on('error',(err)=>{
    console.log(err);
});

socket.on('listening',()=>{
    console.log('socket监听中...');
    //加入组播组
    socket.addMembership(multicastAddr);
    // 设置多播报文存活时间
    socket.setMulticastTTL(128);
    // 设置本机可接受多波信息
    socket.setMulticastLoopback(true);
});

// 发送多播信息
socket.send(messageBuffer, multicastPort, multicastAddr, () => {
  socket.close()
});
@Genluo Genluo added the Node Node相关 label Sep 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Node Node相关
Projects
None yet
Development

No branches or pull requests

1 participant