bubifengyun 2016-06-14 20:41:12 5001次浏览 1条回复 0 3 0

参考网页:

代码实现

thanks to WebRTC, it is very easy to get local IP in WebRTC supported browsers( at least for now). I have modified the source code, reduced the lines, not making any stun requests since you only want Local IP not the Public IP, the below code works in latest Firefox and Chrome, just run the snippet and check for yourself:

function findIP(onNewIP) { //  onNewIp - your listener function for new IPs
  var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
  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 ipIterate(ip) {
    if (!localIPs[ip]) onNewIP(ip);
    localIPs[ip] = true;
  }
  pc.createDataChannel(""); //create a bogus data channel
  pc.createOffer(function(sdp) {
    sdp.sdp.split('\n').forEach(function(line) {
      if (line.indexOf('candidate') < 0) return;
      line.match(ipRegex).forEach(ipIterate);
    });
    pc.setLocalDescription(sdp, noop, noop);
  }, noop); // create offer and set local description
  pc.onicecandidate = function(ice) { //listen for candidate events
    if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
    ice.candidate.candidate.match(ipRegex).forEach(ipIterate);
  };
}

var ul = document.createElement('ul');
ul.textContent = 'Your IPs are: '
document.body.appendChild(ul);

function addIP(ip) {
  console.log('got ip: ', ip);
  var li = document.createElement('li');
  li.textContent = ip;
  ul.appendChild(li);
}

findIP(addIP);

一个简单的例子

<html>
<body>
</body>
<script type="text/javascript">
function findIP(onNewIP) { //  onNewIp - your listener function for new IPs
  var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
  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 ipIterate(ip) {
    if (!localIPs[ip]) onNewIP(ip);
    localIPs[ip] = true;
  }
  pc.createDataChannel(""); //create a bogus data channel
  pc.createOffer(function(sdp) {
    sdp.sdp.split('\n').forEach(function(line) {
      if (line.indexOf('candidate') < 0) return;
      line.match(ipRegex).forEach(ipIterate);
    });
    pc.setLocalDescription(sdp, noop, noop);
  }, noop); // create offer and set local description
  pc.onicecandidate = function(ice) { //listen for candidate events
    if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
    ice.candidate.candidate.match(ipRegex).forEach(ipIterate);
  };
}

var ul = document.createElement('ul');
ul.textContent = 'Your IPs are: '
document.body.appendChild(ul);

function addIP(ip) {
  console.log('got ip: ', ip);
  var li = document.createElement('li');
  li.textContent = ip;
  ul.appendChild(li);
}

findIP(addIP);
</script>
</html>

功能设想

为了在 Yii 2.0 中使用该功能,很方便的使用,最好做成插件的形式。代码可否简化?能跟 PHP 很好的对接吗? 先跟 PHP 对接,然后考虑放到 Yii 2.o 中。使用不断试错的方法来实现。

1、怎么把检测到的 IP 地址传回来?

第一想法是采用 ajax 的方式,在服务器端接收该 IP 地址。问题是把 ajax 的代码放在哪里呢?其中的 onNewIP 是个不错的突破口。 下面开始对代码进行第一次试错。

function findIP(onNewIP) { //  onNewIp - your listener function for new IPs
// 略
}

function writeIP(ip) {
  document.write(ip);
  document.close();
}

findIP(writeIP);

解释

  • 本打算使用 ajax 的方式,但是还需要一个回调函数,太复杂了。
  • 在 widget 里没有找到加入 actionCallBack 的方法。
  • 遂采用直接打印输出的方式回传。
2、做成 InputWidget 类

参考:

下面是怎么修改了。

3、中间过程不说了,下面把可以运行的代码贴下来。

我使用的高级模板,把这些代码放在了三个文件里。分别是

common\
    assets\
        WebRTCLocalIPAsset.php
        js\
            webrtclocalip.js
    components\
        WebRTCLocalIPWidget.php

三个文件的代码如下

./common/assets/js/webrtclocalip.js

var id = 'webrtclocalip';

function setoptions(options){
    id=options.id;
}

