Java网络编程:(socket API编程:TCP协议的 socket API -- 回显程序的服务器端程序的编写)

Java网络编程:(socket API编程:TCP协议的 socket API -- 回显程序的服务器端程序的编写)

文章目录

    • [Java网络编程:(socket API编程:TCP协议的 socket API -- 回显程序的服务器端程序的编写)](#Java网络编程:(socket API编程:TCP协议的 socket API -- 回显程序的服务器端程序的编写))
  • 观前提醒
  • [1. 准备工作](#1. 准备工作)
    • [1.1 创建 ServerSocket对象](#1.1 创建 ServerSocket对象)
    • [1.2 通过构造方法,初始化 ServerSocket对象](#1.2 通过构造方法,初始化 ServerSocket对象)
    • [1.3 创建 start 方法,用来启动服务器](#1.3 创建 start 方法,用来启动服务器)
    • [1.4 编写一个死循环,让服务器不断工作](#1.4 编写一个死循环,让服务器不断工作)
    • [1.5 结束准备工作](#1.5 结束准备工作)
  • [2. 建立连接](#2. 建立连接)
    • [2.1 举例说明,并理解](#2.1 举例说明,并理解)
    • [2.2 使用 accept 方法,建立连接](#2.2 使用 accept 方法,建立连接)
    • [2.3 举例理解两个Socket对象的工作职责](#2.3 举例理解两个Socket对象的工作职责)
    • [2.4 客户端没有发起连接](#2.4 客户端没有发起连接)
    • [2.5 阶段总结](#2.5 阶段总结)
  • [3. 处理客户端请求和响应前的准备工作](#3. 处理客户端请求和响应前的准备工作)
    • [3.1 单独封装成一个方法](#3.1 单独封装成一个方法)
    • [3.2 打印日志](#3.2 打印日志)
    • [3.3 获取两个流对象](#3.3 获取两个流对象)
      • [try with resource 语法说明](#try with resource 语法说明)
      • 编写代码
    • [3.4 阶段总结](#3.4 阶段总结)
  • [4. 处理请求,得到响应](#4. 处理请求,得到响应)
  • [5. 为 TCPEchoServer类(服务器端程序),编写main方法,启动服务器端程序](#5. 为 TCPEchoServer类(服务器端程序),编写main方法,启动服务器端程序)
  • [6. 总结编写步骤](#6. 总结编写步骤)
    • [1. 准备工作](#1. 准备工作)
    • [2. 和客户端建立连接](#2. 和客户端建立连接)
    • [3. 处理客户端请求之前的准备工作](#3. 处理客户端请求之前的准备工作)
    • [4. 处理请求,得到响应](#4. 处理请求,得到响应)
    • [5. 为 TCPEchoServer类(服务器端程序),编写main方法,启动服务器端程序](#5. 为 TCPEchoServer类(服务器端程序),编写main方法,启动服务器端程序)
  • [6. 目前的服务器端代码:](#6. 目前的服务器端代码:)
  • [7. 总结](#7. 总结)

观前提醒

如果你是第一次点击这篇博客进来的,需要你先看完这篇博客,以及它里面提到的博客,再过来看这篇博客:
Java网络编程(4):(socket API编程:TCP协议的 socket API -- 回显程序)

1. 准备工作

先构建一个类:TCPEchoServer(这个类,表示服务器程序)

当然,你也可以自己命名。

1.1 创建 ServerSocket对象

我们编写的是 TCP 协议 的程序,使用的是 TCP协议的 socket API,并且这里是对服务器端程序的编写 ,我们使用的类是:ServerSocket

代码:

java 复制代码
private ServerSocket serverSocket = null;

1.2 通过构造方法,初始化 ServerSocket对象

我们通过 TCPEchoServer 这个类的构造方法,在它的构造方法里面,调用 ServerSocket 类的构造方法 ServerSocket(int port),对 Socket 对象 ,进行端口号的绑定

代码:

java 复制代码
    public TCPEchoServer(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

1.3 创建 start 方法,用来启动服务器

通过start 方法,用来启动服务器。

代码:

java 复制代码
    public void start(){
        System.out.println("启动服务器!");
        
        
    }

接下来,就是在 start 方法里面,实现服务器端程序的全部功能。

1.4 编写一个死循环,让服务器不断工作

对于服务器 来说,客户端什么时候发请求,发送多少个请求 ,我们是无法预测 的。

例如:

你玩游戏(手机里的游戏APP,就是客户端),游戏对应的服务器端(也就是服务器),是不知道你什么时候登录游戏,发送一个登录游戏的请求的,也不知道,在一个小时内,有多少个玩家发送了多少个登录请求,游戏公司无法预测,所以,游戏的服务器,是 7 X 24 小时,不断工作的,只有游戏版本更新,系统维护的时候,服务器才会关闭。

服务器关闭了,玩家登录游戏的时候,发送的登录请求,服务器关闭了,无法作出响应,你就登录不了游戏了。

只有更新结束,游戏系统维护好了以后,才能正常玩游戏。

通过上述例子可知:服务器端程序(服务器)通常都需要有一个 死循环,做到 7 X 24小时运行。

1.5 结束准备工作

代码:

java 复制代码
import java.io.IOException;
import java.net.ServerSocket;


public class TCPEchoServer {

    private ServerSocket serverSocket = null;

    public TCPEchoServer(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

    public void start(){
        System.out.println("启动服务器!");

//        服务器需要不断的工作,用一个死循环实现这一点
        while(true){
            
//            接下来在这里继续编写代码
//            ......
            
        }
        
    }

    
}

2. 建立连接

在while循环中,与UDP那边的服务器端程序不同。
UDP协议 那边,你可以直接读取请求数据并解析,根据请求,计算响应,最后将响应返回给客户端了。

但是 TCP协议 不同,需要先处理客户端发来的连接

这两者的差别就是因为,TCP是有连接的,UDP是无连接的。

如何理解 TCP 这么做的原因呢?

你可以理解为打电话

2.1 举例说明,并理解

TCP 先建立连接,好比如我们生活中打电话。

打电话的情景:

我需要打电话给老板汇报工作我(客户端)需要先拨号 ,给老板的手机(服务器)发送通话请求

我这边发送请求之后,老板的手机就响铃提醒了,有人打电话,是否要接听电话,此时需要老板点击 " 接通 ",我才能和老板进行通话。

否则,老板不点击 " 接通 " ,我这边,说再多东西,都是白搭,因为老板根本就不知道我说了什么,原因就是因为我们两个没有建立连接,无法进行沟通

上述过程,我(打电话那一方)就类似于与客户端,发起了连接的请求,老板(需要点击 " 接通 " 电话的那一方),就类似于服务器端,需要与发起连接的客户端,建立连接。

只要服务器端,和客户端先建立连接 了,后续客户端发送过来的请求信息,服务器端才能接收的到,并进行处理,最后将响应返回给客户端。

2.2 使用 accept 方法,建立连接

建立连接 的操作,我们需要使用到 accept() 方法。

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

相当于 客户端 给 服务器端打电话,服务器端调用了 accept方法,就接通了电话,可以进行通信了。

使用我们创建好的 Socket对象,调用 accept方法:

同时,这个方法还有一个返回值 :Socket

我们需要接收这个返回值:

这个返回值(clientSocket),是建立连接之后,返回的一个 Socket对象,是后续客户端和服务器端进行通信的一个对象。

后续,就是通过 读写 clientSocket ,和客户端进行通信

可能大家还是对于这两个对象,有点懵,下面举个例子,看看你能不能更好的理解。

2.3 举例理解两个Socket对象的工作职责

不知道大家有没有在街上看到过西装革履的小哥或小姐姐。

一般看到这样的人,大概率是销售,只有少部分是其他。

这个销售可以销售很多东西,我就以卖房为例:

西装革履的销售小哥,在马路街道上,发传单或者口头呼喊着:" 这里有一个不错的楼盘,快来看看,买到就是赚到... ... " 之类的。

此时,有一位帅小伙刚好想买房,就过去问了问,于是,销售小哥就把他带到了售楼中心,小哥一挥手,又来了一个西装革履的小姐姐,这位小姐姐是专业的销售顾问(也还是销售),接下来就由这位小姐姐给这位帅小伙介绍楼盘的详细情况。

小哥一转身,又出去招揽客人去了。

像这种,小哥在马路街道上,进行揽客,小姐姐,负责给客人提供详细的服务,就是这两个 Socket对象 的工作职责。



serverSocket :相当于小哥,在马路街道上招揽客人

多个客户端要进行连接的时候,这个 Socket对象(serverSocket),调用 accept方法,与客户端建立连接,之后,把客人(建立了连接的客户端),交给了小姐姐(clientSocket)。

clientSocket :相当于小姐姐给客人(建立了连接的客户端)提供详细的服务

我们跟客户端进行读写发送,接收数据 ,就是用过这个 Socket对象(clientSocket ),来进行负责的。

2.4 客户端没有发起连接

如果客户端没有发起连接 ,此时 accept方法 就会阻塞等待

跟 UDP 那边的 receive方法是类似的,如果客户端没有发送请求数据过来服务器端,receive方法,就会阻塞等待。

2.5 阶段总结

这里,我们主要是完成了对客户端的连接建立,后续,可以通过 clientSocket ,来对客户端的请求和响应进行处理了。

代码:

java 复制代码
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;


public class TCPEchoServer {

    private ServerSocket serverSocket = null;

    public TCPEchoServer(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("启动服务器!");

//        服务器需要不断的读取客户端的请求数据,用一个死循环实现这一点
        while(true){
            // 对于 TCP 来说,需要先处理客户端发来的连接
            // clientSocket 这个变量,就表示和服务器和客户端进行交流的一个对象,
            // 后续,就通过读写 clientSocket ,和客户端进行通信
            Socket clientSocket = serverSocket.accept();
            

        }

    }


}

3. 处理客户端请求和响应前的准备工作

与客户端建立连接之后,我们就可以对客户端的请求和响应,进行处理了。

但是,在那之前,我们仍要做一些准备工作:

处理的过程,封装成一个方法

然后打印日志,说明客户端已经连接成功了,

最后获取两个流对象,InputStream 和 OutStream。

3.1 单独封装成一个方法

当我们和一个客户端建立连接之后,就要对这个客户端的请求和响应,进行处理,这是一个单独的功能模块了。

对于一个单独的功能模块,建议用另一个类,或者封装成一个方法,来进行代码的编写,这样以后程序维护起来比较方便。

这个程序,对于客户端的请求和响应的处理过程,我们封装成一个方法:processConnect(clientSocket)

因为后续对客户端的读写,都是通过 clientSocket,所以,把它当作参数,传递给这个方法。

所以,这个方法,就是处理一个客户端连接之后的工作

一个客户端建立连接之后的操作,可能涉及到这个客户端很多个的请求和响应。

因为一个客户端,建立连接之后 ,并不一定只发一个请求,有可能是多个请求,我们也要处理请求,得到多个响应

3.2 打印日志

我们可以在 processConnect(clientSocket) 方法中,打印一个日志,输出 clientSocket 的 IP 和 端口号,判断客户端是否已经建立连接了。

打印的方式,通过 C语言 中的 printf 方式进行打印:

java 复制代码
//        打印一个日志,输出 clientSocket 的 IP和端口号
        System.out.printf("[%s : %d] 客户端连接成功,客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());

这里,我们使用 Socket 提供的 getInetAddress() 方法,获取到客户端的 IP地址

方法名 方法说明
InetAddress getInetAddress() 返回套接字所连接的地址

clientSocket.getPort(),是获取到客户端的端口号。

3.3 获取两个流对象

TCP协议 并没有像 UDP协议 那里一样,提供了send 和 receive 方法。

而是提供了InputStream getInputStream()OutputStream getOutputStream()这两个方法,获取到 字节输入流对象字节输出流对象

Socket 本身,是不会进行读写操作 的,而是借助两个字节流对象,InputStream 和 OutStream,完成类似于 UDP协议的 send 和 receive 完成的工作。

所以,想要处理请求,得到响应,我们就需要获取到字节输入流对象 和 字节输出流对象(InputStream 和 OutputStream)。

try with resource 语法说明

Java 文件操作 和 IO(3)-- Java文件内容操作(1)-- 字节流操作 这篇博客中,我们介绍了一种定义 字节流对象 的方式:try with resource语法。

try with resource 语法,就是把需要进行关闭的资源 (需要执行关闭操作 close方法 的类),放到 try 后面的括号 里面,只要出了 try 的代码块,就会自动调用该资源的 close方法。

这个语法,不仅可以达到关闭文件资源的操作,而且,也简化了代码,使代码看起来更加美观!

更多细节,点击上面的博客连接,进行了解。

编写代码

代码:

java 复制代码
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            


        } catch (IOException e) {
            throw new RuntimeException(e);
        }

拿到输入流对象(InputStream )和输出流对象(OutputStream )

之后,我们读客户端的请求 的时候,我们使用输入流 对象(InputStream
返回响应给客户端 的时候,我们使用输出流 对象(OutputStream ),把响应写回给客户端

之后,我们就在 try 代码块里面,开始真正处理客户端的请求,并得到响应。

3.4 阶段总结

这里我们主要是在处理客户端的请求和响应之前,做了些准备工作,包括:

  • 处理的过程,封装成一个方法
  • 打印日志,检测客户端是否已经连接成功
  • 获取两个流对象,InputStream 和 OutStream

代码:

java 复制代码
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;


public class TCPEchoServer {

    private ServerSocket serverSocket = null;

    public TCPEchoServer(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("启动服务器!");

//        服务器需要不断的读取客户端的请求数据,用一个死循环实现这一点
        while(true){
            // 对于 TCP 来说,需要先处理客户端发来的连接
            // clientSocket 这个变量,就表示和服务器和客户端进行交流的一个对象,
            // 后续,就通过读写 clientSocket ,和客户端进行通信
            Socket clientSocket = serverSocket.accept();
//            处理一个客户端的请求和响应
            processConnect(clientSocket);



        }

    }

//    处理一个客户端的连接
//    一个客户端,可能会发送多个请求,就要处理多个请求,得到多个响应
    private void processConnect(Socket clientSocket) {
//        打印一个日志,输出 clientSocket 的 IP和端口号
        System.out.printf("[%s : %d] 客户端连接成功,客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());

        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){

//            真正处理客户端的请求,并得到响应
//            ......

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

}

4. 处理请求,得到响应

在上面我们说过:因为一个客户端,建立连接之后,并不一定只发一个请求,有可能是多个请求,我们也要处理请求,得到多个响应。

处理请求的步骤分为三步,但是,处理请求的这三个步骤,一次只能处理一个请求,如果是一个客户端,有多个请求,就需要多次处理,需要使用一个循环,处理多个请求。

并且,这个循环是一个死循环,因为你不知道客户端到底会发多少个请求过来。

代码:

处理请求的步骤

接下来的步骤,和 UDP 那边,处理请求的步骤是一样的了。

处理请求 的过程,典型的服务器都是分为三个步骤的,分别是:

  1. 读取请求,并解析请求数据
  2. 根据请求,计算响应(服务器最关键的逻辑)

本身服务器就是要提供各种各样的功能的,比如:百度搜索 " 如何学习好Java ",我的请求是:" 如何学习好Java ",服务器端的程序就会根据请求,搜索出很多个相关的页面。

说起来很简短,但是,在服务器端程序上,会经过各种各样的逻辑处理,处理请求,计算响应,最终返回结果。

所以,这一块是服务器最关键的逻辑。

但是,我们这篇博客编写的是回显服务器,所以,这个环节就相当于省略了,请求是什么,我们就返回什么。

  1. 把响应返回给客户端

所以,一个典型的服务器,都是按照这三个步骤来进行处理的,不仅是我们编写的这个回显服务器,未来,你从事编写服务器端程序(服务器)相关的工作岗位,涉及到更加复杂的,商业级别的服务器,总体上的逻辑,也就是这三步。

只不过,公司的服务器端程序,会考虑的更多,支持的功能会更多,会很复杂,上至几十万行的代码量。

接下来,我们就根据这三个步骤,进行代码的编写。

由于篇幅太长了,我将处理请求的这三个步骤的代码编写,放到一个新的博客里面,大家需要点击进去这里看:

Java网络编程:(socket API编程:TCP协议的 socket API -- 服务器端处理请求的三个步骤)

5. 为 TCPEchoServer类(服务器端程序),编写main方法,启动服务器端程序

代码:

java 复制代码
    public static void main(String[] args) throws IOException {
        TCPEchoServer tcpEchoServer = new TCPEchoServer(9090);
        tcpEchoServer.start();
    }

6. 总结编写步骤

看完上面的步骤,可能你还有点懵,我来带你会看一下,这个程序,整一个的编写流程:

1. 准备工作

这一步,我们主要是为了后续的编写,做一些准备工作。

准备工作有:

  1. 创建服务器端的 ServerSocket对象 ,并通过 TCPEchoServer 这个服务器类的构造方法,绑定服务器端程序的端口
  2. 创建 start 方法,让实例化的对象,调用这个方法,启动服务器程序。
  3. 为了让服务器不断的工作,在 start 方法中,创建一个死循环

2. 和客户端建立连接

这个时候,是启动服务器后,服务器进行工作,需要做的事情,所以,代码应该是在 start方法 中的死循环中编写。

建立连接:

通过 服务器端的 ServerSocket对象,调用 accept方法 ,和客户端建立连接,并接收返回值。

3. 处理客户端请求之前的准备工作

这一步,是和客户端建立连接之后,客户端回发送请求过来之前,我们需要做准备工作,然后再进行请求的处理。

准备工作:

  1. 处理客户端发送的请求 ,是一个独立的功能模块,用一个单独的方法封装 一下,此处是自定义的,如: processConnect(clientSocket);
  2. 打印一个日志,输出 clientSocket(可以理解为建立连接之后,返回的客户端的代表) 的 IP 和 端口号,判断客户端是否已经建立连接了。
  3. 通过 try with resource 语法获取 clientSocket 的两个流对象,后续通过他们,读取客户端发送过来的请求 和 返回响应给客户端。

4. 处理请求,得到响应

从这一步开始,我们就可以根据处理请求的三个步骤,进行代码的编写了。

由于与客户端建立连接之后,一个客户端可能发送多个请求 ,需要建立一个死循环,不断处理发送过来的请求,直到下一次没有请求发送过来了,就结束死循环,也断开和客户端的连接。

并且,处理请求的三个步骤的代码,也在这个死循环里面写。

代码编写:

  1. 读取请求,并解析(以下两种,二选一)
  • 可以使用 InputStream 提供的 read 方法读取数据字节流 的方式),然后手动转化为 String,方便后续操作
  • 也可以通过 Scanner 的方式(字符流的方式),进行读取。
  1. 根据请求,计算响应
    回显程序:客户端发送的请求,就是返回给客户端的响应
  2. 将响应返回给客户端(以下两种,二选一)
  • 使用 OutputStream 提供的 write方法 ,将响应写到客户端(返回给客户端),这是 字节流的方式。
  • 也可以使用 PrintWriter 将响应写回(发送)给客户端,这是字符流的方式

5. 为 TCPEchoServer类(服务器端程序),编写main方法,启动服务器端程序

编写main方法,创建一个 TCPEchoServer类 的对象,调用 start方法,启动服务器端程序。

6. 目前的服务器端代码:

java 复制代码
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TCPEchoServer {

    private ServerSocket serverSocket = null;

    public TCPEchoServer(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("启动服务器!");

//        服务器需要不断的读取客户端的请求数据,用一个死循环实现这一点
        while(true){
            // 对于 TCP 来说,需要先处理客户端发来的连接
            // clientSocket 这个变量,就表示和服务器和客户端进行交流的一个对象,
            // 后续,就通过读写 clientSocket ,和客户端进行通信
            Socket clientSocket = serverSocket.accept();
//            处理一个客户端的请求和响应
            processConnect(clientSocket);



        }

    }

//    处理一个客户端的连接
//    一个客户端,可能会发送多个请求,就要处理多个请求,得到多个响应
    private void processConnect(Socket clientSocket) {
//        打印一个日志,输出 clientSocket 的 IP和端口号
        System.out.printf("[%s : %d] 客户端连接成功,客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());

        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
//            针对 InputStream,采用 Scanner 进行读取操作,套了一层
            Scanner scanner = new Scanner(inputStream);
//            针对 OutputStream ,采用 PrintWriter 进行写操作,套了一层
            PrintWriter writer = new PrintWriter(outputStream);
//            一个客户端,可能会发送多个请求,就要处理多个请求,得到多个响应
//            所以,需要使用 while 循环,处理多次
//            不知道客户端到底会发送多少个请求,所以,使用死循环
            while(true){
//            这里的工作分为三个步骤:
//                1. 读取请求,并解析请求数据

//                字节流的方式,进行读取操作
//                byte[] requestByte = new byte[1024];
//                int byteNum = inputStream.read(requestByte);
//////                为了后续的处理更加方便,需要手动转换为 String
//                String request = new String(requestByte,0,byteNum);


//                对 InputStream 进行套壳,使用 Scanner 的方式,进行读取
                if (!scanner.hasNext()){
//                    客户端没有请求发送过来了,就不需要继续读取请求,并处理了
//                    可以断开和客户端的连接(结束循环)

//                    打印一个日志,显示 服务器 和 客户端,断开了连接
                    System.out.printf("[%s : %d] 断开连接,客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
                    break;
                }
//                使用 Scanner 读取请求
                String request = scanner.next();


//                2. 根据请求,计算响应(服务器最关键的逻辑)
                String response = precess(request);


//                3. 把响应返回给客户端

//                字节流的方式,将响应写回(发送)给客户端
//                使用字节输出流对象提供的 write方法,将响应写到客户端(返回给客户端)
//                outputStream.write(response.getBytes());
//                OutputStream是以字节为单位,进行写操作的
//                response.getBytes():当前响应是一个String,需要拿到响应中的字节数据,才能进行写操作。

//                对 OutputStream 进行套壳,使用 PrintWriter 的方式,将响应写回(发送)给客户端
                writer.println(response);


//                4.打印日志,显示 客户端发送的请求 和 服务器端返回的响应信息
                System.out.printf("[%s : %d] 请求(req):%s,响应信息(resp):%s \n",clientSocket.getInetAddress(),clientSocket.getPort(),
                                    request,response);

            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private String precess(String request) {
        return request;
    }

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

7. 总结

这篇博客,完成了对服务器端程序代码的编写,但是,当我们配合客户端一起运行的时候,会有问题

这个问题是使用了 PrintWriter 方式后,才会产生的问题

关于缓冲区的问题。

怎么回事?

我们在这篇博客中解决:
Java网络编程(4):(socket API编程:TCP协议的 socket API -- 回显程序)

最后,如果这篇博客能帮到你的,请你点点赞,有写错了,写的不好的,欢迎评论指出,谢谢!

相关推荐
代码萌新知1 小时前
设计模式学习(五)装饰者模式、桥接模式、外观模式
java·学习·设计模式·桥接模式·装饰器模式·外观模式
iナナ3 小时前
Spring Web MVC入门
java·前端·网络·后端·spring·mvc
驱动探索者3 小时前
find 命令使用介绍
java·linux·运维·服务器·前端·学习·microsoft
卷Java3 小时前
违规通知功能修改说明
java·数据库·微信小程序·uni-app
CoderYanger4 小时前
优选算法-双指针:2.复写零
java·后端·算法·leetcode·职场和发展
小雨凉如水4 小时前
k8s学习-pod的生命周期
java·学习·kubernetes
半路_出家ren4 小时前
IPTables防火墙
服务器·网络·iptables
李宥小哥4 小时前
C#基础10-结构体和枚举
java·开发语言·c#
领创工作室4 小时前
安卓设备分区作用详解-测试机红米K40
android·java·linux
朝新_5 小时前
【EE初阶 - 网络原理】网络通信
java·开发语言·网络·php·javaee