《Java-SE-第三十一章》之网络编程

news2024/12/24 21:34:22

前言

在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!”

博客主页:KC老衲爱尼姑的博客主页

博主的github,平常所写代码皆在于此

共勉:talk is cheap, show me the code

作者是爪哇岛的新手,水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!


文章目录

  • 网络编程
    • 为什么需要网编程?
    • 什么是网络编程
    • 网络编程中的基本概念
      • 发送端和接收端
      • 请求和响应
      • 客户端和服务端
      • 常见的客户端服务端模型
    • Socket套接字
      • 概念
      • 套接字分类
        • Java数据报套接字通信模型
        • Java流套接字通信模型
      • UDP数据报套接字编程
          • UDP客户端服务器回显服务程序
          • UDP客户端服务器简单翻译服务程序
      • TCP流套接字编程
        • TCP客户端服务器回显服务程序
      • TCP中的长短连接
        • 一发一收(短连接)

网络编程

为什么需要网编程?

&ebsp;&ebsp;当我们使用浏览器进行搜索时,浏览器会根据关键字搜索出视频,图片文本等资源,这些资源都属于网络资源。网络资源相比于本地资源来说更加的丰富多彩。而这些网络资源都需要通过网络编程来进行数据传输。

什么是网络编程

&ebsp;&ebsp;网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络数据传输。但是,同一台主机上的不同进程,如果是基于网络来进行通信,也属于网络编程。

在这里插入图片描述

网络编程中的基本概念

发送端和接收端

在一次网络数据传输时:

发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。

接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。

收发端:发送端和接收端两端,也简称为收发端。

注意:发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。

在这里插入图片描述

请求和响应

一般来说,获取一个网络资源,涉及到两次网络数据传输:

第一次:请求数据的发送,告诉服务端我要获取xx资源。

第二次:响应数据的发送,服务端返回给客户端xx资源。

举个栗子,这就好比,在外面吃饭,你跟老板说来一份青椒炒肉,自然就会给你提供一份青椒炒肉。
在这里插入图片描述

客户端和服务端

服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务,就像我们平时用的B站app上面的视频,图片等资源,都是通过网络从服务器上得到数据,然后通过网络传输到app上。

客户端获取服务的一方进程,称为客户端。简单来说就是各种软件。

对于服务来说,一般是提供:

  • 客户端获取服务资源

在这里插入图片描述

  • 客户端保存资源在服务端
    在这里插入图片描述

举个栗子, 银行提供了存款服务,用户(客户端)把钱(资源)存在银行(服务端)。同时银行提供了取款服务,用户可以去银行取钱(获得服务端资源)。

常见的客户端服务端模型

最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:

  1. 客户端先发送请求到服务端

  2. 服务端根据请求数据,执行相应的业务处理

  3. 服务端返回响应:发送业务处理结果

  4. 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)

在这里插入图片描述

Socket套接字

概念

Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程

套接字分类

  1. 流套接字:使用传输层TCP协议,对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。
  2. 数据报套接字:使用传输层UDP协议,对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。
  3. 原始套接字用于自定义传输层协议,用于读写内核没有处理的IP协议数据。

Java数据报套接字通信模型

在这里插入图片描述

以上只是一次发送端的UDP数据报发送,及接收端的数据报接收,并没有返回的数据。也就是只有请求,没有响应。对于一个服务端来说,重要的是提供多个客户端的请求处理及响应,流程如下:

在这里插入图片描述

Java流套接字通信模型

在这里插入图片描述

UDP数据报套接字编程

DatagramSocket API

DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。

DatagramSocket 构造方法:

方法签名方法说明
DatagramSocket ()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket (int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)

DatagramSocket 方法:

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

DatagramPacket API

DatagramPacket是UDP Socket发送和接收的数据报。

DatagramPacket 构造方法:

方法签名方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组。address指定目的主机的IP和端口号

DatagramPacket 方法

InetAddress
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端的ip
int getPort()接收端主机IP地址从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端的主机和端口号
byte[] getData()取接收端主机端口号获取数据报中的数据

构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。

InetSocketAddress API

InetSocketAddress ( SocketAddress 的子类 )构造方法:

方法签名方法说明
InetSocketAddress(InetAddress addr, int port)创建一个Socket地址,包含IP地址和端口号
UDP客户端服务器回显服务程序

