• 【Java网络编程】



    前言

    Java是一种广泛应用于网络编程的编程语言。通过Java的网络编程能力,我们可以构建强大的网络应用程序。本文将介绍Java网络编程的基础知识、常用API和一些实践技巧,帮助读者更好地理解和应用Java网络编程。


    一、Java网络编程基础

    在开始探讨Java网络编程之前,我们需要了解一些基本概念和术语。网络编程是指通过计算机网络进行数据交换和通信的过程。Java提供了一套完善的网络编程API,涵盖了各种协议和服务,如TCP/IP、HTTP、UDP等。
    1.网络应用模型

    1. **C/S:**这里的C是指Client,即客户端。而S是指Server,即服务端。网络上的的应用本质上就是两台计算机上的软件进行交互。而客户端和服务端就是对应的两个应用程序,即客户端应用程序和服务端应用程序

    2. **B/S:**这里的B是Browser,即浏览器,而S是指Server。浏览器是一个通用的客户端,可以与不同的服务端进行交互。但是本质上B/S还是C/S结构,只不过浏览器是一个通用的客户端而已。
      2.可靠传输与不可靠传输
      TCP协议与UDP协议都是传输协议,客户端程序与服务端程序基于这些协议完成网络间的数据交互。

    3. TCP是可靠传输协议,是面向连接的协议,保证数据传输中的可靠性和完整性。
      TCP保证可靠传输,但是传输效率低,占用带宽高。

    4. UDP是不可靠传输协议,不保证数据传输的完整性。
      UDP不保证可靠传输,但是传输速度块,占用带宽小
      3.Socket与ServerSocket
      - java.net.Socket
      Socket(套接字)封装了TCP协议的通讯细节,是的我们使用它可以与服务端建立网络链接,并通过 它获取两个流(一个输入一个输出),然后使用这两个流的读写操作完成与服务端的数据交互
      java.net.ServerSocket

    5. ServerSocket运行在服务端,作用有两个:

      • 向系统申请服务端口,客户端的Socket就是通过这个端口与服务端建立连接的。
      • 监听服务端口,一旦一个客户端通过该端口建立连接则会自动创建一个Socket,并通过该Socket与客户端进行数据交互。
        在这里插入图片描述

    二、Java网络编程常用API

    1.Socket类:Socket类是Java网络编程中最常用的类之一,它提供了创建客户端套接字的方法和与服务器进行通信的能力。通过Socket,可以建立与远程服务器的连接,并进行数据传输。
    Socket提供了两个重要的方法:

    • OutputStream getOutputStream()
      该方法会获取一个字节输出流,通过这个输出流写出的字节数据会通过网络发送给对方。
    • InputStream getInputStream()
      通过该方法获取的字节输入流读取的是远端计算机发送过来的数据。
      原理图
      在这里插入图片描述

    例:

    String host = "127.0.0.1";
    int port = 8080;
    
    // 创建Socket对象并连接服务器
    Socket socket = new Socket(host, port);
    
    // 获取输入流和输出流进行数据传输
    InputStream inputStream = socket.getInputStream();
    OutputStream outputStream = socket.getOutputStream();
    
    // 向服务器发送数据
    String message = "Hello, Server!";
    outputStream.write(message.getBytes());
    
    // 从服务器接收数据
    byte[] buffer = new byte[1024];
    int length = inputStream.read(buffer);
    String response = new String(buffer, 0, length);
    
    // 关闭资源
    outputStream.close();
    inputStream.close();
    socket.close();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2.ServerSocket类:ServerSocket类用于创建服务器套接字,接受来自客户端的连接请求,并创建对应的Socket对象进行通信。通过ServerSocket,可以监听指定的端口,等待客户端的连接。

    int port = 8080;
    
    // 创建ServerSocket对象并监听指定端口
    ServerSocket serverSocket = new ServerSocket(port);
    
    // 接受客户端的连接请求
    Socket socket = serverSocket.accept();
    
    // 获取输入流和输出流进行数据传输
    InputStream inputStream = socket.getInputStream();
    OutputStream outputStream = socket.getOutputStream();
    
    // 接收客户端发送的数据
    byte[] buffer = new byte[1024];
    int length = inputStream.read(buffer);
    String request = new String(buffer, 0, length);
    
    // 处理请求并返回响应数据
    String response = "Hello, Client!";
    outputStream.write(response.getBytes());
    
    // 关闭资源
    outputStream.close();
    inputStream.close();
    socket.close();
    serverSocket.close();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    3.URL类:URL类提供了对统一资源定位符(URL)的解析和处理能力,它可以用于打开和读取网络资源。通过URL,可以创建一个表示网络资源的对象,并获取资源的各种属性信息。

    String urlString = "https://www.example.com";
    
    // 创建URL对象
    URL url = new URL(urlString);
    
    // 打开连接并获取输入流
    InputStream inputStream = url.openStream();
    
    // 读取数据
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    String line;
    StringBuilder responseBuilder = new StringBuilder();
    while ((line = reader.readLine()) != null) {
        responseBuilder.append(line);
    }
    
    // 关闭资源
    reader.close();
    inputStream.close();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4.HttpURLConnection类:HttpURLConnection类是一种常用的建立HTTP连接的方式,它继承自URLConnection类,在Java中用于发送HTTP请求和接收HTTP响应。通过HttpURLConnection,可以设置请求头、发送请求参数,以及获取服务器返回的响应数据。

    String urlString = "https://www.example.com";
    
    // 创建URL对象
    URL url = new URL(urlString);
    
    // 打开连接
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    
    // 设置请求方法
    connection.setRequestMethod("GET");
    
    // 发送请求并获取响应码
    int responseCode = connection.getResponseCode();
    
    // 读取响应数据
    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    String line;
    StringBuilder responseBuilder = new StringBuilder();
    while ((line = reader.readLine()) != null) {
        responseBuilder.append(line);
    }
    
    // 关闭资源
    reader.close();
    connection.disconnect();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    5.DatagramSocket类:DatagramSocket类用于进行UDP数据包的发送和接收,适用于一些实时性要求较高的应用场景。通过DatagramSocket,可以实现基于UDP协议的数据传输。
    1.发送数据报文

    String host = "127.0.0.1";
    int port = 9999;
    
    // 创建DatagramSocket对象
    DatagramSocket socket = new DatagramSocket();
    
    // 准备数据
    String message = "Hello, Server!";
    byte[] data = message.getBytes();
    
    // 创建DatagramPacket对象并设置目标地址和端口号
    DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName(host), port);
    
    // 发送数据报文
    socket.send(packet);
    
    // 关闭资源
    socket.close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.接收数据报文

    int port = 9999;
    
    // 创建DatagramSocket对象并监听指定端口
    DatagramSocket socket = new DatagramSocket(port);
    
    // 准备接收数据
    byte[] buffer = new byte[1024];
    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
    
    // 接收数据报文
    socket.receive(packet);
    
    // 处理接收到的数据
    String request = new String(packet.getData(), 0, packet.getLength());
    
    // 关闭资源
    socket.close();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    三、Java网络编程实践技巧

    与服务端建立连接案例
    例:聊天室

    package socket;
    
    import java.io.*;
    import java.net.Socket;
    import java.util.Scanner;
    
    /**
     * 聊天室客户端
     */
    public class Client {
        /*
    java.net.Socket 套接字
    Socket封装了TCP协议的通讯细节,我们通过它可以与远端计算机建立链接,
    并通过它获取两个流(一个输入,一个输出),然后对两个流的数据读写完成
    与远端计算机的数据交互工作。
    我们可以把Socket想象成是一个电话,电话有一个听筒(输入流),一个麦克
    风(输出流),通过它们就可以与对方交流了。
    */
        private Socket socket;
    
        /**
         * 构造方法,用来初始化客户端
         */
        public Client() {
            try {
                System.out.println("正在连接服务器......");
                /*
            实例化Socket时要传入两个参数
            参数1:服务端的地址信息
            可以是IP地址,如果链接本机可以写"localhost"
            参数2:服务端开启的服务端口
            我们通过IP找到网络上的服务端计算机,通过端口链接运行在该机器上
            的服务端应用程序。
            实例化的过程就是链接的过程,如果链接失败会抛出异常:
            java.net.ConnectException: Connection refused: connect
            */
                socket = new Socket("localhost",8088);
                System.out.println("与服务器建立连接了!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 客户端开始工作的方法
         */
        public void start() {
            try {
                /*
                Socket提供了一个方法:
                OutputStream getOutputStream()
                该方法获取的字节输出流写出的字节会通过网络发送给对方计算机。
                */
                //低级流,将字节通过网络发送给对方
                OutputStream out = socket.getOutputStream();
                //高级流,负责衔接字节流与字符流,并将写出的字符按指定字符集转字节
                OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
                //高级流,负责块写文本数据加速
                BufferedWriter bw = new BufferedWriter(osw);
                //高级流,负责按行写出字符串,自动行刷新
                PrintWriter pw = new PrintWriter(bw,true);
    
                Scanner scanner = new Scanner(System.in);
                while(true) {
                    String line = scanner.nextLine();
                    if("exit".equalsIgnoreCase(line)) {
                        break;
                    }
                    pw.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    /*
                    通讯完毕后调用socket的close方法。
                    该方法会给对方发送断开信号。
                    */
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            Client client = new Client();
            client.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    package socket;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.nio.charset.StandardCharsets;
    
    /**
     * 聊天室服务器
     */
    public class Server {
        /**
         * 运行在服务端的ServerSocket主要完成两个工作:
         * 1:向服务端操作系统申请服务端口,客户端就是通过这个端口与ServerSocket建立链接
         * 2:监听端口,一旦一个客户端建立链接,会立即返回一个Socket。通过这个Socket
         * 就可以和该客户端交互了
         *
         * 我们可以把ServerSocket想象成某客服的"总机"。用户打电话到总机,总机分配一个
         * 电话使得服务端与你沟通。
         */
        private ServerSocket serverSocket;
    
        /**
         * 服务端构造方法,用来初始化
         */
        public Server() {
            try {
                /*
                ServerSocket实例化的同时指定服务端口
                如果该端口被其他程序占用则会抛出异常:
                java.net.BindException:address already in use
                此时我们需要更换端口,或者杀死占用该端口的进程。
                端口号范围:0-65535
                */
                System.out.println("正在启动服务......");
                /*
                实例化ServerSocket时要指定服务端口,该端口不能与操作系统其他
                应用程序占用的端口相同,否则会抛出异常:
                java.net.BindException:address already in use
                端口是一个数字,取值范围:0-65535之间。
                6000之前的的端口不要使用,密集绑定系统应用和流行应用程序。
                */
                serverSocket = new ServerSocket(8088);
                System.out.println("服务器启动完毕!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        /**
         * 服务端开始工作的方法
         */
        public void start() {
    
            try {
                /*
                ServerSocket的一个重要方法:
                Socket accept()
                该方法用于接受客户端的连接。这是一个阻塞方法,调用后会"卡住",直到
                一个客户端与ServerSocket连接,此时该方法会立即返回一个Socket实例
                通过这个Socket实例与该客户端对等连接并进行通讯。
                相当于"接电话"的动作
                */
                while(true){
                    System.out.println("等待客户端连接......");
                    Socket socket = serverSocket.accept();
                    System.out.println("一个客户端连接了!");
                    //启动一个线程负责与客户端交互
                    ClientHandler handler = new ClientHandler(socket);
                    //创建线程
                    Thread t = new Thread(handler);
                    //启动线程
                    t.start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            Server server = new Server();
            server.start();
        }
    
        /**
         * 创建线程的方式:实现Runnable接口单独定义线程任务
         * 这个线程任务就是让一个线程与指定的客户端进行交互
         */
        private class ClientHandler implements Runnable{
            private Socket socket;
    
            public ClientHandler(Socket socket) {
                this.socket = socket;
            }
            @Override
            public void run() {
    
                try {
                    InputStream in = socket.getInputStream();
                    InputStreamReader isr = new InputStreamReader(in,StandardCharsets.UTF_8);
                    BufferedReader br = new BufferedReader(isr);
                    String line;
                    while((line = br.readLine()) != null){
                        System.out.println("客户端说:" + line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114

    需要注意的几个点:

    • 当客户端不再与服务端通讯时,需要调用socket.close()断开链接,此时会发送断开链接的信号给服
      务端。这时服务端的br.readLine()方法会返回null,表示客户端断开了链接。
    • 当客户端链接后不输入信息发送给服务端时,服务端的br.readLine()方法是出于阻塞状态的,直到
      读取了一行来自客户端发送的字符串。
      在这里插入图片描述

    总结

    通过深入理解Java网络编程,我们可以构建强大的网络应用程序,满足不同的业务需求。希望读者通过本文的学习,能够掌握Java网络编程的核心概念和技术,提升自己的开发能力。

  • 相关阅读:
    Windows安装mysql详细步骤(通俗易懂,简单上手)
    【笔记】元素水平滑动(松手查看更多、滑动回弹)
    slam学习
    点云地面滤波--布料模拟滤波(CSF)
    gitlab--基础--5.3--CICD--gitlab-ci.yml关键字
    Linux cpu 亲缘性 绑核
    maven+mybatis—实现数据库中图书信息的增删改查
    HDLBits - Verilog学习笔记
    数据结构速通-重点知识点回顾
    不用445端口,替代Windows共享,局域网文件共享新方式
  • 原文地址:https://blog.csdn.net/weixin_72847531/article/details/133761611