Java学习笔记37

Java笔记37

TCP案例

TCP实现发送消息

  • 下面我们来分别编写一个客户端程序和一个服务端程序,使用用户端给服务端发送一句消息,然后使用服务端接收用户端发送过来的这句消息并打印输出。

  • 客户端:

      1. 创建一个与服务端Socket类的实例对象,根据IP地址和端口连接服务器;
      • Socket类:该类实现客户端套接字。 套接字是两台机器之间通讯的端点,用于完成两个应用程序之间的数据传输(可以理解为IP+端口)。该类有很多种构造方法,这里我们使用其中一种参数为IP地址和端口号的构造方法:

      • Socket(InetAddress address, int port)  //创建流套接字并将其连接到指定IP地址的指定端口号。
        
      1. 使用Socket类中的getOutputStream()方法,创建一个Socket对象的字节输出流,使用write()方法发送消息;
      1. 关闭资源。
  • TcpClientDemo01.java

package com.clown.net.tcp;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

//客户端
public class TcpClientDemo01 {
    public static void main(String[] args) {

        Socket socket = null;
        OutputStream os = null;

        try {
            //1. 需要知道服务端的IP地址、端口号
            InetAddress serverIP = InetAddress.getByName("127.0.0.1");
            int port = 9999;
            //2. 创建一个Socket连接
            socket = new Socket(serverIP, port);
            //3. 发送消息,字节输出流
            os = socket.getOutputStream();
            os.write("好好学习,天天向上".getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //4. 关闭资源
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
  • 服务端:

      1. 创建一个ServerSocket类的实例化对象,创建服务并设置端口号;
      • ServerSocket类:这个类实现了服务器套接字。服务器套接字等待通过网络进入的请求。 它根据该请求执行一些操作,然后可能将结果返回给请求者。 这里我们也使用它的构造方法之一来创建端口:

      • ServerSocket(int port)  //创建绑定到指定端口的服务器套接字。
        
      1. 使用ServerSocket类中的accept()方法,监听客户端的连接,创建与客户端连接的Socket对象;
      • public Socket accept() throws IOException  //从连接请求队列中取出一个客户的连接请求,然后创建与客户连接的Socket对象,并将它返回。如果队列中没有连接请求,accept()方法就会一直等待,直到接收到了连接请求才返回。
        
      1. 使用Socket类中的getInputStream()方法,创建一个Socket对象的字节输入流,接收用户端发送过来的消息;
      • 如果要打印输出中文字符,最好使用管道流ByteArrayOutputStream和它的write(byte[] b, int off, int len)方法,这样就能保证结果不会出现乱码。
      1. 关闭资源;
  • TcpServerDemo01.java

package com.clown.net.tcp;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

//服务端
public class TcpServerDemo01 {
    public static void main(String[] args) {

        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        ByteArrayOutputStream baos = null;

        try {
            //1. 创建服务,需要建立一个端口
            serverSocket = new ServerSocket(9999);
            //2. 等待客户端连接过来
            socket = serverSocket.accept();  //阻塞式监听,会一直等待客户端连接
            //3. 读取客户端发送的消息,字节输入流
            is = socket.getInputStream();
            //管道流
            //ByteArrayOutputStream: 该类实现了将数据写入字节数组的输出流。 当数据写入缓冲区时,缓冲区会自动增长。
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer)) != -1) {
                //write(byte[] b, int off, int len);  从指定的字节数组写入 len个字节,从偏移量为 off开始,输出到这个字节数组输出流。
                baos.write(buffer, 0 , len);
            }
            System.out.println(baos.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4. 关闭资源
            if (baos != null) {
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
  • 运行结果:
  • 我们需要先运行服务端:

Java学习笔记37_第1张图片

  • 此时,服务端已启动,因为使用了ServerSocket类中的accept()方法,所以服务端会阻塞,并一直等待客户端发送连接请求。这时,我们再启动客户端:
  • 观察服务端的运行结果:

Java学习笔记37_第2张图片

  • 客户端运行正常,没有发生异常。
  • 观察此时服务端的运行结果:

Java学习笔记37_第3张图片

  • 与客户端建立连接之后,服务端运行正常,没有发生异常,且服务端成功接收并打印了用户端发送的消息。

TCP实现文件上传

  • 首先,我们准备一个任意类型的文件(这里我准备的是一张图片:galaxy.jpg,我把它放在了我项目的根目录):

  • 接着,我们来编写我们的客户端和服务端程序。为了更清晰地呈现代码结构,便于学习和理解,在下面的程序中,我没有使用try/catch/finally语句捕获异常,而是直接抛出了异常:
  • 客户端:
      1. 创建一个与服务端Socket类的实例对象,根据IP地址和端口连接服务器;
      1. 使用Socket类中的getOutputStream()方法,创建一个Socket对象的字节输出流;
      1. 创建一个文件字节输入流,读取我们准备上传的文件
      1. 使用write()方法将文件上传给客户端;
      1. 发送完毕之后,需要禁用输出流,相当于通知客户端本身“数据已经发送完了”;
      1. 接收服务器的通知,确定服务器已接收完毕,客户端才能断开连接;
      1. 关闭资源。

TcpClientDemo02.java

package com.clown.net.tcp;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class TcpClientDemo02 {

    public static void main(String[] args) throws Exception {
        //1. 创建一个与服务端连接的 Socket对象
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);

        //2. 创建一个 Socket对象的字节输出流
        OutputStream os = socket.getOutputStream();

        //3. 读取文件,创建一个文件字节输入流
        FileInputStream fis = new FileInputStream(new File("galaxy.jpg"));

        //4. 发送文件
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }

        //5. 发送完毕之后,禁用输出流
        socket.shutdownOutput();  //我已经发送完了

        //6. 确定服务器已接收完毕,才能断开连接
        InputStream inputStream = socket.getInputStream();
        //管道流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer2 = new byte[1024];
        int len2;
        while ((len2 = inputStream.read(buffer2))!= -1) {
            baos.write(buffer2, 0, len2);
        }
        System.out.println(baos.toString());

        //7. 关闭资源
        fis.close();
        os.close();
        socket.close();
    }

}
  • 服务端:
      1. 创建一个ServerSocket类的实例化对象,创建服务并设置端口号;
      1. 使用ServerSocket类中的accept()方法,监听客户端的连接,创建与客户端连接的Socket对象;
      1. 使用Socket类中的getInputStream()方法,创建一个Socket对象的字节输入流,接收用户端上传过来的文件;
      1. 创建一个文件字节输出流,输出我们接收到的文件;
      1. 接收完毕之后,通知客户端可以断开连接了;
      1. 关闭资源。

TcpServerDemo02.java

package com.clown.net.tcp;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerDemo02 {

    public static void main(String[] args) throws Exception {
        //1. 创建服务,设置端口
        ServerSocket serverSocket = new ServerSocket(9000);

        //2. 监听客户端的连接,创建一个与客户的连接的 Socket对象
        Socket socket = serverSocket.accept();  //阻塞式监听,会一直等待客户端连接

        //3. 接收文件,创建一个Socket对象的字节输入流
        InputStream is = socket.getInputStream();

        //4. 文件输出,文件字节输出流
        FileOutputStream fos = new FileOutputStream(new File("receive.jpg"));
        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer)) != -1) {
            fos.write(buffer, 0, len);
        }

        //5. 接收完毕之后,通知客户端:我已接收完毕,你可以断开连接了
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("我已接收完毕,你可以断开连接了".getBytes());

        //6. 关闭资源
        fos.close();
        is.close();
        socket.close();
        serverSocket.close();
    }

}
  • 运行结果:
  • 我们需要先运行服务端:

Java学习笔记37_第4张图片