function findIP(onNewIP) { //  onNewIp - your listener function for new IPs
    var promise = new Promise(function (resolve, reject) {
        try {
            var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
            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 ipIterate(ip) {
                if (!localIPs[ip]) onNewIP(ip);
                localIPs[ip] = true;
            }
            pc.createDataChannel(""); //create a bogus data channel
            pc.createOffer(function (sdp) {
                sdp.sdp.split('\n').forEach(function (line) {
                    if (line.indexOf('candidate') < 0) return;
                    line.match(ipRegex).forEach(ipIterate);
                });
                pc.setLocalDescription(sdp, noop, noop);
            }, noop); // create offer and set local description

            pc.onicecandidate = function (ice) { //listen for candidate events
                if (ice && ice.candidate && ice.candidate.candidate && ice.candidate.candidate.match(ipRegex)) {
                    ice.candidate.candidate.match(ipRegex).forEach(ipIterate);
                }
                resolve("FindIPsDone");
                return;
            };
        }
        catch (ex) {
            reject(Error(ex));
        }
    });// New Promise(...{ ... });
    return promise;
};

//This is the callback that gets run for each IP address found
function foundNewIP(ip) {
    if (typeof window.ipAddress === 'undefined')
    {
        window.ipAddress = ip;
    }
    else
    {
        window.ipAddress += " - " + ip;
    }
}

function load() {

    //This is How to use the Waitable findIP function, and react to the
    //results arriving
    var ipWaitObject = findIP(foundNewIP);        // Puts found IP(s) in window.ipAddress
    ipWaitObject.then(
        function (result) {
            // for not hidden
            document.getElementById(id).textContent = window.ipAddress;
            // for hidden
            document.getElementById(id).value = window.ipAddress;
        },
        function (err) {
            alert ("无法找到 IP 地址" + err)
        }
    );
}

./common/assets/WebRTCLocalIPAsset.php

<?php

namespace common\assets;

use yii\web\AssetBundle;

class WebRTCLocalIPAsset extends AssetBundle
{
    public $sourcePath = '@common/assets';
    public $css = [];
    public $js = [
        'js/webrtclocalip.js',
    ];
}

./common/components/WebRTCLocalIPWidget.php

<?php
namespace common\components;

use yii\widgets\InputWidget;
use Yii;
use yii\helpers\Html;
use yii\helpers\Url;
use common\assets\WebRTCLocalIPAsset;

class WebRTCLocalIPWidget extends InputWidget
{
    public $localip;
    public $hidden = true;
    public $attributes;
    public $id = 'webrtclocalip';
    public $name = 'webrtclocalip';

    public function init()
    {
        parent::init();
    }

    public function run()
    {
        $view = $this->getView();
        $this->attributes['id'] = $this->id;
        if ($this->hasModel()) {
            if ($this->hidden) {
                $input = Html::activeHiddenInput($this->model, $this->attribute, $this->attributes);
            } else {
                $input = Html::activeTextInput($this->model, $this->attribute, $this->attributes);
            }
        } else {
            if ($this->hidden) {
                $input = Html::hiddenInput($this->name, '', $this->attributes);
            } else {
                $input = Html::textInput($this->name, '', $this->attributes);
            }
        }
        echo $input;
        WebRTCLocalIPAsset::register($view);
        $view->registerJs("setoptions({id: '" . $this->id . "'});load();");
    }
}

4、用法

类似一般的 widget

        <?= $form
            ->field($model, 'ip')
            ->label(false)
            ->widget(WebRTCLocalIPWidget::className(), [
                'id' => 'loginform-ip',
                'hidden' => true,
            ]) ?>

解释

  • WebRTCLocalIPWidget::className() 需要使用 use 语句
  • 'id' => 'loginform-ip', 需要查看网页代码检测该插件的 id,然后写入。这个暂时没有找到好的解决方案。

后期

正在把他作为一个插件,敬请期待

https://github.com/bubifengyun/yii2-webrtc-get-client-local-ip

觉得很赞
您需要登录后才可以回复。登录 | 立即注册