公众号:暮北林
Q Q 群 : 一起学前端
Javascript/node如何获取本机IP
[TOC]
为什么要获取本机IP
- 以前做的一个Electron的客户端启动Server需要把本机IP显示出来
- 一个基于RK3399的硬件平台的APP进行串口通信另一方需要进行IP校验
如何获取
我们知道JS是客户端语言,单纯的JS是无法获取本机IP的,即使有navigator也只能获取网络的连接类型和监听网络的变化,而无法得知IP信息
window.navagator.connection;
{
downlink: 2.95
downlinkMax: Infinity
effectiveType: "4g"
onchange: null
ontypechange: null
rtt: 50
saveData: false
type: "wifi"
}
复制代码
虽然BOM的API无法获取我们可以采取其他方式
JS获取本机IP
- 通过接口让Server端告知我们的client ip
- 通过WebRTC来获取本机IP
JS通过接口获取
我们可以发送一个Request,在Server端就可以得知我们的IP,连接外网的情况外网Server得知的就是外网IP,只有内网的情况下Server得知的就是我们的内网IP,总之一句话通过接口获取的IP都是相对IP
const express = require("express");
const app = express();
app.set('trust proxy', true);
app.get("/api/get_pubip", (req, res) => {
const { ip } = req;
res.send({code: 0, data: {ip}, msg: "success"});
})
app.listen(3002);
复制代码
- 经过测试在没有经过代理的情况下本机localhost访问返回数据为
// http://localhost:3002/api/get_pubip
{
"code": 0,
"data": {"ip": "::1"},
"msg": "success"
}
复制代码
- 经过nginx代理本机访问的返回数据
// http://localhost:8080/api/get_pubip
{
"code": 0,
"data": {"ip": "::ffff:127.0.0.1"},
"msg": "success"
}
复制代码
- 其他机器(192.168.0.110)访问返回数据
// http://l192.168.0.108/api/get_pubip
{
"code": 0,
"data": {"ip": "::ffff:192.168.0.110"},
"msg": "success"
}
复制代码
WebRTC获取本机IP
WebRTC进行音视频通话时是需要进行媒体协商和网络协商的,在网络协商的过程中就需要知道双方的内外网IP地址,网络协商是需要借助IceServer的帮助的,协商通过后就可以进行后续的UPD通信进行音视频数据的packet发送
function getIPs(callback){
var ip_dups = {};
var RTCPeerConnection = window.RTCPeerConnection
|| window.mozRTCPeerConnection
|| window.webkitRTCPeerConnection;
var useWebKit = !!window.webkitRTCPeerConnection;
var mediaConstraints = {
optional: [{RtpDataChannels: true}]
};
// 这里就是需要的ICEServer了
var servers = {
iceServers: [
{urls: "stun:stun.services.mozilla.com"},
{urls: "stun:stun.l.google.com:19302"},
]
};
var pc = new RTCPeerConnection(servers, mediaConstraints);
function handleCandidate(candidate){
var ip_regex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/
var hasIp = ip_regex.exec(candidate)
if (hasIp) {
var ip_addr = ip_regex.exec(candidate)[1];
if(ip_dups[ip_addr] === undefined)
callback(ip_addr);
ip_dups[ip_addr] = true;
}
}
// 网络协商的过程
pc.onicecandidate = function(ice){
if(ice.candidate) {
handleCandidate(ice.candidate.candidate);
}
};
pc.createDataChannel("");
//创建一个SDP(session description protocol)会话描述协议 是一个纯文本信息 包含了媒体和网络协商的信息
pc.createOffer(function(result){
pc.setLocalDescription(result, function(){}, function(){});
}, function(){});
setTimeout(function(){
var lines = pc.localDescription.sdp.split('\n');
lines.forEach(function(line){
if(line.indexOf('a=candidate:') === 0)
handleCandidate(line);
});
}, 1000);
}
getIPs(function(ip){console.log(ip);});
复制代码
Webrtc得到的IP结果和开源API获取的结果对比
// WebRTC获取的结果 122.4.121.156
// 开源API获取到的IP地址
{
"ip": "122.4.121.156",
"city": 1569,
"region": "中国|0|山东省|青岛市|电信"
}
复制代码
其实Webrtc在媒体协商时获取的IP不止一个,总共有四种类型host
srflx
prflx
relay
candidate四种类型说明
候选类型 | code | 来源 | 传输 | 用途 |
---|---|---|---|---|
主机候选项 | host | 网卡 | 信令服务 | 从网卡中获取本地传输地址 |
服务器反射候选项 | srflx | STUN | 信令服务 | 从发给stun服务的stun检查中获取到的传输地址 |
对象反射候选项 | prflx | ICE代理 | STUN Binding | 从对方ice代理发送stun连接检查中获取的传输地址 |
中继候选者 | relay | TURN | 信令服务器 | 媒体中继服务器的传输地址 |
#### 你会发现Webrtc是无法获取到本机IP | ||||
host获取的一直是xxxxx.local的东东为什么呢? | ||||
请参考文末的mdns 文档 |
||||
### 接下来看看用node如何获取本机ip |
-
- 通过os模块来获取
-
- 通过广播来获取
OS模块获取本机IP
let netDict = os.networkInterfaces();
for (const devName in netDict) {
let netList = netDict[devName];
for (var i = 0; i < netList.length; i++) {
let { address, family, internal,mac } = netList[i],
isvm = isVmNetwork(mac);
if (family === 'IPv4' && address !== '127.0.0.1' && !internal) {
return address;
}
}
}
复制代码
以上方法确实获取本机IP 但是假如你本机装了一个虚拟机获取的ip可能就是你虚拟机的IP了
或者你通过VPN访问网络获取到的有可能是你的VPN分配的IP,所以我们需要根据MAC地址来判断是否是虚拟机/VPN
// 增加一个判断VM虚拟机的方法
// 在上面方法的if中加上这个方法的返回判断就行了
function isVmNetwork (mac) {
// 常见的虚拟网卡MAC地址和厂商
let vmNetwork = [
"00:05:69", //vmware1
"00:0C:29", //vmware2
"00:50:56", //vmware3
"00:1C:42", //parallels1
"00:03:FF", //microsoft virtual pc
"00:0F:4B", //virtual iron 4
"00:16:3E", //red hat xen , oracle vm , xen source, novell xen
"08:00:27", //virtualbox
"00:00:00", // VPN
]
for (let i = 0; i < vmNetwork.length; i++) {
let mac_per = vmNetwork[i];
if (mac.startsWith(mac_per)) {
return true
}
}
return false;
}
复制代码
广播获取本机IP
开启一个广播服务端监听message并发送广播,就可以接受到本机发送的广播,从而获取到发送端(本机)的address信息
const dgram = require("dgram");
const socket = dgram.createSocket("udp4");
const getLocalIp = () => {
return new Promise((resolve, reject) => {
socket.on("error", err => {
reject(err);
socket.close();
})
socket.on("message", (msg, rinfo) => {
let { address, port } = rinfo;
resolve(address);
})
socket.bind(19319, _=> {
socket.setBroadcast(true);
});
var message = new Buffer.from("hello");
socket.send(message, 0, message.length, 19319, "255.255.255.255", (err, bytes)=> {
if (err) {
reject(err);
return
}
})
})
}
getLocalIp().then(res => {
console.log(res);
// 192.168.0.108 这里打印的结果和ifconf得到的结果一致
})
复制代码
你还知道哪些获取IP的方法欢迎留言或进群讨论
参考文档
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END