  • 此时,服务端已启动,因为使用了ServerSocket类中的accept()方法,所以服务端会阻塞,并一直等待客户端发送连接请求。这时,我们再启动客户端:
  • 观察客户端的运行结果:

Java学习笔记37_第5张图片

  • 客户端运行正常,没有发生异常,并且成功接收到了服务端发来的 “我已接收完毕,你可以断开连接” 的通知。
  • 观察此时服务端的运行结果:

Java学习笔记37_第6张图片

  • 与客户端建立连接之后,服务端运行正常,没有发生异常。
  • 最后,我们再查看一下我们的项目文件,检查文件是否已经上传成功:

  • 确认了图片确实已经上传成功。

UDP案例

UDP实现发送消息

  • UDP没有明确的客户端与服务端的概念,只有发送端和接收端。

  • 下面,我们就来分别写一个发送端和一个接收端,实现消息的发送:

    • 在正式开始写之前,我们需要先了解以下知识:

    • DatagramSocket类:此类表示用于发送和接收数据报包的套接字。数据报套接字是分组传送服务的发送或接收点。

      • 常用的构造方法:

      • DatagramSocket()
        DatagramSocket(int port)  //参数:port-要使用的端口
        
    • DatagramPacket类:该类表示数据报包。

      • 常用的构造方法:

