JS/node获取本机ip的方法

公众号:暮北林

Q Q 群 : 一起学前端
Javascript/node如何获取本机IP

[TOC]

为什么要获取本机IP

  1. 以前做的一个Electron的客户端启动Server需要把本机IP显示出来
  2. 一个基于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

  1. 通过接口让Server端告知我们的client ip
  2. 通过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
    1. 通过os模块来获取
    1. 通过广播来获取

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的方法欢迎留言或进群讨论

参考文档

CSDN
WebRTC cancidate文档
关于虚拟机MAC
mdns
mdns

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享