Working with Network Interfaces in Java – 在Java中使用网络接口

最后修改: 2016年 11月 4日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll focus on network interfaces and how to access them programmatically in Java.

在这篇文章中,我们将重点讨论网络接口以及如何在Java中以编程方式访问它们。

Simply put, a network interface is the point of interconnection between a device and any of its network connections.

简单地说,网络接口是一个设备与其任何网络连接之间的互连点

In everyday language, we refer to them by the term Network Interface Cards (NICs) – but they don’t all have to be of hardware form.

在日常用语中,我们用网络接口卡(NIC)一词来指代它们–但它们不一定都是硬件形式的。

For example, the popular localhost IP 127.0.0.1, which we use a lot in testing web and network applications is the loopback interface – which is not a direct hardware interface.

例如,我们在测试网络和网络应用时经常使用的流行的localhost IP127.0.0.1是环回接口–它不是一个直接的硬件接口。

Of course, systems often have multiple active network connections, such as wired ethernet, WIFI, Bluetooth, etc.

当然,系统通常有多个活跃的网络连接,如有线以太网、WIFI、蓝牙等。

In Java, the main API we can use to interact directly with them is the java.net.NetworkInterface class. And so, to get started quickly, let’s import the full package:

在Java中,我们可以用来与它们直接互动的主要API是java.net.NetworkInterface类。因此,为了快速入门,让我们导入完整的包。

import java.net.*;

2. Why Access Network Interfaces?

2.为什么要访问网络接口?

Most Java programs won’t probably interact with them directly; there are however special scenarios when we do need this kind of low-level access.

大多数Java程序可能不会与它们直接交互;但在一些特殊情况下,我们确实需要这种低层次的访问。

The most outstanding of these is where a system has multiple cards and you would like to have the freedom to choose a specific interface to use a socket with. In such a scenario, we usually know the name but not necessarily the IP address.

其中最突出的是,一个系统有多张卡,而你希望有自由选择一个特定的接口来使用套接字。在这种情况下,我们通常知道名称,但不一定知道IP地址。

Normally, when we want to make a socket connection the to a specific server address:

通常情况下,当我们想建立一个套接字连接到一个特定的服务器地址。

Socket socket = new Socket();
socket.connect(new InetSocketAddress(address, port));

This way, the system will pick a suitable local address, bind to it and communicate to the server through its network interface. However, this approach does not allow us to choose our own.

这样,系统将挑选一个合适的本地地址,与之绑定,并通过其网络接口与服务器通信。然而,这种方法不允许我们选择自己的。

We will make an assumption here; we don’t know the address but we know the name. Just for demonstration purposes, let’s assume we want the connection over the loopback interface, by convention, its name is lo, at least on Linux and Windows systems, on OSX it is lo0:

我们在这里做一个假设;我们不知道地址,但我们知道名字。只是为了演示,让我们假设我们想通过回环接口进行连接,按照惯例,它的名字是lo,至少在Linux和Windows系统上是这样,在OSX上是lo0

NetworkInterface nif = NetworkInterface.getByName("lo");
Enumeration<InetAddress> nifAddresses = nif.getInetAddresses();

Socket socket = new Socket();
socket.bind(new InetSocketAddress(nifAddresses.nextElement(), 0));
socket.connect(new InetSocketAddress(address, port));

So we retrieve the network interface attached to lo first, retrieve the addresses attached to it, create a socket, bind it to any of the enumerated addresses which we don’t even know at compile time and then connect.

所以我们首先检索附属于lo 的网络接口,检索附属于它的地址,创建一个套接字,将其绑定到任何一个我们在编译时甚至不知道的列举的地址,然后进行连接。

A NetworkInterface object contains a name and a set of IP addresses assigned to it. So binding to any of these addresses will guarantee communication through this interface.

一个NetworkInterface对象包含一个名称和一组分配给它的IP地址。因此,与任何这些地址的绑定将保证通过这个接口进行通信。

This does not really say anything special about the API. We know that if we want our local address to be localhost, the first snippet would suffice if we just added the binding code.

这并没有真正说明关于API的任何特殊情况。我们知道,如果我们希望我们的本地地址是localhost,那么如果我们只是添加绑定代码,第一个片段就足够了。

Additionally, we would never really have to go through all the several steps since localhost has one well-known address, 127.0.0.1 and we can easily bind the socket to it.

此外,我们永远不会真正经历所有的几个步骤,因为localhost有一个众所周知的地址,127.0.0.1,我们可以很容易地将套接字绑定到它。

However, in your case, lo could perhaps have represented other interfaces like Bluetooth – net1, wireless network – net0 or ethernet – eth0. In such cases, you would not know the IP address at compile time.

然而,在你的案例中,lo可能代表其他接口,如蓝牙 – net1、无线网络 – net0或以太网 – eth0。在这种情况下,你在编译时不会知道IP地址。

3. Retrieving Network Interfaces

3.检索网络接口

In this section, we will explore the other available APIs for retrieving the available interfaces. In the previous section, we saw just one of these approaches; the getByName() static method.

在这一节中,我们将探索其他可用的API来检索可用的接口。在上一节中,我们只看到了这些方法中的一种;getByName()静态方法。

It’s worth noting that the NetworkInterface class does not have any public constructors, so we are of course not able to create a new instance. Instead, we’re going to use the available APIs to retrieve one.

值得注意的是,NetworkInterface类没有任何公共构造函数,所以我们当然不能创建一个新的实例。相反,我们将使用可用的API来获取一个实例。

The API we looked at so far is used to search a network interface by the specified name:

到目前为止,我们所看的API是用来按指定的名称搜索一个网络接口。

@Test
public void givenName_whenReturnsNetworkInterface_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertNotNull(nif);
}

It returns null if none is for the name:

如果没有这个名字,它返回null

@Test
public void givenInExistentName_whenReturnsNull_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("inexistent_name");

    assertNull(nif);
}

The second API is getByInetAddress(), it also requires that we provide a known parameter, this time we can provide the IP address:

第二个API是getByInetAddress(),它也要求我们提供一个已知参数,这次我们可以提供IP地址。

@Test
public void givenIP_whenReturnsNetworkInterface_thenCorrect() {
    byte[] ip = new byte[] { 127, 0, 0, 1 };

    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getByAddress(ip));

    assertNotNull(nif);
}

Or name of the host:

或者主机的名称。

@Test
public void givenHostName_whenReturnsNetworkInterface_thenCorrect()  {
    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getByName("localhost"));

    assertNotNull(nif);
}

Or if you are specific about localhost:

或者如果你对localhost有特殊要求。

@Test
public void givenLocalHost_whenReturnsNetworkInterface_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getLocalHost());

    assertNotNull(nif);
}

Another alternative is also to explicitly use the loopback interface:

另一种方法是明确使用回环接口。

@Test
public void givenLoopBack_whenReturnsNetworkInterface_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByInetAddress(
      InetAddress.getLoopbackAddress());

    assertNotNull(nif);
}

The third approach which has only been available since Java 7 is to get a network interface by its index:

第三种方法是从Java 7开始才有的,就是通过索引来获得一个网络接口。

NetworkInterface nif = NetworkInterface.getByIndex(int index);

The final approach involves using the getNetworkInterfaces API. It returns an Enumeration of all available network interfaces in the system. It’s upon us to retrieve the returned objects in a loop, the standard idiom uses a List:

最后一种方法是使用getNetworkInterfaces API。它返回系统中所有可用的网络接口的Enumeration。我们应该在一个循环中检索返回的对象,标准的习惯做法是使用一个List

Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();

for (NetworkInterface nif: Collections.list(nets)) {
    //do something with the network interface
}

4. Network Interface Parameters

4.网络接口参数

There is a lot of valuable information we can get from one after retrieving its object. One of the most useful is the list of IP addresses assigned to it.

在检索其对象后,我们可以从其中得到很多有价值的信息。其中一个最有用的是分配给它的IP地址列表

We can get IP addresses using two APIs. The first API is getInetAddresses(). It returns an Enumeration of InetAddress instances which we can process as we deem fit:

我们可以使用两个API来获得IP地址。第一个API是getInetAddresses()。它返回一个InetAddress实例的枚举,我们可以按我们认为合适的方式处理。

@Test
public void givenInterface_whenReturnsInetAddresses_thenCorrect()  {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    Enumeration<InetAddress> addressEnum = nif.getInetAddresses();
    InetAddress address = addressEnum.nextElement();

    assertEquals("127.0.0.1", address.getHostAddress());
}

The second API is getInterfaceAddresses(). It returns a List of InterfaceAddress instances which are more powerful than InetAddress instances. For example, apart from the IP address, you may be interested in the broadcast address:

第二个API是getInterfaceAddresses()。它返回一个InterfaceAddress实例的List,它比InetAddress实例更强大。例如,除了IP地址之外,你可能对广播地址感兴趣。

@Test
public void givenInterface_whenReturnsInterfaceAddresses_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    List<InterfaceAddress> addressEnum = nif.getInterfaceAddresses();
    InterfaceAddress address = addressEnum.get(0);

    InetAddress localAddress=address.getAddress();
    InetAddress broadCastAddress = address.getBroadcast();

    assertEquals("127.0.0.1", localAddress.getHostAddress());
    assertEquals("127.255.255.255",broadCastAddress.getHostAddress());
}

We can access network parameters about an interface beyond the name and IP addresses assigned to it. To check if it is up and running:

我们可以访问关于一个接口的网络参数,而不仅仅是分配给它的名称和IP地址。要检查它是否已经启动和运行。

@Test
public void givenInterface_whenChecksIfUp_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertTrue(nif.isUp());
}

To check if it is a loopback interface:

要检查它是否是一个环回接口。

@Test
public void givenInterface_whenChecksIfLoopback_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertTrue(nif.isLoopback());
}

To check if it represents a point to point network connection:

要检查它是否代表一个点对点的网络连接。

@Test
public void givenInterface_whenChecksIfPointToPoint_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertFalse(nif.isPointToPoint());
}

Or if it’s a virtual interface:

或者说,如果它是一个虚拟接口。

@Test
public void givenInterface_whenChecksIfVirtual_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    assertFalse(nif.isVirtual());
}

To check if multicasting is supported:

要检查是否支持组播。

@Test
public void givenInterface_whenChecksMulticastSupport_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");

    assertTrue(nif.supportsMulticast());
}

Or to retrieve its physical address, usually called MAC address:

或检索其物理地址,通常称为MAC地址。

@Test
public void givenInterface_whenGetsMacAddress_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("lo");
    byte[] bytes = nif.getHardwareAddress();

    assertNotNull(bytes);
}

Another parameter is the Maximum Transmission Unit which defines the largest packet size that can be transmitted through this interface:

另一个参数是最大传输单位,它定义了可通过该接口传输的最大数据包大小。

@Test
public void givenInterface_whenGetsMTU_thenCorrect() {
    NetworkInterface nif = NetworkInterface.getByName("net0");
    int mtu = nif.getMTU();

    assertEquals(1500, mtu);
}

5. Conclusion

5.结论

In this article, we have shown network interfaces, how to access them programmatically and why we would need to access them.

在这篇文章中,我们展示了网络接口,如何以编程方式访问它们,以及为什么我们需要访问它们。

The full source code and samples used in this article are available in the Github project.

本文中使用的完整源代码和样本可在Github项目中获得。