回显程序就是客户端向服务端发送什么,服务端就返回给客户端什么,就像在夜深人静的时候,你突然说了一句话,然后无形你听到了一句一模一样你刚说的话。

客户端代码设计

  1. .创建客户端DatagramSocket
  2. 准备要发送的数据
  3. .组装要发送的UDP数据报,包含数据,及发送的服务端信息
  4. 发送UDP数据报

代码示例

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {
    /**
     */
    private DatagramSocket clientSocket;
    /**
     * 服务器IP地址
     */
    private String serverIp;
    /**
     * 服务器端口号
     */
    private int serverPort;
    public UdpEchoClient(String ip, int port) throws SocketException {
        //客户端可以自己指定端口号,也可以让系统自动分配,但是自己指定的端口号可能已经被使用了,所以系统分配端口号更好
        this.clientSocket = new DatagramSocket();
        this.serverIp = ip;
        this.serverPort = port;
    }

    /**
     * 启动客户端
     * @throws IOException
     */
    public void start() throws IOException {
        //1. 获取用户输入的数据
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.print("请输入需要发送的数据->");
            String request = sc.next();
            //2. 根据用户输入的数据,将数据打包,待发送
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIp), serverPort);
            // 3.发送数据
            clientSocket.send(requestPacket);
            // 4.接收请求
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
            clientSocket.receive(responsePacket);
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength(), "UTF-8");
            System.out.printf("我的请求: %s, 它的回应: %s\n", request, response);
        }
    }
    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
        client.start();
    }
}

服务端代码设计

  1. 创建服务端DatagramSocket
  2. 创建数据报,用于接收客户端发送的数据
  3. 等待客户端数据,一旦得到构造数据报
  4. 返回数据

代码示例

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

public class UdpEchoServer {
    /**
     *  准备好socket实例,准备传输
     */
    private DatagramSocket serverSocket;

    /**
     *指定端口
     * @param port
     * @throws SocketException
     */
    public UdpEchoServer(int port) throws SocketException {
        this.serverSocket = new DatagramSocket(port);
    }

    /**
     * 启动服务器
     * @throws IOException
     */
    public void start() throws IOException {
        System.out.println("服务器准备就绪!");
        while (true) {
            //1. 读取客户端的请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024);//参数为储存数据的数组与最大空间大小
            serverSocket.receive(requestPacket);
            //2. 解析收到的数据包,一般解析成字符串进行处理
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength(), "UTF-8");
            //3. 处理请求
            String response = process(request);
            //4. 发送请求,因为数据的传输是依据DatagramPacket来进行传输的,所以我们需要先包装在发送
            //除此之外,我们还需要知道客户端的地址和端口号
            //接收DatagramPacket对象时。该对象里面存有客户端的地址和端口号。可以使用getSocketAddress方法获取
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length,
                    requestPacket.getSocketAddress());
            serverSocket.send(responsePacket);
            //5. 输出发送日志
            System.out.printf("[%s:%d] 收到的请求: %s, 回应: %s\n",
                    requestPacket.getAddress().toString(), requestPacket.getPort(), request, response);
        }
    }

    /**
     * .处理数据,回显服务直接将原数据返回即可
     * @param data
     * @return
     */
    public String process(String data) {
        return data;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
        udpEchoServer.start();
    }
}

运行结果:
在这里插入图片描述

在这里插入图片描述

UDP客户端服务器简单翻译服务程序

翻译其实就是利用了哈希表,我们只需要将中文中的词和英文中相对应意思的词一一对应,全部存放在哈希表中,这样就 构成了一个词库。当需要将中文翻译好成英文的时候,就只需要在哈希表中查找对应的英文。对应该程序我们只需将上述的服务端代码中的处理请求的部分稍加修改就行。

翻译服务端代码

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;

public class UdpDictServer extends UdpEchoServer{
    //最简单的翻译处理服务器
    private final HashMap<String, String> dict = new HashMap<>();

    public UdpDictServer(int port) throws SocketException {
        super(port);
 
        dict.put("cat", "小猫");
        dict.put("dog", "小狗");
        dict.put("bird", "小鸟");
    }

    @Override
    public String process(String data) {
        return dict.getOrDefault(data, "词库没有该单词!");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer server = new UdpDictServer(9090);
        server.start();
    }
}

运行结果:
在这里插入图片描述
在这里插入图片描述

TCP流套接字编程

ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。

ServerSocket 构造方法:

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

ServerSocket 方法

