网络编程 一 设备信息

一 网络设备接口信息

  Java中提供了NetworkInterface供应用访问设备IP地址信息,这是一个被final修饰的类,足见其重要程度:

/**
 * This class represents a Network Interface made up of a name,
 * and a list of IP addresses assigned to this interface.
 * It is used to identify the local interface on which a multicast group
 * is joined.
 *
 * Interfaces are normally known by names such as "le0".
 *
 * @since 1.4
 */
public final class NetworkInterface {
	...
	    NetworkInterface() {
    }

    NetworkInterface(String name, int index, InetAddress[] addrs) {
        this.name = name;
        this.index = index;
        this.addrs = addrs;
    }
	...
}

  NetworkInterface的构造函数都是非public的,那么应用如果需要获取此类对象,可以通过下例中的静态方法获取当前设备中的所有网络接口对象:

public class NetworkInterfaceTest {
    public static void main(String[] args) throws Exception {
        Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces();
    }
}

  NetworkInterface提供了诸多访问网络设备信息,下面列举一些常见的API:

API功能说明
getName()网络设备名称一般网络设备名称是由操作系统设定的,如网卡名称多以eth开头,表示Ethernet以太网的意思,如果系统中存在多个网卡,那么设备名会多出序号后缀,eth0、eth1等,linux、macos可通过ifconfig查看到,windows可通过ipconfig查看
getDisplayName()包含设备厂商和设备具体型号的完整名称是getName返回值的一个扩充版本
getIndex()返回设备索引不同的操作系统返回值不同,未知时返回-1
isUp()此设备接口是否启用
isLoopback()此设备接口是否为回环接口,见附录 一 回环网卡常见于web服务器如tomcat,即使本机没有安装网卡,也可通过localhost(127.0.0.1)来访问tomcat服务
getMTU()获取此设备接口支持的最大的数据包长度,见[附录 二 MTU(#fl2)]这里需要注意的是不建议对MTU进行修改,因为MTU太小,则传输包变多,传输速度变慢,反之虽然传输速度会变快,但是延迟会增高(接收端处理大数据包变慢导致),另外如果MTU值为-1,说明此接口可能被禁用了
isVirtual()返回此接口是否为虚拟接口

二 网络设备子接口信息

  子接口指的是基于现有物理网卡通过软件模拟的虚拟网络接口,目前Linux操作系统支持网络设备子接口,在Java中通过NetworkInterface对象的getSubInterfaces()方法获取子接口信息集合:

public class NetworkInterfaceTest {
    public static void main(String[] args) throws Exception {
        Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces();
		for (NetworkInterface ni : nifs) {
			Enumeration<NetworkInterface> sis = ni.getSubInterfaces();
		}
    }
}

  实际上可以子接口信息依然是NetworkInterface类型,差异点在于getParent方法:

/**
     * Returns the parent NetworkInterface of this interface if this is
     * a subinterface, or {@code null} if it is a physical
     * (non virtual) interface or has no parent.
     *
     * @return The {@code NetworkInterface} this interface is attached to.
     * @since 1.6
     */
    public NetworkInterface getParent() {
        return parent;
    }

三 地址信息

  做网络编程,有两个地址信息是绕不过去的,一个是硬件地址,一个是IP地址。下面分两个小节分别介绍,不会太过详细,读者可以从其他媒体资源搜索相关信息。

3.1 硬件地址

  所谓硬件地址是指硬件设备的唯一标识号,就是常见的MAC地址(Media Access Control),这个标识号是RA(RA是IEEE的注册管理机构)分配的,采用16进制共计48位长度的标识号来描述,其中RA只会分配高3位,低3位由厂商自行分配。

  操作系统都提供查看硬件地址的方法,Windows下可通过ipconfig指令查询,MacOS和Linux可通过ifconfig指令查询,当然并不限于这些手段。

  在Java中访问硬件地址可通过NetworkInterface对象的gethardwareAddress方法实现(我是在MacOS上做的测试):

public class NetworkInterfaceTest {
    public static void main(String[] args) throws Exception {
        Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
        while (nis.hasMoreElements()) {
            NetworkInterface ni = nis.nextElement();
            System.out.println(ni.getName() + ":" + ni.getHardwareAddress());
        }
    }
}

输出结果:
en5:[B@5e481248
utun0:null
awdl0:[B@66d3c617
vboxnet0:[B@63947c6b
en0:[B@2b193f2d
lo0:null

  注意,getHardwareAddress方法返回的是byte[],那么想看到16进制的表示的标识号,需要对返回值进行处理:

public class NetworkInterfaceTest {
    public static void main(String[] args) throws Exception {
        Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
        while (nis.hasMoreElements()) {
            NetworkInterface ni = nis.nextElement();
            System.out.println(ni.getName() + ":" + bytes2HexString(ni.getHardwareAddress()));
        }
    }

    private static String bytes2HexString(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            builder.append(hex.toUpperCase());
            if (i < bytes.length - 1) {
                builder.append("-");
            }
        }
        return builder.toString();
    }
}

输出结果:
utun0:null
awdl0:3E-F0-43-8B-3C-97
vboxnet0:0A-00-27-00-00-00
en0:38-F9-D3-72-E8-47
lo0:null

  通过终端查硬件地址核对下上面的API返回是否正确:

awdl0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1484
	ether 3e:f0:43:8b:3c:97 
	inet6 fe80::3cf0:43ff:fe8b:3c97%awdl0 prefixlen 64 scopeid 0xc 
	nd6 options=201<PERFORMNUD,DAD>
	media: autoselect
	status: active

  可以看到ether行的地址信息和API返回结果一致。

3.2 IP地址

IP是Internet Protocol(网际互连协议)的缩写,是TCP/IP体系中的网络层协议。设计IP的目的是提高网络的可扩展性:一是解决互联网问题,实现大规模、异构网络的互联互通;二是分割顶层网络应用和底层网络技术之间的耦合关系,以利于两者的独立发展。根据端到端的设计原则,IP只为主机提供一种无连接、不可靠的、尽力而为的数据报传输服务。(叶阿勇.计算机网络实验与学习指导.北京:电子工业出版社,2017:69-70)

  IP是每一个网络设备在此网络中的唯一标识。IP地址分为IPv4和IPv6两类,现阶段IPv6已经逐步普及开来,但是IPv4依然是主流。

  和硬件地址一样的,各个操作系统都提供访问IP信息的手段,在Java中通过InetAddress类用于描述IP地址信息。InetAddress和NetworkInterface一样都是不提供构造函数的,NetworkInterface对象的getInetAddress方法可以返回InetAddress对象,因为IP地址分为IPv4和IPv6,因此InetAddress有两个派生类Inet4Address和Inet6Address分别对应不同的IP类型。

  因此我们打开InetAddress的源码会发现它的结构非常简单:

public class InetAddress implements java.io.Serializable {
    
    static final int IPv4 = 1;
    static final int IPv6 = 2;
    static transient boolean preferIPv6Address = false;

    static class InetAddressHolder {
		......
	}
}

class InetAddressImplFactory {

    static InetAddressImpl create() {
        return InetAddress.loadImpl(isIPv6Supported() ?
                                    "Inet6AddressImpl" : "Inet4AddressImpl");
    }

    static native boolean isIPv6Supported();
}

  InetAddress已经为IPv4和IPv6预定义了常量值,并且通过InetAddressImplFactory创建不同类型的派生类实例对象,通过InetAddress提供的API可以便捷的访问IP地址相关信息:

方法名应用场景特殊说明
getCanonicalHostName获取IP地址的全限定名全限定指的是主机名+路径
getHostName获取主机名
getHostAddress获取IP地址此API返回的是IP地址字符串
getAddress获取IP地址此API返回的是byte[]描述的IP地址信息

  以上四个API的具体区别简单测试一下就知道了,这里不再赘述,需要补充的是除了通过NetworkInterface对象来获取InetAddress对象外,InetAddress 本身还提供了一个名为getLocalHost的静态方法,意指返回本机IP,但如果本机存在多个IP,则返回第一个IP信息(数组第一个元素)。

  如果想获取本机的全部IP地址信息,则需要通过getAllByName方法实现,这个API要求传入一个String类型的参数,参数值可以是IP地址、计算机名,甚至是域名。

  最后一个是回环IP地址信息的获取,通过getLoopbackAddress方式实现,此方法返回的依然是类型为InetAddress的对象,再调用此对象的getAddress方法即可访问到回环地址信息了。

  如果我们知道了IP信息,能否反过来获取对应的InetAddress对象?答案是可以的,这里仅列出相关API:

API应用场景特殊说明
static InetAddress getByAddress(byte[] addr)根据IP地址信息获取对应的InetAddress对象
static InetAddress getByAddress(String host, byte[] addr)根据主机名+IP地址信息获取对应的InetAddress对象这里的host参数无任何逻辑作用

四 网络接口地址

  描述网络接口地址的类是InterfaceAddress,这个类在JDK文档中是这样描述的:

此类表示网络接口地址。简言之,对于 IPv4 地址,是指 IP 地址、子网掩码和广播地址。对于 IPv6 地址,是指 IP 地址和网络前缀长度。 

  通过NetworkInterface对象的getInterfaceAddresses方法可返回InterfaceAddress对象集合,InterfaceAddress对象提供的核心API一共就三个:

API应用场景特殊说明
InetAddress getAddress()返回此地址的InetAddress
InetAddress getBroadcast()返回此 InterfaceAddress 广播地址的 InetAddress只有 IPv4 网络具有广播地址,因此对于 IPv6 网络将返回 null
short getNetworkPrefixLength()返回此地址的网络前缀长度网络前缀长度在 IPv4 地址上下文中也称为子网掩码。典型的 IPv4 值是 8 (255.0.0.0)、16 (255.255.0.0) 或 24 (255.255.255.0)。典型的 IPv6 值是 128 (::1/128) 或 10 (fe80::203:baff:fe27:1243/10)

五 其他设备信息

  除前文介绍的API,NetworkInterface还提供了一些比较有用的用于访问API,如判断此设备是否为点对点设备:

public boolean isPointToPoint();

  点对点设备指的是通过拨号或者专用线路实现的点对点通信,是网桥、路由器等设备间最简单的通信连接解决方案。

  判断设备是否支持多播:

public boolean supportsMulticast();

  对应于多播的是单播、广播,简单了说单路广播就是一对一的通信过程,比如两个人聊微信,广播是一对多通信,这个多的范围极大,指网络中的所有主机;多播和广播一样,都是一对多的,但是可以对支持多播类型的IP地址进行分组,仅和指定分组内的主机进行通信,因此也称组播。

  NetworkInterface中还有一些文中尚未介绍的API,受篇幅影响,读者可自行查阅源码,或者从其他媒体途径获取相关信息,总之NetworkInterface是一个设备信息的集大成者,知道它提供的常用API的应用场景就可以了。

附录

一 回环网卡

回环网卡(Loopback adaptor),是一种特殊的网络接口,不与任何实际设备连接,而是完全由软件实现。与回环地址(127.0.0.0/8 或::1/128)不同,回环网卡对系统“显示”为一块硬件。任何发送到该网卡上的数据都将立刻被同一网卡接收到。例子有Linux下的 lo 接口和Windows下的 Microsoft Loopback Interface 网卡。
百度知道

二 MTU

最大传输单元(Maximum Transmission Unit,MTU)用来通知对方所能接受数据服务单元的最大尺寸,说明发送方能够接受的有效载荷大小。
是包或帧的最大长度,一般以字节记。如果MTU过大,在碰到路由器时会被拒绝转发,因为它不能处理过大的包。如果太小,因为协议一定要在包(或帧)上加上包头,那实际传送的数据量就会过小,这样也划不来。大部分操作系统会提供给用户一个默认值,该值一般对用户是比较合适的。
百度知道

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬睡客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值