龙空技术网

Java路径-41-Java网络编程

燃燃taba 13

前言:

如今你们对“相邻节点实现可靠传输的方式”大致比较着重,同学们都需要了解一些“相邻节点实现可靠传输的方式”的相关文章。那么小编同时在网摘上网罗了一些对于“相邻节点实现可靠传输的方式””的相关内容,希望大家能喜欢,咱们快快来了解一下吧!

1 Java网络编程的概念

网络编程是指编写在通过网络连接的多个设备(计算机)上运行的程序。

网络编程的主要工作是在发送端通过指定的协议组装信息包,在接收端根据指定的协议对信息包进行分析,从而提取相应的信息,达到通信目的。

java.net包中的J2SE的API包含提供低级通信细节的类和接口,可以直接使用这些类和接口来专注于解决问题,而不必关注通信细节。

java.net包支持两种常见的网络协议:

TCP: TCP(Transmission Control Protocol,传输控制协议)是一种面向连接、可靠且基于字节流的传输层通信协议。TCP层是IP层之上和应用层之下的中间层。TCP保证两个应用程序之间的可靠通信。它通常用于internet协议,称为TCP/IP。UDP: UDP(User Datagram Protocol,用户数据报协议),位于OSI模型的传输层。一种无连接的协议。提供数据报以在应用程序之间发送数据。由于UDP缺乏可靠性,并且属于无连接协议,因此应用程序通常必须允许一些丢失、不正确或重复的数据包。2 Socket编程2.1 构造方法2.1.1 服务端

在开发TCP程序时,首先需要创建服务器端程序。JDK的java.net包中提供了一个ServerSocket娄,该类的实例对象可以实现一个服务器端的程序。通过查阅API文档可知,ServerSocket类提供了多个构造方法,接下来就对ServerSocket的构造方法进行详细讲解。

(1) ServerSocket()

使用该构造方法在创建ServerSocket对象时并没有指定端口号,因此该对象不监听任何端口,不能直接使用,使用时还需要调用bind(SocketAddress endpoint)方法将其绑定到指定的端口号上。

(2)ServerSocket(int port)

使用该构造方法在创建ServerSocket对象时,可以将其绑定到指定的端口号上。如果port参数值为0,此时系统就会分配一个未被其他程序占用的端口号。由于客户端需要根据指定的端口号来访问服务器端程序,因此端口号随机分配的情况并不常用,通常都会给服务器端指定一个端口号

(3)ServerSocket(int port,int backlog)

该构造方法就是在第2个构造方法的基础上,增加了一个backlog参数。该参数用于指定在服务器忙时,可以与之保持连接请求的等待客户端数量,如果没有指定这个参数,默认为50。

(4)ServerSocket(int port, int backlog, InetAddress bindAddr)

该构造方法就是在第3个构造方法的基础上,指定了相关的IP地址,这种情况适用于计算机上有多块网卡和多个IP的情况,使用时可以明确规定ServerSocket在哪块网卡或IP地址上等待客户端的连接请求。显然,对于一般只有一块网卡的情况,就不用专门指定该参数。

2.1.1 客户端

客户端需要主动构造与服务器连接的 Socket,构造方法有以下几种重载形式:

Socket()Socket(InetAddress address, int port) throws UnknownHostException,IOExceptionSocket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOExceptionSocket(String host, int port) throws UnknownHostException,IOExceptionSocket(String host, int port, InetAddress localAddr, int localPort) throws IOExceptionSocket(Proxy proxy)

除了第一个不带参数的构造方法,其他构造方法都会试图建立与服务器的连接,一旦连接成功,就返回 Socket 对象,否则抛出异常

(1)设定等待建立连接的超时时间

当客户端的 Socket 构造方法请求与服务器连接时,可能要等待一段时间。在默认情况下,Socket 构造方法会一直等待下去,直到连接成功,或者出现异常。Socket 构造方法请求连接时,受底层网络的传输速度的影响,可能会处于长时间的等待状态。如果希望限定等待连接的时间,就需要使用第一个不带参数的构造方法

Socket socket = new Socket();SocketAddress remoteAddr = new InetSocketAddress("1ocalhostn", 8000);// 参数endpoint指定服务器的地址,参数timeout设定的超时时间(ms)// 如果参数timeout被设为0则表示永远不会超时socket.connect(remoteAddr, 60000);

以上代码用于连接到本地机器上的监听 8000 端口的服务器程序,等待连接的最长时间为一分钟。如果在一分钟内连接成功,则 connect() 方法顺利返回,如果在一分钟内出现某种异常则抛出该异常,如果在一分钟后既没有连接成功,也没有出现异常,那么会抛出 SocketTimeoutException

(2)设定服务器的地址

除了不带参数的构造方法,其他构造方法都需要在参数中设定服务器的地城,包括服务器的 IP 或主机名,以及端口

// address表示主机的IP地址Socket(InetAddress address, int port)// address表示主机的名字Socket(String host, int port)

InetAddress 类表示主机的P地址,提供了一系列静态工厂方法用于构造自身实例