方法签名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,,并基于该Socket建立与客户端的连接,否则阻塞等待
void close()关闭此套接字

Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

Socket 构造方法:

方法签名方法说明
Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

Socket 方法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

TCP客户端服务器回显服务程序

TCP和UDP不同,TCP是需要建立连接,并且通过对文件读写的方式以字节为单位进行传输。对于TCP传输,java 中提供了2个类来进行数据的传输,一个是ServerSocket,用于服务端接受客户端的连接,另一个是Socket,用于服务端和客户端之间的通信。

TCP客户端服务器简单翻译服务程序

客户端程序设计:

  1. 创建Socket对象
  2. 初始化服务端的ip和端口
  3. 启动客户端,发送数据并接收返回数据

示例代码

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoClient {
    /**
     * 创建Socket对象
     */
    private Socket socket;

    /**
     *
     * @param serverIP  服务端ip
     * @param serverPort 服务端口
     * @throws IOException
     */
    public TcpEchoClient(String serverIP, int serverPort) throws IOException {
        socket = new Socket(serverIP, serverPort);
    }

    /**
     * 启动客户端
     */
    public void start() {
        System.out.println("客户端启动成功!");
        //用户输入数据
        Scanner input = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream()) {
            try (OutputStream outputStream = socket.getOutputStream()) {
                while (true) {
                    //请输入数据
                    System.out.print("请输入需要传输的数据!->");
                    String request = input.next();
                    //发送数据
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(request);
                    //刷新缓冲区
                    printWriter.flush();
                    //接收回应
                    Scanner receiverScanner = new Scanner(inputStream);
                    String response = receiverScanner.next();
                    //输出数据
                    System.out.printf("我的请求:%s 它的回应:%s\n", request, response);
                }
            }
         } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9092);
        client.start();
    }
}

服务端设计:

  1. 创建socket对象,初始化端口
  2. 接收客户端的数据,返回客户端的数据。

示例代码

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoServer {
    /**
     *  创建socket对象
     */
    private ServerSocket serverSocket;
    /**
     *  初始化服务端端口
     */
    public TcpEchoServer(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

    /**
     * 启动服务器
     * @throws IOException
     */
    public void start() throws IOException {
        System.out.println("服务器准备就绪!");
        while (true) {
            // 3. 接收客户端的数据
            Socket clientSocket = serverSocket.accept();
            // 4. 接收 处理 回应数据
			processContain(clientSocket);
		}
    }
    private void processContain(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 服务器正式与客户端建立连接!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
            try (InputStream inputStream = clientSocket.getInputStream()) {
                try (OutputStream outputStream = clientSocket.getOutputStream()) {
                    //接收数据 使用Scanner比InputStream的原生方法read更方便
                    Scanner receiveScanner = new Scanner(inputStream);
                    while (true) {
                        if (!receiveScanner.hasNext()) {
                            System.out.printf("[%s:%d] 服务器与客户端已经断开连接!\n", clientSocket.getInetAddress().toString(),
                                    clientSocket.getPort());
                            break;
                        }
                        String request = receiveScanner.next();
                        //处理数据
                        String response = process(request);
                        //发送数据,为了方便,我们可以使用PrintWriter类将OutputStream类对象包裹起来,就是用来把数据打印到文件里面
                        PrintWriter printWriter = new PrintWriter(outputStream);
                        printWriter.println(response);
                        //及时刷新缓冲区
                        printWriter.flush();
                        //输出回应信息
                        System.out.printf("[%s:%d] 收到的请求: %s  回应: %s\n", clientSocket.getInetAddress().toString(),
                                clientSocket.getPort(), request, response);
                    }
                }
            }catch (IOException e) {
                e.printStackTrace();
            } finally {
                //释放资源 相当于挂断电话
                clientSocket.close();
            }
    }
    public String process(String data) {
        return data;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9092);
        server.start();
    }
}

运行结果:
在这里插入图片描述
在这里插入图片描述

上述服务端代码无法处理多个客户端的请求,因为每次建立连接服务端只能和一个客户端连接,当服务端和客户端建立连接后,处理数据会进入processContain方法,如果此时又有一个客户端尝试建立连接发送数据,就无法跳出processContain中的循环去建立新的连接,导致客户端和服务端连接不上,为了解决上述问题,可以使用线程,每次建立连接就分配一个线程去处理该连接。

