WIFI管家实现原理:局域网设备扫描

WIFI管家实现原理:局域网设备扫描

(可实现类似腾讯wifi管家的设备扫描功能)

  • 一:效果展示
  • 二:主要原理
  • 三:关键代码展示
  • 四:总结

一:效果展示

搜索界面
搜索结果

二:主要原理

1.ping命令:
使用ping命令轮询本网内可能存在的IP地址(192.168.1.1~192.168.1.255)。如果ping命令返回值正常,记录当前IP地址;

2.MAC地址:
主要通过arp表查询IP所对应的MAC地址,具体可以参考
NetworkInterface. getHardwareAddress ();

3.设备制作商:
通过局域网搜索的设备是无法知道设备名的,不像计算机可以通过nbtstas命令得到计算机名称,所以只能通过mac地址查询对应厂商列表
点击打开:MAC地址对应厂商

三:关键代码展示

1.局域网扫描代码:

package com.yideng168.wifimanager;

import android.text.TextUtils;
import android.util.Log;
import android.widget.TextView;

import org.apache.http.conn.util.InetAddressUtils;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 */
public class ScanDeviceTool {

    private static final String TAG = ScanDeviceTool.class.getSimpleName();

    /**
     * 核心池大小
     **/
    private static final int CORE_POOL_SIZE = 1;
    /**
     * 线程池最大线程数
     **/
    private static final int MAX_IMUM_POOL_SIZE = 255;

    private String mDevAddress;// 本机IP地址-完整
    private String mLocAddress;// 局域网IP地址头,如:192.168.1.
    private Runtime mRun = Runtime.getRuntime();// 获取当前运行环境,来执行ping,相当于windows的cmd
    private Process mProcess = null;// 进程
    private String mPing = "ping -c 1 -w 3 ";// 其中 -c 1为发送的次数,-w 表示发送后等待响应的时间
    private static List<String> mIpList = new ArrayList<String>();// ping成功的IP地址
    private ThreadPoolExecutor mExecutor;// 线程池对象
    private boolean mFlag = true;