// 返回本地主机的IP地址、InetAddress addr1 = inetAddress.getLocalHost();// 返回代表 "222.34.57” 的 IPv4 地址InetAddress addr2 = InetAddress.getByName("222.34.5.7");// 返同代表 ”2001:DB8:2DE::E13" 的 IPv6 地址InetAddress addr3 = InetAddress.getByName("2001:DB8:2DE::E13");// 返回主机名为 "; 的 IP 地址InetAddress addr4 = InetAddress.getByName (";);

(3)设定客户端的地址

在一个 Socket 对象中既包含远程服务器的 IP 地址和端口信息,也包含本地客户端的 IP 地址和端口信息。在默认情况下,客户端的 IP 地址来自客户程序所在的主机,客户端的端口则由操作系统随机分配。Socket 类还有两个构造方法允许显式地设置客户端的 IP 地址和端口

Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOExceptionSocket(String host, int port, InetAddress localAddr, int localPort) throws IOException

如果一个主机同时属于两个以上的网络,它就可能拥有两个以上 IP 地址,例如一个主机在 Internet 网络中的 IP 地址为 “222.67,1.34”,在一个局域网中的 IP 地址为 “1125.4.3",假定这个主机上的客户程序希望和同一个局城网上的一个地址为 “112.5.4.4:8000” 的服务器程序通信,客户端可按照如下方式构造 Socket 对象

InetAddress remoteAddr = InetAddress.getByName("112.5,4.45");InetAddress localAddr = InetAddress.getByName("112.5.4.3");//客户端使用口2345Socket socket = new Socket(remoteAddr, 8000, localAddr, 2345);

(4) 客户连接服务器时可能抛出的异常

当 Socket 的构造方法请求连接服务器时,可能会抛出以下异常:

UnknownHostException:无法识别主机的名字或 IP 地址ConnectException:没有服务器进程监听指定的端口,或者服务器进程拒绝连接SocketTimeoutException:等待连接超时BindException:无法把Socket 对象与指定的本地 IP 地址或端口绑定

(5)使用代理服务器

在实际应用中,有的客户程序会通过代理服务器来访问远程服务器。代理服务器有许多功能,比如能作为防火墙进行安全防范,或者提高访问速度,或者具有访问特定远程服务器的权限

String proxyIP = "myproxy.abc.oom"; // 代理服务器地址int proxyPort = 1080; // 代理服务器端口// 创建代理对象Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(proxyIP, proxyPort));Socket socket  new Socket(proxy);//连接到远程服务器socket.connect(new InetSocketAddress(";, 80));

ProxyType 类表示代理服务器的类型,有以下可选值:

Proxy.Type.SOCKS:在分层的网络结构中,SOCKS 是位于会话层的代理类型Proxy.Type.HTTP:在分层的网络结构中,HTTP 是位于应用层的代理类型Proxy.Type.DIRECT:不使用代理,直接连接远程服务器

(6)InetAddress 地址类的用法

InetAddress 类表示主机的IP 地址,InetAddress 类的静态工厂方法给 getByName() 用于构造自身的实例

// 返回代表 "222.34.5.7" 的 IPv4 地址InetAddress addr2 = InetAddress,getByName("222.34.5.7");// 返回主机名为 "; 的 IP 地址InetAddress addr4 = InetAddress.getByName(";);

InetAddress 还提供了获取相应的主机名的两种方法:

getHostname():首先从 DNS 缓存中查找与 IP 地址匹配的主机名,如果不存在,再通过 DNS 服务器查找,如果找到,则返回主机名,否则返回 IP 地址getCanonicalHostName():通过 DNS 服务器查找与 IP 地址匹配的主机名,如果找到则返回主机名,否则返问 IP 地址

以上两种方法的区别在于 getHostname() 会先查找 DNS 缓存,减少查找 DNS 服务器的概率,提高查找性能。而 getCanonicalHostName() 总是查找 DNS 服务器,确保获得当前最新版本的主机名

InetAddress 类还提供了两个测试能否从本地主机连接到特定主机的方法:

public boolean isReachable(int timeout) throws IOExceptionpublic boolean isReachable(NefworkInterface interface, int ttl, int timeout) throws IOException

如果远程主机在参数 timeout(ms)指定的时间内做出回应,以上方法返回true,否则返回 false,如果出现网络错误则抛出 IOException。第二种方法还允许从参数指定的本地网络接口建立连接,以及 TTL(IP 数据包被丢弃前允许存在的时间)

(7)NetworkInterface 类的用法

NetworkInterfiace 类表示物理上的网络接口,它有两种构造自身实例的静态工厂方法,这两种方法都声明抛出 SocketException

// 参数 name 指定网络接口的名字,如果不存在与名字对应的网络接口,就返回 nullgetByName(String name)// 参数 address 指定网络接口的 IP 地址,如果不存在与 IP 地址对应的网络接口,就返回 nullgetByInetAddress(InetAddress address)

NetworkInterface 类的以下方法用于获取网络接口的信息

// 返回网络接口的名字public String getName()// 返回和网络接口绑定的所有 IP 地址,返回值为 Enumeration 类型,里面存放了表示 IP 地址的 InetAddress 对象public Enumeration getInetAddresses()
2.2 ServerSocket常用方法2.2.1 getLocalPort方法

getLocalPort方法的返回值可分为以下三种情况:

ServerSocket对象未绑定端口,getLocalPort方法的返回值为-1.ServerSocket对象绑定了一个固定的端口,getLocalPort方法返回这个固定端口。ServerSocket对象的绑定端口为0,getLocalPort方法返回一个随机的端口(这类端口被称为匿名端口)。 getLocalPort方法的定义如下:

public int getLocalPort()

getLocalPort方法主要是为这些匿名端口而准备的。下面的代码演示了ServerSocket对象产生随机端口的过程:

package server;import java.net.*;    public class RandomPort  {    public static void main(String[] args) throws Exception      {        for (int i = 1; i <= 5; i++)          {              System.out.print("Random Port" + i + ":");              System.out.println(new ServerSocket(0).getLocalPort());          }      }  }
2.2.2 getInetAddress方法

getInetAddress可以得到ServerSocket对象绑定的IP地址。如果ServerSocket对象未绑定IP地址,返回0.0.0.0. getInetAddress方法的定义如下:

public InetAddress getInetAddress()

下面的代码演示了getInetAddress的使用方法:

ServerSocket serverSocket = new ServerSocket();  serverSocket.bind(new InetSocketAddress("192.168.18.100", 0));      System.out.println(serverSocket.getInetAddress().getHostAddress());

运行结果:

192.168.18.100
2.2.3 getLocalSocketAddress方法

这个方法其实是将getLocalPort和getInetAddress方法的功能集成到了一起。也就是说,使用getLocalSocketAddress方法可以同时得到绑定端口和绑定IP地址。这个方法返回了一个SocketAddress对象。SocketAddress类是一个抽象类,要想分别得到端口和IP地址,必须将SocketAddress对象转换成InetSocketAddress对象(InetSocketAddress类是从SocketAddress类继承的)。getLocalSocketAddress方法的定义如下:

public SocketAddress getLocalSocketAddress()

下面的代码演示了getLocalSocketAddress的使用方法。

ServerSocket serverSocket = new ServerSocket();  serverSocket.bind(new InetSocketAddress("192.168.18.100", 1234));  System.out.println(serverSocket.getLocalSocketAddress());  InetSocketAddress nsa = (InetSocketAddress)serverSocket.getLocalSocketAddress();  System.out.println( nsa.getAddress().getHostAddress());  System.out.println( nsa.getPort());

运行结果:

/192.168.18.100:1234  192.168.18.100  1234
2.2.4 setSoTimeout(int timeout)ServerSocket的setSoTimeout(20000) :单位为毫秒,用于设置20s内无客户端 Socket 连接,则抛出SocketTimeoutException异常。

ServerSocket的setSoTimeout(20000)示例代码如下:

//ServerSocketDemoimport java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketTimeoutException;public class ServerSocketDemo extends Thread {    private ServerSocket serverSocket;    private int i = 1;    public ServerSocketDemo(int port) throws IOException {        serverSocket = new ServerSocket(port);        //设置20s内无客户端连接,则抛出SocketTimeoutException异常        serverSocket.setSoTimeout(20000);    }    public void run(){        while(true) {            System.out.println("服务端第"+i+"次启动中...对应的端口号为:"+ serverSocket.getLocalPort());            i++;            try {                Socket server = serverSocket.accept();                                //当服务端监听到客户端的连接后才会执行以下代码                System.out.println("服务端打印的远程主机地址为:"+server.getRemoteSocketAddress());                //监听来自客户端的消息                DataInputStream dis = new DataInputStream(server.getInputStream());                System.out.println("服务端接收到的来自于客户端的信息为:"+dis.readUTF());                //通过socket向客户端发送信息                DataOutputStream dos = new DataOutputStream(server.getOutputStream());                dos.writeUTF("我是服务端,您已连接到:"+server.getLocalSocketAddress());                server.close();            }catch (SocketTimeoutException e){                System.out.println("20s内无客户端连接,正在关闭服务端监听服务");                continue;            }catch (IOException e) {                e.printStackTrace();                break;            }        }    }    public static void main(String[] args) {        try {            Thread t1 = new ServerSocketDemo(8089);            t1.run();        }catch(IOException e){            e.printStackTrace();            return;        }    }}
//ClientSocketDemoimport java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.Socket;import java.util.logging.SocketHandler;public class ClientSocketDemo {    public static void main(String[] args) {        String serverName = "localhost";        int port = 8089;        try {            Socket client = new Socket(serverName, port);            //当客户端连接到服务端后,才会执行以下代码            System.out.println("客户端已连接到远程主机地址:"+client.getRemoteSocketAddress());            //向服务端发送消息            DataOutputStream dos = new DataOutputStream(client.getOutputStream());            dos.writeUTF("服务端你好,我是客户端,我的地址是: "+client.getLocalSocketAddress());            //接收来自服务端的消息            DataInputStream dis = new DataInputStream(client.getInputStream());            System.out.println("来自于服务端的消息:"+dis.readUTF());            client.close();        } catch (IOException e) {            e.printStackTrace();            return;        }    }}

两个类的执行结果如下:

//ServerSocketDemo服务端第1次启动中...对应的端口号为:8089服务端打印的远程主机地址为:/127.0.0.1:62770服务端接收到的来自于客户端的信息为:服务端你好,我是客户端,我的地址是: /127.0.0.1:62770服务端第2次启动中...对应的端口号为:808920s内无客户端连接,正在关闭服务端监听服务服务端第3次启动中...对应的端口号为:808920s内无客户端连接,正在关闭服务端监听服务//ClientSocketDemo客户端已连接到远程主机地址:localhost/127.0.0.1:8089来自于服务端的消息:我是服务端,您已连接到:/127.0.0.1:8089Process finished with exit code 0
Socket的setSoTimeout(5) :
public void setSoTimeout(int timeout) throws SocketException//启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。将此选项设为非零的超时值时,在与此 Socket 关联的 InputStream 上调用 read() 将只阻塞此时间长度。//如果超过超时值,将引发 java.net.SocketTimeoutException,虽然 Socket 仍旧有效。选项必须在进入阻塞操作前被启用才能生效。//超时值必须是 > 0 的数。超时值为 0 被解释为无穷大超时值。//参数//timeout - 指定的以毫秒为单位的超时值。//抛出://SocketException - 如果底层协议出现错误,例如 TCP 错误。

简单来说就是Socket对象的接收消息的非阻塞实现,注意此方法只是针对read()方法,上述的DataInputStream调用readUTF()方法无效,在固定时间内没有得到结果,就会结束本次阻塞,等待进行下一次的阻塞轮询。也就实现了应用层面的非阻塞。具体含义是指:在5毫秒内,如果没有读取完客户端发送的消息,就会停止读取,并抛出SocketTimeoutException异常。 示例代码如下:

//ServerSocketDemoimport java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketTimeoutException;public class ServerSocketDemo extends Thread {    private ServerSocket serverSocket;    private int i = 1;    public ServerSocketDemo(int port) throws IOException {        serverSocket = new ServerSocket(port);        //设置20s内无客户端连接,则抛出SocketTimeoutException异常        serverSocket.setSoTimeout(20000);    }    public void run(){        while(true) {            System.out.println("服务端第"+i+"次启动中...对应的端口号为:"+ serverSocket.getLocalPort());            i++;            try {                Socket server = serverSocket.accept();                // 接收消息非阻塞实现。                // 注意此方法只是针对read()方法,下方的DataInputStream调用readUTF()方法无效                // 方法设置超时时间,在固定时间内没有得到结果,就会结束本次阻塞,等待进行下一次的阻塞轮询。也就实现了应用层面的非阻塞。                // 具体含义是指:在5毫秒内,如果没有读取完客户端发送的消息时,就会停止读取,并抛出SocketTimeoutException异常                server.setSoTimeout(5);                //当服务端监听到客户端的连接后才会执行以下代码                System.out.println("服务端打印的远程主机地址为:"+server.getRemoteSocketAddress());                                /*                * server.setSoTimeout(5);起作用是在以下代码的情况下inputStream调用read()方法时才起作用。                * */                InputStream inputStream = server.getInputStream();                byte[] bytes = new byte[1024];                while ((inputStream.read(bytes))!=-1){                    System.out.println("在5ms内读取的内容有:"+bytes.toString());                }				//以下代码不会执行,因为5ms内读取不完客户端发送的消息,会产生SocketTimeoutException异常,接着进入下一次阻塞轮询                //通过socket向客户端发送信息                DataOutputStream dos = new DataOutputStream(server.getOutputStream());                dos.writeUTF("我是服务端,您已连接到:"+server.getLocalSocketAddress());                server.close();            }catch (SocketTimeoutException e){                System.out.println("5ms内未读取完客户端的信息,正在关闭服务端监听服务");                continue;            }catch (IOException e) {                e.printStackTrace();                break;            }        }    }    public static void main(String[] args) {        try {            Thread t1 = new ServerSocketDemo(8089);            t1.run();        }catch(IOException e){            e.printStackTrace();            return;        }    }}
//ClientSocketDemoimport java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.Socket;import java.util.logging.SocketHandler;public class ClientSocketDemo {    public static void main(String[] args) {        String serverName = "localhost";        int port = 8089;        try {            Socket client = new Socket(serverName, port);            //当客户端连接到服务端后,才会执行以下代码            System.out.println("客户端已连接到远程主机地址:"+client.getRemoteSocketAddress());            //向服务端发送消息            DataOutputStream dos = new DataOutputStream(client.getOutputStream());            dos.writeUTF("服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n服务端你好,我是客户端\n123123"+                    "我的地址是: "+client.getLocalSocketAddress());            //接收来自服务端的消息            DataInputStream dis = new DataInputStream(client.getInputStream());            System.out.println("来自于服务端的消息:"+dis.readUTF());            client.close();        } catch (IOException e) {            e.printStackTrace();            return;        }    }}

两个类的执行结果如下:

//ServerSocketDemo服务端第1次启动中...对应的端口号为:8089服务端打印的远程主机地址为:/127.0.0.1:63446在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a574795在5ms内读取的内容有:[B@4a5747955ms内未读取完客户端的信息,正在关闭服务端监听服务服务端第2次启动中...对应的端口号为:8089//ClientSocketDemo客户端已连接到远程主机地址:localhost/127.0.0.1:8089//同时客户端也处于阻塞状态,不会关闭应用进程。因为客户端在等待服务端返回消息Process finished with exit code 0