      • DatagramPacket(byte buf[], int offset, int length, InetAddress address, int port)
        //参数:buf-数据包数据; offset-分组数据偏移量; length-分组数据长度; address-目的地址; port-目的端口号。
        DatagramPacket(byte buf[], int offset, int length, SocketAddress address)
        //参数:buf-数据包数据; offset-分组数据偏移量; length-分组数据长度,address-目标套接字地址。
        DatagramPacket(byte buf[], int offset, int length)
        //参数:buf-数据包数据; offset-分组数据偏移量; length-分组数据长度。
        
      • 常用方法:

      • send(DatagramPacket p)  //从此套接字发送数据报包
        receive(DatagramPacket p)  //从此套接字接收数据报包。该方法阻塞,直到接收到数据报
        getAddress()  //返回该数据报发送或接收数据报的计算机的 IP地址
        getData()  //返回数据缓冲区
        
  • 发送端:

      1. 创建一个DatagramSocket类的实例对象;
      1. 实例化一个DatagramPacket类的对象,来创建一个数据包,并把想要发送的数据和目的(接收端)IP地址及端口号作为参数,传入该数据包的构造方法;
      1. 使用DatagramPacket类中的send(DatagramPacket p)方法发送数据;
      1. 关闭资源。

UdpSenderDemo01

package com.clown.net.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

//UDP,不需要建立连接
//发送端
public class UdpSenderDemo01 {

    public static void main(String[] args) throws Exception {
        //1. 建立一个 DatagramSocket
        //DatagramSocket: 此类表示用于发送和接收数据报套接字。
        DatagramSocket socket = new DatagramSocket();

        //2. 建立一个包
        //要发送的消息
        String msg = "你好啊,接收端!";
        //要发送给谁
        InetAddress localhost = InetAddress.getByName("localhost");  //IP地址
        int port = 9090;  //端口号

        //DatagramPacket: 该类表示数据报包
        //构造方法: DatagramPacket(byte buf[], int offset, int length, InetAddress address, int port);
        //参数: buf-数据包数据; offset-分组数据偏移量; length-分组数据长度; address-目的地址; port-目的端口号。
        DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);

        //3. 发送包
        //send(DatagramPacket p);  从此套接字发送数据报包
        socket.send(packet);

        //4. 关闭资源
        socket.close();
    }

}
  • 接收端:
      1. 创建一个DatagramSocket类的实例对象,并设置端口号;
      1. 建立一个缓冲区并设置它的大小,根据该容器建立一个数据包(DatagramPacket),用于接收发送过来的数据;
      1. 使用DatagramPacket类中的receive(DatagramPacket p)方法接收数据包,该方法为阻塞方法,它会一直等待,直到接收到发送端发来的数据包;
      1. 关闭资源。

UdpReceiverDemo01

package com.clown.net.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

//还是需要一个阻塞方法,监听发送端发送数据
//接收端
public class UdpReceiverDemo01 {

    public static void main(String[] args) throws Exception {
        //1. 设置端口
        DatagramSocket socket = new DatagramSocket(9090);

        //2. 建立一个缓冲区并设置它的大小,根据该缓冲区建立一个包,用于接收发送过来的数据
        byte[] buffer = new byte[1024];
        //构造方法: DatagramPacket(byte buf[], int offset, int length)
        //参数: buf-数据包数据; offset-分组数据偏移量; length-分组数据长度;
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);

        //3. 接收数据包
        //receive(DatagramPacket p);  从此套接字接收数据报包,该方法阻塞,直到接收到数据报
        socket.receive(packet);

        //getAddress();  返回该数据报发送或接收数据报的计算机的 IP地址
        System.out.println(packet.getAddress().getHostAddress());  //获取发送者的 IP地址
        //getData();  返回数据缓冲区
        System.out.println(new String(packet.getData(), 0, packet.getLength()));  //打印接收到的消息

        //4. 关闭资源
        socket.close();
    }

}
  • 运行结果:
  • 同样的,我们还是需要先运行接收端:

Java学习笔记37_第7张图片

  • 此时,接收端已启动,因为使用了DatagramPacket类中的receive(DatagramPacket p)方法,所以接收端会阻塞,并一直等待发送端发送数据包。这时,我们再启动发送端:
  • 观察发送端的运行结果:

Java学习笔记37_第8张图片

  • 发送端运行正常,没有发生异常。
  • 观察此时接收端的运行结果:

