Node.js dgram模块实现UDP通信示例代码
1、什么是UDP?
这里简单介绍下,UDP,即用户数据报协议,一种面向无连接的传输层协议,提供不可靠的消息传送服务。UDP协议使用端口号为不同的应用保留其各自的数据传输通道,这一点非常重要。与TCP相比,占用资源更少,传输速度更快。
2、了解UDP单播、广播和组播
单播:向一个单播地址发送UDP数据报时,数据报只能被指定的IP主机接收,同一子网下的其它主机都不会接收该数据报。单播过程(假设子网地址:192.168.10,该子网下有两台主机:192.168.10.2,192.168.10.3,向192.168.10.2发送数据报)
1.向主机192.168.10.2的8060端口发送数据报,该IP数据报目标IP在以太网中被ARP解析成MAC地址,然后将此MAC地址设置为目标地址发出去。
2.该子网下的主机以太网接口看到该数据帧时,对比自己的MAC地址,如果不一样,则忽略该数据帧。
3.直到192.168.10.2主机看到该数据帧时,发现MAC一致,则读入该数据帧,然后在IP层对比目标IP和本机IP,在UDP层对比端口号是否一致,最后接收该数据帧。
广播:向一个广播地址发送UDP数据报,该广播网络下的所有主机都会受到影响,主机根据端口号来判断是否丢弃该数据。广播过程(假设子网地址:192.168.10,则该网络的广播地址为:192.168.10.255,该子网下有两台主机:192.168.10.2,192.168.10.3,向192.168.10.255发送数据报):
1.向192.168.10.255主机端口为8060发送数据报,,该IP数据报目标IP在以太网中被ARP解析成MAC地址,然后将此MAC地址设置为目标地址发出去。
2.192.168.10.2和192.168.10.3的主机接口都会和此广播地址匹配成功,直到该数据报进入传输层时根据端口是否匹配判断是否丢弃该数据,端口为8060的会保留该数据,否则将数据丢弃。
组播:向一个组播地址发送UDP数据报,该组网络下的所有主机都会受到影响,主机根据端口号来判断是否丢弃该数据。组播过程原理与广播类似。
3、dgram模块API介绍
该模块用来创建UDP数据报socket,继承了EventEmitter,该模块的API如下所示:
3、UDP单播实现
单播的原理上面已经说了,这里便不再叙述。
服务端:
const dgram = require('dgram'); const server = dgram.createSocket('udp4'); server.on('close',()=>{ console.log('socket已关闭'); }); server.on('error',(err)=>{ console.log(err); }); server.on('listening',()=>{ console.log('socket正在监听中...'); }); server.on('message',(msg,rinfo)=>{ console.log(`receive message from ${rinfo.address}:${rinfo.port}`); server.send('exit',rinfo.port,rinfo.address) }); server.bind('8060');
客户端:
const dgram = require('dgram'); const client = dgram.createSocket('udp4'); client.on('close',()=>{ console.log('socket已关闭'); }); client.on('error',(err)=>{ console.log(err); }); client.on('message',(msg,rinfo)=>{ if(msg=='exit') client.close(); console.log(`receive message from ${rinfo.address}:${rinfo.port}`); }); client.send(`hello`,8060,'1.1.1.69');
3、UDP广播实现
要实现UDP广播,就必须知道广播地址,广播地址如何计算呢?可以利用ipconfig(windows)或ifconfig(*unix)得到ip地址和mask掩码地址,然后利用ip地址与mask子网掩码做与操作得到网络地址,网络地址中主机位都为1就是广播地址,举个例子:
IP:149.88.160.58
mask:11111111.11111111.11000000.00000000
IP&MASK得到网络地址:149.88.128.0/18
网络号就是主机位全为0,即149.88.10000000.00000000 即149.88.128.0
广播地址就是主机位全为1,即149.88.10111111.11111111即149.88.191.255
接着实现UDP广播,我机子的IP为1.1.1.69,掩码为255.255.255.0,因此广播地址为1.1.1.255,服务端向广播地址的8061端口发送数据报,客户端绑定本机的8061端口,如下所示:
服务端:
const dgram = require('dgram'); const server = dgram.createSocket('udp4'); server.on('close',()=>{ console.log('socket已关闭'); }); server.on('error',(err)=>{ console.log(err); }); server.on('listening',()=>{ console.log('socket正在监听中...'); server.setBroadcast(!0);//开启广播 server.setTTL(128); server.send('大家好啊,我是服务端.',8061,'1.1.1.255'); }); server.on('message',(msg,rinfo)=>{ console.log(`receive message from ${rinfo.address}:${rinfo.port}`); }); server.bind('8060','1.1.1.69');
客户端:
const dgram = require('dgram'); const client = dgram.createSocket('udp4'); client.on('close',()=>{ console.log('socket已关闭'); }); client.on('error',(err)=>{ console.log(err); }); client.on('listening',()=>{ console.log('socket正在监听中...'); }); client.on('message',(msg,rinfo)=>{ console.log(`receive message from ${rinfo.address}:${rinfo.port}:${msg}`); }); client.bind(8061,'1.1.1.69');
3、UDP组播实现
组播地址是实现UDP组播的关键,因此了解组播地址是重点。什么是组播地址?IANA将D类地址(224.0.0.0-239.255.255.255)分配给IP组播,用来标识一个IP组播组,由IGMP(组管理协议)协议维护组成员关系,其中:
224.0.0.0~224.0.0.255为永久组地址,地址224.0.0.0保留不做分配,其它地址供路由协议使用;
224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。
向组播地址发送数据报,只有该组成员才会接收此数据报。
dgram模块的addMembership()方法将socket加入组播组中,dropMembership()方法退出组播组。下面以组播地址224.100.100.100来实现UDP组播,如下所示:
服务端:
const dgram = require('dgram'); const server = dgram.createSocket('udp4'); const multicastAddr = '224.100.100.100'; server.on('close',()=>{ console.log('socket已关闭'); }); server.on('error',(err)=>{ console.log(err); }); server.on('listening',()=>{ console.log('socket正在监听中...'); server.addMembership(multicastAddr); server.setMulticastTTL(128); setInterval(()=>{ sendMsg(); },1500); }); server.on('message',(msg,rinfo)=>{ console.log(`receive message from ${rinfo.address}:${rinfo.port}`); }); function sendMsg(){ server.send('大家好啊,我是服务端.',8061,multicastAddr); } server.bind('8060','1.1.1.69');
客户端:
const dgram = require('dgram'); const client = dgram.createSocket('udp4'); const multicastAddr = '224.100.100.100'; client.on('close',()=>{ console.log('socket已关闭'); }); client.on('error',(err)=>{ console.log(err); }); client.on('listening',()=>{ console.log('socket正在监听中...'); client.addMembership(multicastAddr); }); client.on('message',(msg,rinfo)=>{ console.log(`receive message from ${rinfo.address}:${rinfo.port}:${msg}`); }); client.bind(8061,'1.1.1.69');
另外UDP内网穿透相关的内容,大家也可自行了解一下。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
- 克隆虚拟机的注意点
- keepalived+nginx搭建高可用(注意点)
- 我的WCF之旅(10):如何在WCF进行Exception Handling
- 安装nginx出现的问题
- 18.11 LVS DR模式搭建
- Linux基础(day64)
- 我的WCF之旅(9):如何在WCF中使用tcpTrace来进行Soap Trace
- 物联网设备已沦陷,咖啡机也不能例外
- 我的WCF之旅(13):创建基于MSMQ的Responsive Service
- 开发自己的Data Access Application Block[上篇]
- 18.9/18.10 LVS NAT模式搭建
- 谈谈WCF中的Data Contract (1):Data Contract Overview
- Linux基础(day66)
- 字符串的驻留(String Interning)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python -二叉树 创建与遍历算法(很详细,转自国外教程)
- APP流量来源追踪方式——Android篇
- 从浏览器输入网址回车到看到页面过程到底经历了什么?
- Flutter Bloc 官方文档(BlocBuilder翻译)
- OpenGL ES 3.0 | 统一变量和属性的概念与(在程序中的)获取流程、统一变量缓冲区对象详解、std140块规范、用 命名统一变量块 建立 统一变量缓冲区对象 的流程 和 相关API 和...
- 深入浅出SVM(PART III)
- 高频原题——LeetCode题目8:字符串转换整数 (atoi)
- 不转字符串判断——LeetCode题目9:回文数
- 关于双指针的简单理解
- 面试题噩梦之一——LeetCode题目10:正则表达式匹配
- 什么时候触发GC
- 找找数学上的规律——LeetCode题目11:盛最多水的容器
- Java学习笔记, 不断更新
- 这题真是送分——LeetCode题目12:整数转罗马数字
- 该了解一波了!零基础入门Nginx