注:为什么 dos.writeUTF() 方法传递了这么长的字符串? 为了让服务端在 5ms 内读取不完,从而触发 SocketTimeoutException 异常。

2.2.5 bind(SocketAddress host, int backlog)

ServerSocket类的bind(SocketAddress host, int backlog)方法用于将ServerSocket绑定到特定的地址(IP地址和端口号)。此方法在构造ServerSocket之后,且没有绑定地址之前被调用。

参数:

host:要绑定ServerSocket的地址。backlog:连接队列的最大长度。

import java.net.InetSocketAddress;import java.net.ServerSocket; public class ServerSocketExample {    public static void main(String[] args) {        try {            // 创建ServerSocket对象            ServerSocket serverSocket = new ServerSocket();             // 绑定ServerSocket到特定地址和端口            serverSocket.bind(new InetSocketAddress("localhost", 8080), 50);             System.out.println("ServerSocket bound to " + serverSocket.getLocalSocketAddress());             // 在这里可以添加你的代码来接受客户端连接             // 关闭ServerSocket            serverSocket.close();        } catch (Exception e) {            e.printStackTrace();        }    }}
3 InetAddress类

概念:InetAddress类是 Java 中用于表示 IP 地址的类。它提供了一种标准的方法来处理 IP 地址,无论是 IPv4 还是 IPv6 地址。InetAddress 类位于 java.net 包中,是 Java 网络编程的一部分。

3.1 常用方法:getLocalHost():这个方法获取本地主机的 InetAddress 对象,它表示当前计算机。在你的示例中,你获取了本地主机的 InetAddress 对象并打印了它。getByName(String host):这个方法根据指定的主机名或域名获取对应的 InetAddress 对象。在你的示例中,你使用了两个不同的主机名(本地主机名和百度域名)来获取相应的 InetAddress 对象,并打印了它们。getHostName():这个方法用于获取 InetAddress 对象的主机名。在你的示例中,你使用 host2 对象(百度域名)调用了 getHostName() 方法,以获取主机名并将其打印出来。getHostAddress():这个方法用于获取 InetAddress 对象的 IP 地址。在你的示例中,你使用 host2 对象(百度域名)调用了 getHostAddress() 方法,以获取 IP 地址并将其打印出来。

代码:

public class API_ {    public static void main(String[] args) throws UnknownHostException {        //获取本机 InetAddress 对象 getLocalHost        InetAddress localHost = InetAddress.getLocalHost();        System.out.println(localHost);//LAPTOP-U9K8AF0S/172.21.202.203 获取到主机的信息(主机名和ip地址)        //根据指定主机名/域名获取 ip 地址对象        InetAddress host1 = InetAddress.getByName("LAPTOP-U9K8AF0S");//LAPTOP-U9K8AF0S主机名        System.out.println(host1);//LAPTOP-U9K8AF0S/172.21.202.203,通过主机名来获取到ip地址,当然这边输出的是主机名和ip地址        System.out.println(host1.getHostAddress());//172.21.202.203,获取到ip地址        InetAddress host2 = InetAddress.getByName(";);//域名        System.out.println(host2);//,通过域名来获取ip地址,当然这边输出的是域名和ip地址        System.out.println(host2.getHostAddress());//153.3.238.102,获取ip地址    }}
3.2 InetAddress的常用方法public String getHostName() 获得该InetAddress对象的主机名称public String getCanonicalHostName() 获取此IP地址的全限定域名public bytes[] getHostAddress() 获取该InetAddress对象对应的IP地址字符串public static InetAddress getLocalHost() 获取本机对应的InetAddress对象public static InetAddress getByName(String host) 根据主机获得对应的InetAddress对象,参数host可以是IP地址或域名public static InetAddress[] getAllByName(String host) 根据主机获得对应的InetAddress对象public static InetAddress getByAddress(byte[] addr) 获取addr所封装的IP地址对应的Inet Address对象public boolean isReachable(int timeout) 判断是否可以到达该地址3.3 示例

1、过一个demo来演示一下这些方法:

package TEMP.Temp3;//使用以上方法的小demoimport java.net.InetAddress;import java.net.UnknownHostException;public class InetAddressDemo {    public static void main(String[] args) {        try{            //获取自己本机地址信息            InetAddress localIp=InetAddress.getLocalHost();            //1获取此IP地址的全限定域名            System.out.println("1.localIp.getCanonicalHostName()="                    + localIp.getCanonicalHostName());            //2获取该InetAddress对象对应的IP地址字符串            System.out.println("2.localIp.getHostAddress()="                    + localIp.getHostAddress());            //3获得该InetAddress对象的主机名称            System.out.println("3.localIp.getHostName()="                    + localIp.getHostName());            System.out.println("4.localIp.toString()="+localIp.toString());            //5.判断是否可以到达该地址            System.out.println("5.localIp.isReachable(5000)="                    + localIp.isReachable(5000));            System.out.println("====================================");            System.out.println("====================================");            //获取指定域名地址的信息([eg]百度)            InetAddress baiduip = InetAddress.getByName(";);            //1获取此IP地址的全限定域名            System.out.println("1.baiduIp.getCanonicalHostName()="                    + baiduip.getCanonicalHostName());            //2获取该InetAddress对象对应的IP地址字符串            System.out.println("2.baiduIp.getHostAddress()="                    + baiduip.getHostAddress());            //3获得该InetAddress对象的主机名称            System.out.println("3.baiduIp.getHostName()="                    + baiduip.getHostName());            System.out.println("4.baiduIp.toString()="+baiduip.toString());            //5判断是否可以到达该地址            System.out.println("5.baiduIp.isReachable(5000)="                    + baiduip.isReachable(5000));            System.out.println("====================================");            System.out.println("====================================");            //获取指定原始IP地址信息            InetAddress ip = InetAddress.getByAddress(new byte[]{127,0,0,1});            // InetAddress ip = InetAddress.getByName("127.0.0.1");            System.out.println("1.ip.getCanonicalHostName()=" + ip.getCanonicalHostName());            System.out.println("2.ip.getHostAddress()= "+ ip.getHostAddress());            System.out.println("3.ip.getHostName()="+ ip.getHostName());            System.out.println("4.ip.toString()="+ ip.toString());            System.out.println("5.ip.isReachable(5000)="                    + ip.isReachable(5000));        }catch(UnknownHostException e){            e.printStackTrace();        }catch(Exception e){            e.printStackTrace();        }    }}//输出结果//        1.localIp.getCanonicalHostName()=shizhanli.lan//        2.localIp.getHostAddress()=192.168.30.151//        3.localIp.getHostName()=shizhanli//        4.localIp.toString()=shizhanli/192.168.30.151//        5.localIp.isReachable(5000)=true//        ====================================//        ====================================//        1.baiduIp.getCanonicalHostName()=14.215.177.38//        2.baiduIp.getHostAddress()=14.215.177.38//        3.baiduIp.getHostName()=        4.baiduIp.toString()=        5.baiduIp.isReachable(5000)=true//        ====================================//        ====================================//        1.ip.getCanonicalHostName()=localhost//        2.ip.getHostAddress()= 127.0.0.1//        3.ip.getHostName()=localhost//        4.ip.toString()=localhost/127.0.0.1//        5.ip.isReachable(5000)=true

2、给定地址,找到主机名称

package TEMP.Temp3;//给定地址,找出主机名import java.net.InetAddress;import java.net.UnknownHostException;public class ReverseTest {    public static void main(String[] args) throws UnknownHostException {        InetAddress ia = InetAddress.getByName("192.168.30.151");        System.out.println(ia.getCanonicalHostName());    }}//输出结果://shizhanli.lan

3、找到本地机器的IP地址

package TEMP.Temp3;//找到本地机器的IP地址import java.net.InetAddress;import java.net.UnknownHostException;public class MyAddress {    public static void main(String[] args) {        try{            InetAddress me = InetAddress.getLocalHost();            String dottedQuad = me.getHostAddress();//创建一个String类型的变量来接受返回的ip地址            System.out.println("My address is " + dottedQuad);        }catch (UnknownHostException ex){            System.out.println("I'm sorry.I don't know my own address.");        }    }}//输出:My address is 192.168.30.151
3.4 InetAddress缓存

InetAddress类具有高速缓存以存储成功以及不成功的主机名解析。

默认情况下,安装安全管理器时,为了防止DNS欺骗攻击,主机名正确解决方案的结果将永久缓存。

当没有安装安全管理器时,默认的行为是缓存条目以获得有限的(实现依赖的)时间段。

不成功的主机名解析的结果被缓存很短的时间(10秒),以提高性能。

如果不需要默认行为,则可以将Java安全属性设置为不同的生存时间(TTL)值进行正缓存。 同样,系统管理员可以在需要时配置不同的缓存缓存TTL值。

3.5 Inet4Address和Inet6Address

Java使用了两个类Inet4Address和Inet6Address,来区分IPv4地址和IPv6地址.

public final class Inet4Address extends InetAddress  public final class Inet6Address extends InetAddress
3.6 地址类型

有些IP地址和地址模式有特殊的含义。Java提供了许多方法来测试InetAddress对象是否符合其中某个标准。

1.public boolean isAnylocalAdress()

如果是通配地址,方法返回true。通配地址可以匹配本地系统中的任何地址,在IPv4中,通配地址是0.0.0.0。在IPv6中,通配地址是0:0:0:0:0:0:0:0。

2.public boolean isLoopbackAdress()

如果是回送地址,方法返回true。回送地址直接在IP层连接同一台计算机,而不使用任务物理硬件。在IPv4中,这个地址是127.0.0.1。在IPv6中,这个回送地址是0:0:0:0:0:0:0:1。

3.public boolean isLinkLocalAddress()

如果地址是一个IPv6本地链接地址,方法返回true,否则返回false。

4.public boolean isSiteLocalAddress()

如果地址是一个IPv6本地网络地址,方法返回true,否则返回false。

5.public boolean isMulticastAddress()

如果地址是一个组播地址,方法返回true,否则返回false。

6.public boolean isMCGlobal()

如果地址是全球组播地址,方法返回true,否则返回false。

7.public boolean isMCNodeLocal()

如果地址是一个本地接口组播地址,方法返回true,否则返回false。

8.public boolean isMCLinkLocal()

如果地址是一个子网范围组播地址,方法返回true,否则返回false。

9.public boolean isMCSiteLocal()

如果地址是一个网站范围组播地址,方法返回true,否则返回false。

10.public boolean isMCOrgLocal()

如果地址是一个组织范围组播地址,方法返回true,否则返回false。

3.7 测试可达性

InetAddress类有两个isReachable()方法,可以测试一个特定节点对当前主机是否可达哦。

//方法1:public boolean isReachable(int timeout) throws IOException  //方法2:public boolean isReachable(NetworkInterface interface, int ttl, int timeout) throws IOException

这些方法尝试使用traceroute查看指定地址是否可达。如果主机在timeout毫秒内响应,则方法返回true;否则返回false。如果出现网络错误则抛出IOException异常。第二个方法还允许指定从哪个本地网络接口建立连接,以及“生存时间”。

3.8 Object方法

1.public boolean equals(Object o)

若与一个InetAddress对象有相同的IP地址,并不要求这两个对象有相同的主机名,就返回true,否则返回false。

2.public int hasCode()

只根据IP地址来计算,不考虑主机名

3.public String toString()

生成的字符串有两种格式:主机名或点分四段地址。

4 网络协议

java.net 包中提供了两种常见的网络协议的支持:

TCP:TCP(英语:Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的、基于字节流的传输层通信协议,TCP 层是位于 IP 层之上,应用层之下的中间层。TCP 保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。

UDP:UDP (英语:User Datagram Protocol,用户数据报协议),位于 OSI 模型的传输层。一个无连接的协议。提供了应用程序之间要发送数据的数据报。由于UDP缺乏可靠性且属于无连接协议,所以应用程序通常必须容许一些丢失、错误或重复的数据包。

4.1 网络分层

(1)物理层 物理层处于OSI的最底层,是整个开放系统的基础。物理层涉及通信信道上传输的原始比特流(bits),它的功能主要是为数据端设备提供传送数据的通路以及传输数据。机房,硬件空调什么的。

(2)数据链路层 数据链路层的主要任务是实现计算机网络中相邻节点之间的可靠传输,把原始的、有差错的物理传输线加上数据链路协议以后,构成逻辑上可靠的数据链路。需要完成的功能有链路管理、成帧、差错控制以及流量控制等。其中成帧是对物理层的原始比特流进行界定,数据链路层也能够对帧的丢失进行处理。交换机 双绞线。

(3)网络层 网络层涉及源主机节点到目的主机节点之间可靠的网络传输,它需要完成的功能主要包括路由选择、网络寻址、流量控制、拥塞控制、网络互连等。IP,路由器。

(4)传输层 传输层起着承上启下的作用,涉及源端节点到目的端节点之间可靠的信息传输。传输层需要解决跨越网络连接的建立和释放,对底层不可靠的网络,建立连接时需要三次握手,释放连接时需要四次挥手。

(5)应用层 应用层为OSI的最高层,是直接为应用进程提供服务的。其作用是在实现多个系统应用进程相互通信的同时,完成一系列业务处理所需的服务。约定数据内容和格式。

4.2 TCP/UDP

TCP(传输控制协议) TCP是面向连接的协议,因此每个TCP连接都有3个阶段:连接建立、数据传送和连接释放。

连接建立经历三个步骤,通常称为“三次握手”。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。

(TCPServer)

package com.lanou.test;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.util.Scanner;public class TCPServer {	public static void main(String[] args) {		//JAVA的TCP编程中,BIO模型使用一个工具实现TCP编程,这个工具叫做Socket,服务端叫ServerSocket,		try {			ServerSocket ss = new ServerSocket(65535);//这个端口号 写死 固定的			//这个是客户端发送上来的链接,服务器使用这个链接做数据处理			Socket s = ss.accept();			//分别是两个指针对当前联机的输入输出流			OutputStream os = s.getOutputStream();			InputStream is = s.getInputStream();						//改变输入输出流的状态(套一个其它流)			PrintWriter pw = new PrintWriter(os);			Scanner scanner = new Scanner(System.in);			BufferedReader br = new BufferedReader(new InputStreamReader(is));			while(true) {				String str= scanner.nextLine();	//nextline  阻塞式代码				pw.println(str);				pw.flush();				if(str.equals("")) {					break;				}				str = br.readLine();				System.out.println("我是服务器,我收到客户端消息" + str);				if(str.equals("")) {					break;				}				}		} catch (IOException e) {			e.printStackTrace();		}	}}

(TCPClient)

package com.lanou.test;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;import java.net.UnknownHostException;import java.util.Scanner;public class TCPClient {	public static void main(String[] args) {		//客户端,直接使用Sockert工具		try {			Socket s = new Socket("127.0.0.1", 65535);			// host  链接到那个主机上 String的参数       port端口 			InputStream is = s.getInputStream();			OutputStream os = s.getOutputStream();			BufferedReader br = new BufferedReader(new InputStreamReader(is));			PrintWriter pw = new PrintWriter(os);			Scanner scanner = new Scanner(System.in);			String str;			while(!(str = br.readLine()).equals("")) {					System.out.println("客户端收到"+str);				str = scanner.nextLine();				pw.println(str);				pw.flush();				if(str.equals("")) {					break;				}			}		} catch (UnknownHostException e) {			e.printStackTrace();		} catch (IOException e) {			e.printStackTrace();		}	}}

(UDPServer)

package com.lanou.udp;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.SocketException;public class UDP {	public static void main(String[] args) {		//UDP是面向报文的,所以数据传输过程中,是不需要建立链接的,只要有对应的报文,就可以指挥网卡去发送这个数据包		//但是对于服务器来说,UDP虽然没有建立链接,却需要在对应的端口上进行监听,否则是有问题的		//DatagramSocket,这个类型是JAVA对于UDP实现的一个网络套接字,当我们需要使用UDP传输内容的时候,只需要创建一个		//DatagramSocket,对象就可以了,不用指定这个对象是否是服务(区别TCP的ServerSocket)			try {			DatagramSocket socket = new DatagramSocket(10086);			byte[] b = new byte[1024];			DatagramPacket p =new DatagramPacket(b, b.length);			while(true) {				socket.receive(p);				b = p.getData();//拿出数据 拆开包裹				System.out.println(new String(b));					System.out.println(p.getAddress() + "说 : " + new String(b));				p.setData("你开心就好".getBytes());				socket.send(p);			}				} catch (SocketException e) {			e.printStackTrace();		} catch (IOException e) {			e.printStackTrace();		}	}}

(UDPClient)

package com.lanou.udp;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetSocketAddress;import java.net.SocketAddress;import java.net.SocketException;public class UDPClient {	public static void main(String[] args) {		try {			DatagramSocket socket = new DatagramSocket();			String str = "我是客户端  ";			DatagramPacket p = new DatagramPacket(str.getBytes(), str.getBytes().length);			p.setSocketAddress(new InetSocketAddress("127.0.0.1", 10086));			socket.send(p);			socket.receive(p);			System.out.println(new String(p.getData()));		} catch (SocketException e) {			e.printStackTrace();		} catch (IOException e) {			e.printStackTrace();		}			}}
4.3 TCP与UDP区别

区别1:连接类型

TCP(面向连接): TCP是一个细心的老大哥,它在传输数据之前会先与对方建立连接,确保数据能够安全可靠地传输。这就像你在打电话之前先拨通了电话一样,保证通信的畅通。UDP(无连接): 相对于TCP,UDP就像个自由奔放的旅行者,不喜欢拘束。它不会为了保证可靠性而浪费时间建立连接,直接把数据扔出去,自由自在。

区别2:可靠性

TCP(高可靠性): TCP是一个稳如泰山的好汉,它会确保数据在传输过程中不丢失、不乱序、不重复。有点像你在快递里签收了一个保价包裹,绝对不会丢失!UDP(低可靠性): 与TCP不同,UDP是个敢爱敢恨的大胆者,不怕损失一点数据。它直接扔出数据,可能会丢失一些,但在某些场景下,这点损失是可以接受的,比如语音通话、视频会议等。

区别3:传输方式

TCP(字节流传输): TCP会把数据切成小块,然后一个一个传输,就像我们在吃大餐时一口一口慢慢享受。UDP(数据报传输): UDP则是直接扔出一个个数据包,就像你一次性吃了好几颗葡萄,轻松自在。

区别4:顺序性

TCP(保证数据按顺序到达): TCP会确保发送方发送的数据按照正确的顺序到达接收方。就好比你按照电影的时间顺序一样,不会跳着播放。UDP(不保证数据按顺序到达): UDP则不拘泥于顺序,数据包按照发送的顺序到达不是它的责任。这就像是你随意地点播了几首歌,它们可能会以各种顺序到达你的耳朵。

区别5:适用场景

TCP(稳重务实): TCP适用于对数据准确性要求较高的场景,比如文件传输、网页访问等。它的连接机制和可靠性保证了数据的安全传输。UDP(轻松自在): UDP适用于对实时性要求高、能容忍少量数据丢失的场景,比如在线游戏、语音通话等。它的无连接特性使得数据能够更迅速地传输。

区别6:连接建立时间

TCP(较慢): TCP在建立连接时需要进行三次握手,稍微花费一些时间。但这也是它能保证可靠性的基础。UDP(较快): 相对于TCP,UDP无需建立连接,所以连接建立得更快。就像你不需要在打电话前进行繁琐的拨号过程一样。

区别7:开销

TCP(较大): 由于TCP需要维护连接状态、保证可靠性,它的开销相对较大。但这也是为了数据的稳定传输。UDP(较小): UDP直接发送数据,不维护连接状态,因此开销相对较小。适用于那些对实时性要求高、能容忍一些数据丢失的场景。

区别8:使用场景举例

TCP:

HTTP、HTTPSFTP(文件传输协议)SMTP(简单邮件传输协议)

UDP:

DNS(域名系统)VoIP(语音传输)视频流传输在线游戏

标签: #相邻节点实现可靠传输的方式