Java学习笔记37_第9张图片

  • 接收端运行正常,没有发生异常,且接收端成功接收到了发送端发送的数据包,并打印了发送端的IP地址。

UDP实现循环发送消息

  • 在现实生活中,我们通常并不只是发送一句消息,怎样才能实现连续的发送多条消息呢?
  • 下面我们再写一个发送端和一个接收端,实现循环发送消息:
  • 发送端:

UdpSenderDemo02

package com.clown.net.udp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

public class UdpSenderDemo02 {

    public static void main(String[] args) throws Exception {
        //1. 创建 DatagramSocket对象,设置端口号
        DatagramSocket socket = new DatagramSocket(8888);

        //2. 准备数据,控制台读取:System.in
        //桥转换流:InputStreamReader,将字符流转换为字节流
        InputStreamReader isr = new InputStreamReader(System.in);
        //字节缓冲流:BufferedReader
        BufferedReader reader = new BufferedReader(isr);

        //循环
        while (true) {
            String data = reader.readLine();
            byte[] bytes = data.getBytes();

            //3. 创建数据包
            //InetSocketAddress: 该类实现 IP套接字地址(IP地址 + 端口号),它也可以是一对(主机名 + 端口号)
            DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress("localhost", 6666));

            //4. 发送数据包
            socket.send(packet);

            //设置终止循环的条件
            if (data.equals("bye")) {
                break;
            }
        }

        //5. 关闭资源
        socket.close();
    }

}
  • 接收端:

UdpReceiverDemo02

package com.clown.net.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpReceiverDemo02 {

    public static void main(String[] args) throws Exception {
        //1. 创建 DatagramSocket对象,设置端口号
        DatagramSocket socket = new DatagramSocket(6666);

        //循环
        while (true) {
            //2. 准备接收数据包
            //创建一个缓冲区
            byte[] buffer = new byte[1024];
            //创建一个数据包,用于接收数据
            DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);

            //设置终止循环的条件
            byte[] bytes = packet.getData();  //读取接收到的数据包中的数据
            String receiveStr = new String(bytes, 0, packet.getLength());  //将数据转换成字符串形式

            //打印接收到的消息
            System.out.println(receiveStr);

            //3. 接收数据包
            socket.receive(packet); //阻塞式

            //当收到 "bye" 时,终止循环
            if (receiveStr.equals("bye")) {
                break;
            }
        }

        //4. 关闭资源
        socket.close();
    }

}
  • 运行结果:
  • 先运行接收端:

Java学习笔记37_第10张图片

  • 再运行发送端:

Java学习笔记37_第11张图片

  • 此时,接收端等待发送端发送数据,发送端等待控制台输入数据。
  • 我们往发送端的控制台中输入消息:

Java学习笔记37_第12张图片

  • 往发送端的控制台中输入完以上消息之后,查看接收端:

Java学习笔记37_第13张图片

  • 接收端成功接收到了发送端发送过来的消息。
  • 最后,我们往发送端的控制台中输入bye,终止程序:

Java学习笔记37_第14张图片

  • 输入bye后,发送端停止运行。
  • 查看接收端:

Java学习笔记37_第15张图片

  • 接收端在接收到发送端发来的bye之后,也停止了运行。

UDP实现在线聊天

  • 在上面我们已经实现了连续的发送多条消息,下面我们结合之前学习的多线程的内容,编写程序实现在线聊天:
  • 发送端:

TalkSend.java

package com.clown.net.udp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

public class TalkSend implements Runnable {

    DatagramSocket socket = null;
    BufferedReader reader = null;
    private int ownPort;  //自身发送进程的端口号
    private String destinationIP;  //目的 IP地址
    private int destinationPort;  //目的接收进程的端口号

