再做前端页面的时候,想获取本地的ip地址,可能是为了和服务器通信,可能是为了展示,无论哪种,目的要获取js get IP
相信大家搜到最多的是以下的js方法(都是超来抄去),我来讲讲我遇到的坑,方法虽然很笨,只为了解决问题。
这里要强调下,我们这里是获取本机IP,局域网或者独立电脑,木有联网,若是借用网络接口,如搜狐、新浪等接口地址不行,因为木有网络。就是那种我一打开网页就直接获取了。
方法一:
getUserIP(onNewIP) { // onNewIp-新IP的侦听器函数
//兼容 firefox和 chrome浏览器
var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
//定义参数
var pc = new myPeerConnection({
iceServers: []
}),
noop = function() {},
localIPs = {},
ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g,
key;
function iterateIP(ip) {
if (!localIPs[ip]) onNewIP(ip);
localIPs[ip] = true;
}
//create a bogus data channel
pc.createDataChannel("");
// create offer and set local description
pc.createOffer().then(function(sdp) {
sdp.sdp.split('\n').forEach(function(line) {
if (line.indexOf('candidate') < 0) return;
line.match(ipRegex).forEach(iterateIP);
});
pc.setLocalDescription(sdp, noop, noop);
}).catch(function(reason) {
// An error occurred, so handle the failure to connect
});
//sten for candidate events
pc.onicecandidate = function(ice) {
if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
ice.candidate.candidate.match(ipRegex).forEach(iterateIP);
};
// console.log("ip1:"+ip);
},
方法二:
getUserIP(){
var $this = this
var RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
if (RTCPeerConnection) (function () {
var rtc = new RTCPeerConnection({iceServers:[]});
if (1 || window.mozRTCPeerConnection) {
rtc.createDataChannel('', {reliable:false});
};
rtc.onicecandidate = function (evt) {
if (evt.candidate) grepSDP("a="+evt.candidate.candidate);
};
rtc.createOffer(function (offerDesc) {
grepSDP(offerDesc.sdp);
rtc.setLocalDescription(offerDesc);
}, function (e) { console.warn("offer failed", e); });
var addrs = Object.create(null);
addrs["0.0.0.0"] = false;
var currentIP2
function updateDisplay(newAddr) {
if (newAddr in addrs) return;
else addrs[newAddr] = true;
var displayAddrs = Object.keys(addrs).filter(function (k) { return addrs[k]; });
for(var i = 0; i < displayAddrs.length; i++){
if(displayAddrs[i].length > 16){
displayAddrs.splice(i, 1);
i--;
}
}
// return displayAddrs[0];
alert("ip" + displayAddrs[0]);
}
function grepSDP(sdp) {
var hosts = [];
sdp.split('\r\n').forEach(function (line, index, arr) {
if (~line.indexOf("a=candidate")) {
var parts = line.split(' '),
addr = parts[4],
type = parts[7];
if (type === 'host') updateDisplay(addr);
} else if (~line.indexOf("c=")) {
var parts = line.split(' '),
addr = parts[2];
updateDisplay(addr);
}
});
}
})()
}
还有好几种,就不一一列举了。
以上的方法我验证是可以的,如何调用:
getUserIP(function(ip){
alert("Got IP! :" + ip);
});
其实这样就可以调用出来了,但是这里我只想让这个IP变为全局,我我需要访问服务端的时候可以用(服务端和前端放在一台服务器,不能分开);
这里大家想说那直接用127.0.0.1不就可以了,问题是与后端交互中,发现无法通信,必须写服务端的地址。那我们两放在一起,我只要获取我本机地址即可。
那么问题来了一:
根据上面的方法,发现必须得使用回调getUserIP才能获取ip,要是再这个回调方法外得IP无效,例如:
var getIP
getUserIP(function(ip){
alert("Got IP! :" + ip);
getIP = ip;
});
console.log("外部打印IP:",this.getIP)
这个你就会发现,再外部打印是为空得,这个只能再这个回调方法使用,也是非常麻烦之一;
有人会想到VUE中使用main.js得prototype全局方法,详情:网站整改公告 - 博客园团队 - 博客园
先把上面的方法写在一个'../src/util/main.js'下抛出;
import Vue from "vue";
import App from "./views/App";
import oIp1 from '../src/util/main';
Vue.config.productionTip = false;
Vue.prototype.$getIP=oIp1
// console.log(configN);
//测试该方法是否实现,方法再oIp1里
oIp1.getUserIP((ip)=>{
debugger;
console.log('newIp=>',ip);
});
new Vue({
});
再任何地方就可以调用$getIP了。很遗憾,不知道我得框架里写了多个new Vue({ }); 这种方法对我来说不行,(不知道哪个人写得框架),其实不一定能用,接着看下面
问题二:
我们发现回调获取ip时获取不到,打印出来ip时空的,这是为什么?
大家注意下这些方法的判断if (line.indexOf('candidate') < 0),注意:candidate这个我打印出来发现木有此项,换成c=IN就有0.0.0.0,这个到底是什么原因,我以为是我电脑连接wifi的和连接网线的(之前连接网线可以)的区别?然后找根网线再试试发现不行,那么估计是环境问题了(要详细网络大神,他们说亲试可以,肯定是可以的)。
针对这个单独百度查了candidate项,才发现是浏览器默认是被限制了,就是为了安全,浏览器不允许你获取地址。参考:获取本机ip失败,webrtc candidate xxx.local mDNS ip地址问题_梦想身高1米8的博客-CSDN博客
谷歌解决办法
浏览器输入chrome://flags/#enable-webrtc-hide-local-ips-with-mdns
将Anonymize local IPs exposed by WebRTC置为disabled
火狐解决办法
浏览器输入about:config
将media.peerconnection.ice.obfuscate_host_addresses置为false
然后再运行,就有地址弹出了。
问题三:
问题又绕回来,上面的方法只能做回调方法里使用,不能做全局,很多人应该可以想到,写个方法接收里面的参数,例如:
var getIP
getUserIP(function(ip){
alert("Got IP! :" + ip);
test(ip)//调用这个方法,把这个ip值传出去
});
//写一个方法
test(ip){
getIP = ip;
};
console.log("外部打印IP:",this.getIP)
呵呵,不好意思,上面的方法打印出来的getIp还是空的,获取不到。
我又把方法优化了下,方法三:
getUserIP(){
if(typeof window != 'undefined'){
var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
if (RTCPeerConnection) (()=>{
var rtc = new RTCPeerConnection()
rtc.createDataChannel(''); //创建一个可以发送任意数据的数据通道
rtc.createOffer( offerDesc => { //创建并存储一个sdp数据
rtc.setLocalDescription(offerDesc)
}, e => { console.log(e)})
rtc.onicecandidate =(evt) => { //监听candidate事件
if (evt.candidate) {
console.log('evt:',evt.candidate)
let ip_rule = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/
var ip_addr = ip_rule.exec(evt.candidate.candidate)[1]
console.log('ip_addr:',ip_addr) //打印获取的IP地址
this.test(ip_addr);//调用方法把ip地址的值传出去
}}
})()
else{console.log("没有找到")}
}
},
这个方法一看也是需要回调,但是我们不想回调;大家发现方法一有参数,不回调木有办法使用,不然你传什么值,而方法二和三,直接执行即可
getUserIP();//直接执行(运行)该方法
只要再方法二和方法三里调用外部方法,把ip参数传过去
var getIP
test(ip){
console.log("把ip地址传过来",ip)
//这里就可以把ip传给全局的变量了
getIP = ip;
}
console.log("外部打印IP:",getIP)
那样你爱怎么用这个getIp就怎么用。
最后。。。最后。。。还有更残酷的事实:
我们获取IP的目的就是把web前端打包给客户,但不知道客户的IP,不可能写死,和服务端再一台电脑中部署,127.0.0.1不能与服务端交互,那怎么办?
发现localhost竟然可以通信,这样直接写死就行,放在哪台电脑都可以用,弄了这么久,这127.0.0.1和localhost区别大家自行百度吧!坑大了。