线程版服务端代码

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoServer {
    /**
     *  创建socket对象
     */
    private ServerSocket serverSocket;
    /**
     *  初始化服务端端口
     */
    public TcpEchoServer(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

    /**
     * 启动服务器
     * @throws IOException
     */
    public void start() throws IOException {
        System.out.println("服务器准备就绪!");
        while (true) {
            // 3. 接收客户端的数据
            Socket clientSocket = serverSocket.accept();
            // 4. 接收 处理 回应数据
			Thread t= new Thread(() -> {
                try {
                    processContain(clientSocket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            t.start();
		}
    }
    private void processContain(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 服务器正式与客户端建立连接!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
            try (InputStream inputStream = clientSocket.getInputStream()) {
                try (OutputStream outputStream = clientSocket.getOutputStream()) {
                    //接收数据 使用Scanner比InputStream的原生方法read更方便
                    Scanner receiveScanner = new Scanner(inputStream);
                    while (true) {
                        if (!receiveScanner.hasNext()) {
                            System.out.printf("[%s:%d] 服务器与客户端已经断开连接!\n", clientSocket.getInetAddress().toString(),
                                    clientSocket.getPort());
                            break;
                        }
                        String request = receiveScanner.next();
                        //处理数据
                        String response = process(request);
                        //发送数据,为了方便,我们可以使用PrintWriter类将OutputStream类对象包裹起来,就是用来把数据打印到文件里面
                        PrintWriter printWriter = new PrintWriter(outputStream);
                        printWriter.println(response);
                        //及时刷新缓冲区
                        printWriter.flush();
                        //输出回应信息
                        System.out.printf("[%s:%d] 收到的请求: %s  回应: %s\n", clientSocket.getInetAddress().toString(),
                                clientSocket.getPort(), request, response);
                    }
                }
            }catch (IOException e) {
                e.printStackTrace();
            } finally {
                //释放资源 相当于挂断电话
                clientSocket.close();
            }
    }
    public String process(String data) {
        return data;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9092);
        server.start();
    }
}

运行结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

server.png)]TCP客户端服务器简单翻译服务程序

该逻辑和UDP实现一致

示例代码

import java.io.IOException;
import java.util.HashMap;

public class TcpDictServer extends TcpEchoServer{
    private final HashMap<String, String> dict = new HashMap<>();
    public TcpDictServer(int port) throws IOException {
        super(port);
        dict.put("cat", "小猫");
        dict.put("dog", "小狗");
    }

    @Override
    public String process(String data) {
        return dict.getOrDefault(data, "词库为找到该单词!");
    }

    public static void main(String[] args) throws IOException {
        TcpDictServer server = new TcpDictServer(9092);
        server.start();
    }
}

运行结果:
在这里插入图片描述

TCP中的长短连接

TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

  • 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。

  • 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。

对比以上长短连接,两者区别如下:

  • 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。

  • 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。

  • 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等.

一发一收(短连接)

以下为一个客户端一次数据发送,和服务端多次数据接收(一次发送一次接收,可以接收多次),即只有客户端请求,但没有服务端响应的示例:

TCP服务端

示例代码

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

public class TcpServer {
    //服务器socket要绑定固定的端口
    private static final int PORT = 8888;

    public static void main(String[] args) throws IOException {
        // 1.创建一个服务端ServerSocket,用于收发TCP报文
        ServerSocket server = new ServerSocket(PORT);
        // 不停的等待客户端连接
        while (true) {
            System.out.println("------------------------------------------------ ---");
            System.out.println("等待客户端建立TCP连接...");
            // 2.等待客户端连接,注意该方法为阻塞方法
            Socket client = server.accept();
            System.out.printf("客户端IP:%s%n",
                    client.getInetAddress().getHostAddress());
            System.out.printf("客户端端口号:%s%n", client.getPort());
            // 5.接收客户端的数据,需要从客户端Socket中的输入流获取
            System.out.println("接收到客户端请求:");
            InputStream is = client.getInputStream();
            // 为了方便获取字符串内容,可以将以上字节流包装为字符流
            BufferedReader br = new BufferedReader(new InputStreamReader(is,
                    "UTF-8"));
            String line;
            // 一直读取到流结束:TCP是基于流的数据传输,一定要客户端关闭Socket输出流才表示服务端接收IO输入流结束
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
            // 6.双方关闭连接:服务端是关闭客户端socket连接
            client.close();
        }
    }
}

如果没有客户端请求,此时代码是阻塞等待在 server.accept() 代码行,直到有新的客户端申请建立连接。

TCP客户端

示例代码

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

public class TcpClient {
    //服务端IP或域名
    private static final String SERVER_HOST = "localhost";
    //服务端Socket进程的端口号
    private static final int SERVER_PORT = 8888;

    public static void main(String[] args) throws IOException {
        // 3.创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接
        Socket client = new Socket(SERVER_HOST, SERVER_PORT);
        // 4.发送TCP数据,是通过socket中的输出流进行发送
        OutputStream os = client.getOutputStream();
        // 为了方便输出字符串作为发送的内容,可以将以上字节流包装为字符流
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
        // 4-1.发送数据:
        pw.println("hello world!");
        // 4-2.有缓冲区的IO操作,真正传输数据,需要刷新缓冲区
        pw.flush();
        // 7.双方关闭连接:客户端关闭socket连接
        client.close();
    }
}

运行结果:

在这里插入图片描述
在这里插入图片描述

客户端向服务端发送数据后就会重新断开连接,而服务端不会断开会等待下一个客户端的连接。


各位看官如果觉得文章写得不错,点赞评论关注走一波!谢谢啦!。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/850296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

右值引用与移动语义与完美转发

右值引用 右值 什么是右值&#xff0c;没有地址临时数据的我们称之为右值 我们无法对10、aa、字符串取地址的值我们称之为右值。因为他们是临时数据&#xff0c;并不保存再内存中&#xff0c;所以我们右值没有地址&#xff0c;也无法被赋值&#xff08;除const外&#xff0c;左…

【Elasticsearch】学好Elasticsearch系列-分词器

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 先看后赞&#xff0c;养成习惯。 点赞收藏&#xff0c;人生辉煌。 文章目录 规范化&#xff1a;normalization字符过滤器&#xff1a;character filterHTML Strip Character FilterMapping Character FilterP…

IMV6.0

一、背景 经历了多个版本&#xff0c;基础内容在前面&#xff0c;可以使用之前的基础环境&#xff1a; v1&#xff1a; https://blog.csdn.net/wtt234/article/details/132139454 v2&#xff1a; https://blog.csdn.net/wtt234/article/details/132144907 v3&#xff1a; https…

vue实现pdf预览功能

背景&#xff1a;材料上传之后点击预览实现在浏览器上预览的效果 效果如下&#xff1a; 实现代码如下&#xff1a; //预览和下载操作 <el-table-column fixed"right" label"操作" width"210"><template #default"scope">…

JAVA Android 正则表达式

正则表达式 正则表达式是对字符串执行模式匹配的技术。 正则表达式匹配流程 private void RegTheory() {// 正则表达式String content "1998年12月8日&#xff0c;第二代Java平台的企业版J2EE发布。1999年6月&#xff0c;Sun公司发布了第二代Java平台(简称为Java2) &qu…

每次执行@Test方法前都执行一次DB初始化(SpringBoot Test + JUnit5环境)

引言 在执行单元测试时&#xff0c;可以使用诸如H2内存数据库替代线上的Mysql数据库等&#xff0c;如此在执行单元测试时就能尽可能模拟真实环境的SQL执行&#xff0c;同时也无需依赖线上数据库&#xff0c;增加了测试用例执行环境的可移植性。而使用H2数据库时&#xff0c;通…

Node.js |(二)Node.js API:fs模块 | 尚硅谷2023版Node.js零基础视频教程

学习视频&#xff1a;尚硅谷2023版Node.js零基础视频教程&#xff0c;nodejs新手到高手 文章目录 &#x1f4da;文件写入&#x1f407;writeFile 异步写入&#x1f407;writeFileSync 同步写入&#x1f407;appendFile / appendFileSync 追加写入&#x1f407;createWriteStrea…

点成分享丨qPCR仪的原理与使用——以Novacyt产品为例

近年来&#xff0c;PCR检测在多种领域发挥着巨大的作用。短时高效和即时监测都成为了PCR仪发展的方向。作为世界领先的制造商之一&#xff0c;Novacyt公司为来自全球多个国家和行业的用户提供了优质的qPCR仪。 MyGo Mini S qPCR仪是一种紧凑型的实时qPCR仪&#xff0c;非常适合…

【算法|双指针系列No.1】leetcode283. 移动零

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

c++进阶--二叉搜索树

目录 前言 一、二叉搜索树 1.二叉搜索树概念 2.二叉搜索树操作 二、二叉搜索树实现 0.定义一个节点 1.定义一棵树 2.增删改查 2.1.查找 2.2.插入 2.3.删除 2.3.1非递归删除法 a.只有左孩子 -- 删除14 b.只有右孩子-- 删除10 c.有左右孩子--删除8 2.3.2递归删除…

【论文总结】Chatting with GPT-3 for Zero-Shot Human-Like Mobile Automated GUI Testing

与GPT-3对话进行零尝试人类化移动自动化GUI测试 摘要&#xff1a; 移动应用在人们的日常生活中变得不可或缺&#xff0c;而自动化图形用户界面&#xff08;Graphical User Interface&#xff0c;GUI&#xff09;测试广泛用于应用程序质量保证。对于自动化GUI测试&#xff0c;越…

rv1126更新rknpu驱动教学

测试平台&#xff1a;易佰纳rv1126 38板 查看板端版本-------------------------------------------------- 1&#xff1a;查看npu驱动版本 dmesg | grep -i galcore&#xff0c;可以看到版本为6.4.3.5 2&#xff1a;查看rknn-server版本 strings /usr/bin/rknn_server | g…

新能源电动车充电桩控制主板的材料选择

新能源电动车充电桩控制主板的材料选择 你是否想过&#xff0c;汽车充电桩控制主板的材料选择竟然还有这么多讲究?不再只是简单的充电问题&#xff0c;而是涉及到耐高温、耐高压、耐腐蚀、耐潮湿、耐油污、低热耗等方方面面。 首先&#xff0c;安全性是材料选择的首要考虑因素…

mysql自增主键不连续情况分析

1.唯一键冲突 比如increnment_test中已经存在了co1为3的记录,当再插入col1为3的记录时,就会出现主键不唯一错误,但此时自增主键已经1,所以会发生主键不连续情况 DROP TABLE IF EXISTS increnment_test; CREATE TABLE increnment_test (id int(0) NOT NULL AUTO_INCREMENT,col…

svg使用技巧

什么是svg SVG 是一种基于 XML 语法的图像格式&#xff0c;全称是可缩放矢量图&#xff08;Scalable Vector Graphics&#xff09;。其他图像格式都是基于像素处理的&#xff0c;SVG 则是属于对图像的形状描述&#xff0c;所以它本质上是文本文件&#xff0c;体积较小&#xf…

Python-OpenCV中的图像处理

Python-OpenCV中的图像处理 颜色空间转换物体跟踪获取HSV的值几何变换图像缩放图像平移图像旋转仿射变换透视变换 图像阈值单阈值自适应阈值Otsus二值化 颜色空间转换 在 OpenCV 中有超过 150 中进行颜色空间转换的方法。但是你以后就会 发现我们经常用到的也就两种&#xff1…

ZMQ发布订阅模式二次封装

ZeroMQ 参考ZMQ从入门到掌握一 ZeroMQ是一种基于消息队列的多线程网络库&#xff0c;其对套接字类型、连接处理、帧、甚至路由的底层细节进行抽象&#xff0c;提供跨越多种传输协议的套接字。ZeroMQ 并不是一个对 socket 的封装&#xff0c;不能用它去实现已有的网络协议。它有…

加密、解密、编码

urlencode urlencode_百度百科 Base64是一种二进制到文本的编码方式&#xff0c;而且编码出的字符串只包含ASCII基础字符 下图是Base64码表&#xff0c;可以看到从0到63的每个数字都对应一个上面的一个字符。 文件-base64字符串互转 sun.misc包中的类 try (FileOutputStre…

Node.Js安装与配置教程

目录 1.下载官网 2.选择安装路径 3.添加环境变量 4.验证是否安装成功 5.修改模块下载位置 (1)查看npm默认存放位置 6.在node.js安装目录下&#xff0c;创建两个文件夹 7.修改默认文件夹 8.测试默认位置是否更改成功 9.安装报错解决办法 10.路径未更改成功解决办法 …

MIT 6.830数据库系统 -- lab six

MIT 6.830数据库系统 -- lab six 项目拉取引言steal/no-force策略redo log与undo log日志格式和检查点 开始回滚练习1&#xff1a;LogFile.rollback() 恢复练习2&#xff1a;LogFile.recover() 测试结果疑问点分析 项目拉取 原项目使用ant进行项目构建&#xff0c;我已经更改为…