    //构造器
    public TalkSend(int ownPort, String destinationIP, int destinationPort) {
        this.ownPort = ownPort;
        this.destinationIP = destinationIP;
        this.destinationPort = destinationPort;

        try {
            socket = new DatagramSocket(ownPort);
            reader = new BufferedReader(new InputStreamReader(System.in));
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        //循环
        while (true) {
            try {
                String data = reader.readLine();  //读取数据
                byte[] bytes = data.getBytes();  //将数据转换成字节类型
                //创建数据包
                DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress(this.destinationIP, this.destinationPort));

                //发送数据包
                socket.send(packet);

                //设置终止循环的条件
                if (data.equals("bye")) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //关闭资源
        socket.close();
    }

}
  • 接收端

TalkReceive.java

package com.clown.net.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class TalkReceive implements Runnable {

    DatagramSocket socket = null;
    String msgFrom = null;  //谁发送的消息
    private int ownPort;  //自身接收进程的端口号

    //构造器
    public TalkReceive(int ownPort, String msgFrom) {
        this.ownPort = ownPort;
        this.msgFrom = msgFrom;

        try {
            socket = new DatagramSocket(ownPort);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        //循环
        while (true) {
            try {
                //准备接收数据包
                //创建一个缓冲区
                byte[] buffer = new byte[1024];
                //创建一个数据包,用于接收数据
                DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);

                //接收数据包
                socket.receive(packet); //阻塞式

                //设置终止循环的条件
                byte[] bytes = packet.getData();  //读取接收到的数据包中的数据
                String receiveData = new String(bytes, 0, packet.getLength());  //将数据转换成字符串形式

                //打印接收到的消息
                System.out.println(msgFrom + ": " + receiveData);

                //当收到 "bye" 时,终止循环
                if (receiveData.equals("bye")) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //关闭资源
        socket.close();
    }

}
  • 模拟两个用户:老师学生 - 他们都能接收对方发送的消息,也都能向对方发送消息,实现聊天。
  • 老师:

TalkTeacher.java

package com.clown.net.udp;

public class TalkTeacher {

    public static void main(String[] args) {
        //开启两个线程(发送、接收)
        new Thread(new TalkSend(5555, "localhost", 8888)).start();
        new Thread(new TalkReceive(9999, "学生")).start();
    }

}
  • 学生:

TalkStudent.java

package com.clown.net.udp;

public class TalkStudent {

    public static void main(String[] args) {

        //开启两个线程(发送、接收)
        new Thread(new TalkSend(7777, "localhost", 9999)).start();
        new Thread(new TalkReceive(8888, "老师")).start();
    }

}
  • 启动TalkTeacher.javaTalkStudent.java

Java学习笔记37_第16张图片

  • TalkStudent.java的控制台中输入一句消息,模拟学生向老师发送了一句话:

Java学习笔记37_第17张图片

  • 老师成功收到了学生发来的消息。接着,在TalkTeacher.java的控制台中输入一句消息,模拟老师回复了学生的消息:

Java学习笔记37_第18张图片

  • 学生也成功接收到了老师回复的消息。我们还可以继续聊天:

Java学习笔记37_第19张图片

  • 当其中双方都收到了对方发送的bye后,终止程序,结束聊天:

Java学习笔记37_第20张图片

URL

  • 统一资源定位器(URL)用于定位互联网上的某个资源。
    • 如:https://www.baidu.com/
  • 一个URL可以由以下五个部分组成:
协议://IP地址:端口/项目名/资源
  • 注:
    • 一个域名本质上就是一个IP地址,如:www.baidu.com 使用时会由DNS(域名系统)解析为IP地址:39.156.66.18
    • URL可以由五个以下的部分组成(比如一个URL可以不包含/项目名/资源这两个部分),但通常不会超过五个部分。

URL的组成部分

  • 为了更好地理解URL的组成部分,我们新建一个URL对象,调用URL类中的一些常用方法来获取URL字符串中的各个组成部分:
package com.clown.net.url;

import java.net.MalformedURLException;
import java.net.URL;

public class URLDemo01 {

    public static void main(String[] args) throws MalformedURLException {
        URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=clown&password=123");

        //getProtocol();  获取此 URL的协议名
        System.out.println(url.getProtocol());
        //getHost();  获取此 URL的主机名
        System.out.println(url.getHost());
        //getPort();  获取此 URL的端口号
        System.out.println(url.getPort());
        //getPath();  获取此 URL的文件路径部分
        System.out.println(url.getPath());
        //getFile();  获取此 URL的文件名,返回的文件部分将与 getPath()相同,加上 getQuery()的返回值 (如果有)
        System.out.println(url.getFile());
        //getQuery(); 获取此 URL的查询部分
        System.out.println(url.getQuery());
    }

}
  • 运行结果:

Java学习笔记37_第21张图片

URL实现下载网络资源

  • 首先,我们需要准备一个网络资源。这里我使用Tomcat创建网络资源:
  • 打开Tomcat所在目录下的webapps目录,在里面新建一个自己的文件夹(这里我新建的文件名为clown),然后在新建的文件夹里添加文件(这里我添加了一个文本文件:ConfidentialFile.txt):

Java学习笔记37_第22张图片

  • 给我们的文件添加一些内容:

Java学习笔记37_第23张图片

  • 注意,为了避免出现乱码,要把文件的编码方式修改为ANSIANSI:在不同的系统中,ANSI表示不同的编码,因为我的电脑是简体中文系统的,所以ANSI表示的编码方式为GBK):

Java学习笔记37_第24张图片

  • 然后打开Tomcat所在目录下的bin目录,双击startup.bat启动Tomcat服务器:

Java学习笔记37_第25张图片

Java学习笔记37_第26张图片

  • 这时,我们再打开浏览器,在上方地址栏中输入URL:http://localhost:8080/clown/ConfidentialFile.txt

就能查看我们刚刚创建的文件了:

Java学习笔记37_第27张图片

  • 下面,我们来编写代码,实现利用URL下载我们刚刚创建的这个网络资源ConfidentialFile.txt
package com.clown.net.url;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class URLDownload {

    public static void main(String[] args) throws Exception {
        //1. 下载地址
        URL url = new URL("http://localhost:8080/clown/ConfidentialFile.txt");

        //2. 连接到这个资源  HTTP
        //openConnection();  打开连接,返回一个 URLConnection类的实例。
        //URLConnection类: URLConnection 是一个抽象类,代表应用程序和 URL 之间的通信链接。它的实例可用于读取和写入此 URL 引用的资源。
        //HttpURLConnection类: 支持 HTTP特定功能的 URLConnection。是 Java提供的发起 HTTP请求的基础类库,提供了 HTTP请求的基本能力
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();  //强制转换为 HttpURLConnection类

        //读取输入流
        //getInputStream();  返回从此打开的连接读取的输入流。
        InputStream inputStream = urlConnection.getInputStream();

        //创建文件输出流
        FileOutputStream fos = new FileOutputStream("ConfidentialFile.txt");

        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            fos.write(buffer, 0, len);  //写出数据
        }

        //关闭资源
        fos.close();
        inputStream.close();
        //disconnect();  关闭连接
        urlConnection.disconnect();

    }

}
  • 运行结果:

Java学习笔记37_第28张图片

  • 程序运行正常,没有报错。
  • 我们再查看我们的项目文件,看一下文件是否已经成功下载到了我们的项目文件夹里:

Java学习笔记37_第29张图片

  • 文件确实已经下载到我们的项目文件夹中了。
  • 我们再去下载一个互联网上的资源试试:
  • 随便在网易云音乐的网站上找一首音乐,F12,找到该音乐资源:

Java学习笔记37_第30张图片

  • 双击打开,复制URL:

Java学习笔记37_第31张图片

  • 将复制的URL粘贴到我们刚才写的程序中覆盖,再更改一下我们保存文件的文件名,运行程序:
package com.clown.net.url;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class URLDownload {

    public static void main(String[] args) throws Exception {
        //1. 下载地址
        URL url = new URL("https://m701.music.126.net/20230818212400/a087d364aa84bdf9360cdce0d896f386/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/19668440665/bacd/3d83/588c/d0bbf5eb57a04d4b650f304bea6be713.m4a");

        //2. 连接到这个资源  HTTP
        //openConnection();  打开连接,返回一个 URLConnection类的实例。
        //URLConnection类: URLConnection 是一个抽象类,代表应用程序和 URL 之间的通信链接。它的实例可用于读取和写入此 URL 引用的资源。
        //HttpURLConnection类: 支持 HTTP特定功能的 URLConnection。是 Java提供的发起 HTTP请求的基础类库,提供了 HTTP请求的基本能力
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();  //强制转换为 HttpURLConnection类

        //读取输入流
        //getInputStream();  返回从此打开的连接读取的输入流。
        InputStream inputStream = urlConnection.getInputStream();

        //创建文件输出流
        FileOutputStream fos = new FileOutputStream("ThatIsUS.m4a");

        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            fos.write(buffer, 0, len);  //写出数据
        }

        //关闭资源
        fos.close();
        inputStream.close();
        //disconnect();  关闭连接
        urlConnection.disconnect();

    }

}
  • 运行结果:

Java学习笔记37_第32张图片

  • 程序运行正常,没有报错。
  • 我们再查看我们的项目文件,看一下文件是否已经成功下载到了我们的项目文件夹里:

Java学习笔记37_第33张图片

  • 最后,我们看看下载的音乐资源能否正常播放:

Java学习笔记37_第34张图片

  • 音乐能够正常播放,下载成功!

你可能感兴趣的:(Java,SE,学习笔记合集,java,学习,笔记)