    /**
     * TODO<扫描局域网内ip,找到对应服务器>
     *
     * @return void
     */
    public synchronized void scan(final TextView textView) {


        if (mFlag) {
            mIpList.clear();
            mFlag = false;
            mDevAddress = getLocAddress();// 获取本机IP地址
            mLocAddress = getLocAddrIndex(mDevAddress);// 获取本地ip前缀
            Log.d(TAG, "开始扫描设备,本机Ip为:" + mDevAddress);

            if (TextUtils.isEmpty(mLocAddress)) {
                Log.e(TAG, "扫描失败,请检查wifi网络");
                return;
            }

            /**
             * 1.核心池大小 2.线程池最大线程数 3.表示线程没有任务执行时最多保持多久时间会终止
             * 4.参数keepAliveTime的时间单位,有7种取值,当前为毫秒
             * 5.一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响
             * ,一般来说,这里的阻塞队列有以下几种选择:
             */
            mExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_IMUM_POOL_SIZE,
                    2000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(
                    CORE_POOL_SIZE));

            // 新建线程池
            for (int i = 1; i < 255; i++) {// 创建256个线程分别去ping
                final int lastAddress = i;// 存放ip最后一位地址 1-255

                Runnable run = new Runnable() {

                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        String ping = ScanDeviceTool.this.mPing + mLocAddress
                                + lastAddress;
                        String currnetIp = mLocAddress + lastAddress;
                        /*if (mDevAddress.equals(currnetIp)) // 如果与本机IP地址相同,跳过
                            return;*/

                        try {
                            mProcess = mRun.exec(ping);

                            int result = mProcess.waitFor();
                            Log.d(TAG, "正在扫描的IP地址为:" + currnetIp + "返回值为:" + result);
                            if (result == 0) {

                                mIpList.add(currnetIp);

                                Log.d(TAG, "扫描成功,Ip地址为:" + mIpList.size() + "个设备:" + currnetIp + ":" + getHardwareAddress(currnetIp));
                                textView.setText("发现" + mIpList.size() + "个设备");

                            } else {
                                // 扫描失败
                                Log.d(TAG, "扫描失败");
                            }
                        } catch (Exception e) {
                            Log.e(TAG, "扫描异常" + e.toString());
                        } finally {
                            if (mProcess != null)
                                mProcess.destroy();
                        }
                    }
                };

                try {
                    mExecutor.execute(run);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            mExecutor.shutdown();

            while (true) {
                try {
                    if (mExecutor.isTerminated()) {// 扫描结束,开始验证
                        Log.d(TAG, "扫描结束,总共成功扫描到" + mIpList.size() + "个设备.");
                        textView.setText("发现" + mIpList.size() + "个设备");
                        break;
                    }
                } catch (Exception e) {
                    // TODO: handle exception
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * TODO<销毁正在执行的线程池>
     *
     * @return void
     */
    public void destory() {
        if (mExecutor != null) {
            mExecutor.shutdownNow();
            mFlag = true;
        }
    }

    public static List<String> getGetList() {
        return mIpList;
    }


    /**
     * TODO<获取本地ip地址>
     *
     * @return String
     */
    private String getLocAddress() {
        String ipaddress = "";

        try {
            Enumeration<NetworkInterface> en = NetworkInterface
                    .getNetworkInterfaces();
            // 遍历所用的网络接口
            while (en.hasMoreElements()) {
                NetworkInterface networks = en.nextElement();
                // 得到每一个网络接口绑定的所有ip
                Enumeration<InetAddress> address = networks.getInetAddresses();
                // 遍历每一个接口绑定的所有ip
                while (address.hasMoreElements()) {
                    InetAddress ip = address.nextElement();
                    if (!ip.isLoopbackAddress()
                            && InetAddressUtils.isIPv4Address(ip
                            .getHostAddress())) {
                        ipaddress = ip.getHostAddress();
                    }
                }
            }
        } catch (SocketException e) {
            Log.e("", "获取本地ip地址失败");
            e.printStackTrace();
        }

        Log.i(TAG, "本机IP:" + ipaddress);
        return ipaddress;
    }

    /**
     * TODO<获取本机IP前缀>
     *
     * @param devAddress // 本机IP地址
     * @return String
     */
    private String getLocAddrIndex(String devAddress) {
        if (!devAddress.equals("")) {
            return devAddress.substring(0, devAddress.lastIndexOf(".") + 1);
        }
        return null;
    }

    public static String getHardwareAddress(String ip) {
        String MAC_RE = "^%s\\s+0x1\\s+0x2\\s+([:0-9a-fA-F]+)\\s+\\*\\s+\\w+$";
        int BUF = 8 * 1024;
        String hw = "00:00:00:00:00:00";
        try {
            if (ip != null) {
                String ptrn = String.format(MAC_RE, ip.replace(".", "\\."));
                Pattern pattern = Pattern.compile(ptrn);
                BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/net/arp"), BUF);
                String line;
                Matcher matcher;
                while ((line = bufferedReader.readLine()) != null) {
                    matcher = pattern.matcher(line);
                    if (matcher.matches()) {
                        hw = matcher.group(1);
                        break;
                    }
                }
                bufferedReader.close();
            } else {
                Log.e(TAG, "ip is null");
            }
        } catch (IOException e) {
            Log.e(TAG, "Can't open/read file ARP: " + e.getMessage());
            return hw;
        }
        return hw;
    }


}

2.根据IP得到MAC地址

 public static String getHardwareAddress(String ip) {
        String MAC_RE = "^%s\\s+0x1\\s+0x2\\s+([:0-9a-fA-F]+)\\s+\\*\\s+\\w+$";
        int BUF = 8 * 1024;
        String hw = "00:00:00:00:00:00";
        try {
            if (ip != null) {
                String ptrn = String.format(MAC_RE, ip.replace(".", "\\."));
                Pattern pattern = Pattern.compile(ptrn);
                BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/net/arp"), BUF);
                String line;
                Matcher matcher;
                while ((line = bufferedReader.readLine()) != null) {
                    matcher = pattern.matcher(line);
                    if (matcher.matches()) {
                        hw = matcher.group(1);
                        break;
                    }
                }
                bufferedReader.close();
            } else {
                Log.d("MainActivity", "ip is null");
            }
        } catch (IOException e) {
            Log.e("MainActivity", "Can't open/read file ARP: " + e.getMessage());
            return hw;
        }
        return hw;
    }


    /**
     * TODO<获取本地ip地址>
     *
     * @return String
     */
    private String getLocAddress() {
        String ipaddress = "";

        try {
            Enumeration<NetworkInterface> en = NetworkInterface
                    .getNetworkInterfaces();
            // 遍历所用的网络接口
            while (en.hasMoreElements()) {
                NetworkInterface networks = en.nextElement();
                // 得到每一个网络接口绑定的所有ip
                Enumeration<InetAddress> address = networks.getInetAddresses();
                // 遍历每一个接口绑定的所有ip
                while (address.hasMoreElements()) {
                    InetAddress ip = address.nextElement();
                    if (!ip.isLoopbackAddress()
                            && InetAddressUtils.isIPv4Address(ip
                            .getHostAddress())) {
                        ipaddress = ip.getHostAddress();
                    }
                }
            }
        } catch (SocketException e) {
            Log.e("", "获取本地ip地址失败");
            e.printStackTrace();
        }

        return ipaddress;
    }

3.查询mac地址对应的厂商
(对比发现,和腾讯wifi管家显示的名字一致,由此可知,腾讯也是通过此方式得到设备名称)

package com.yideng168.wifimanager;

import android.content.Context;
import android.util.Log;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * Created by ming on 2016/9/5.
 */
public class MacUtils {

    public  static String GetVendor(Context context,String MacAddress){


      /*  R.raw.oui 为raw文件下的mac厂商文件,这里通过查询mac地址把厂商名拿出来*/

        try {
            InputStream is = context.getResources().openRawResource(R.raw.oui);
            InputStreamReader reader = new InputStreamReader(is);
            BufferedReader bufferedReader = new BufferedReader(reader);
            StringBuffer buffer = new StringBuffer("");
            String str;
            while ((str = bufferedReader.readLine()) != null) {

                if(str.contains(MacAddress)){
                    buffer.append(str);
                }
            }
            return buffer.toString();


        }
        catch (Exception e){
            e.printStackTrace();

            return  "未知设备";
        }


    }

}

四:总结

1.本文部分代码参考了一些大神的博客,在此表示感谢;
参考一:局域网扫描
参考二:由IP得到MAC地址

2.写这个项目大概用了一周的时间,一开始只能得到IP地址和MAC地址,而不知如何得到设备制造商名称,所以造成项目停顿了两天;

3.然后,花了大半天时间在网络上搜索如何解决设备名问题,却依然一无所有,网络上涉及到的基本都是如何搜索IP地址然后进行socket通讯,而并没有说到如何获取设备名称,就是有,也是基于双方进行socket通讯后所获取到的;

4.找不到好的方法后,又尝试去反编译“腾讯wifi管家”和*Fing – Network Tools“两款APP,最终还是没找到解决方法,此时差点想放弃;

5.最终,在上班休息之余,刚好看到在线MAC地址查询厂商的资料,然后整个人豁然开朗,一下子就找到了解决设备名称的方法

6.总结,这次的小项目随意经历了一些波折,但最终效果还是实现了,同时这次的经验也告诉我,有些事情,还是需要坚持,别人能能做到的,你也一样可以做到,不气馁,才是成功的根本!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值