【Java EE】-网络编程(二) Socket(套接字) + Udp版本客户端服务器 +Tcp版本客户端服务器

news2025/6/10 9:23:04

作者:学Java的冬瓜
博客主页:☀冬瓜的主页🌙
专栏:【JavaEE】
主要内容:传输层协议对应Socket编程,DatagramSocket,DatagramPacket,Udp版本的客户端和服务器,UdpEchoSever,UdpEchoClient,Udp版本的查词典服务器底层原理;Tcp版本的客户端和服务器,TcpEchoServer,TcpEchoClient。Tcp版本的服务器的几个要点。

文章目录

  • 一、UDP和TCP
  • 二、Udp版本客户端服务器
    • 1、DatagramSocket和DatagramPacket(数据报)
    • 2、UdpEchoSever&&UdpEchoClient
      • 2.1、什么是Echo Sever?
      • 2.2、UDP客户端+UDP回显服务器代码
      • 2.3、查词典服务器代码
  • 三、Tcp版本客户端服务器
    • 1、ServerSocket和Socket
    • 2、TcpEchoServer&&TcpEchoClient
      • 2.1、Tcp客户端
      • 2.2、Tcp服务器
  • 三、UDP和TCP总结

一、UDP和TCP

       Socket API 是操作系统给应用程序提供的来进行网络数据的 发送和接收的api(即传输层给应用层使用的api)。
在需要通过操作系统来执行的传输层里,提供了两个最核心的协议:UDP和TCP。因此Socket API也提供了两种风格:UDP、TCP。下面我们来看看UDP和TCP两种方式有什么区别。

TCP:有连接,可靠传输,面向字节流,全双工。
UDP:无连接,不可靠传输,面向数据报,全双工。

直接记上面的功能可能有点懵,那我们来举例子来类比理解:
比如关于连接:打电话时,双方是先拨通后,才进行你一句我一句的通话,是有连接的;而如果是发消息,因为不需要拨通等双方都先接受,是无连接的。
比如关于是否可靠传输:打电话时,可以互相回应,可以知道我的消息对方收到没有,这是可靠传输。而如果是发消息,则我无法确定对方是否收到我的消息,则是不可靠传输。
面向的对象 :TCP是面向字节流,即操作单位是字节;而UDP是面向数据报进行编程,即操作单位是数据报(一个数据报带有一定的格式,可能有多个字节)。
全双工:比如一根水管,它只能实现单向输水,可以叫做半双工;而这里的全双工指的是一个通信管道,可以双向传输(既可以发送也可以接收),怎么实现的? 一根网线里,其实有8根线,4根负责传输,4根负责接收,这样就完美实现全双工。

二、Udp版本客户端服务器

1、DatagramSocket和DatagramPacket(数据报)

基于 UDP 来编写一个客户端服务器的网络通信程序:
DatagramSocket:使用这个类表示一个Socket对象,在操作系统中,把这个Socket对象也当成是一个文件来处理,是在文件描述符表上的一项。
区别是:普通文件对应的硬件是 硬盘;而Socket对象对应的硬件是网卡,或者说操作系统内核中,用"Socket"这样的文件对象来抽象表示网卡。
DatagramPacket:表示UDP传输中的一个数据报。

DatagramSocket类的相关方法:
构造方法:

构造方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口一般用于客户端)
DatagramSocket(intport)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)
  • 进程关联了端口号,本质上是进程里的Socket对象关联了 端口号。同时一个进程可以创建多个Socket对象,每个Socket对象都可以连接到不同的网络地址和端口。因此一个进程可以关联多个端口,但一个端口只能关联一个进程。

普通方法:

普通方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞待)
void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字
  • receive方法中的DatagramPacket是我们创建的传入的一个空的对象,当receive接收到发送方发来的数据报时,才把发送方发来的内容填充进入我们传入的这个空的DatagramPacket对象,得到接收到的数据报,这个参数也叫做"输出型参数"。

DatagramPacket类(数据报)的相关方法:
构造方法:

构造方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
DatagramPacket(byte[] buf, int offset, int length,SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号

普通方法:

普通方法说明
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
byte[] getData()获取数据报中的数据(字节数组的形式)
int getLength()获取数据报中数据的长度
SocketAddress getSocketAddress()获取数据报发送端主机的IP和端口号(DatagramPacket中隐含发送方的IP和端口)

2、UdpEchoSever&&UdpEchoClient

2.1、什么是Echo Sever?

Echo Sever是一种基于客户端/服务器模型的网络应用程序,它的功能是将客户端发送的数据原封不断地返回给客户端,称为回显服务器。
这里我们主要来理解怎么实现客户端服务器,以及Socket API的使用,所以就省略了中间的业务逻辑,而是直接把客户端发来的数据直接返回。

2.2、UDP客户端+UDP回显服务器代码

客户端

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    // 客户端的ip是环回ip(127.0.0.1),端口是操作系统随机分配的一个端口
    // 因为在本机模拟通信,所以服务器的ip也是环回ip(127.0.0.1),端口是程序员指定的
    // 服务器的ip和端口都得告诉客户端,我们才能在客户端访问服务器
    private String serverIp = null;
    private int serverPort = 0;

    public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
        this.socket = new DatagramSocket();
        this.serverIp = serverIp;
        this.serverPort = serverPort;
    }
    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        while (true){
            // 1.从控制台读取数据到一个空的DatagramPacket中
            System.out.print("> ");
            String request = scanner.next();
            if(request.equals("exit")){
                System.out.println("客户端关闭!");
                break;
            }
            
            // 注意1:InetAddress.getByName(serverIp)操作把点分十进制的ip(127.0.0.1)转换成32位二进制数
            // 注意2:发送数据报时,使用String的getBytes().length方法获取数据报长度
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length
                                           ,InetAddress.getByName(serverIp), this.serverPort);
            // 2.把DatagramPacket发给服务器
            socket.send(requestPacket);
            
            // 3.使用空的DatagramPacket,接收服务器处理后的响应数据
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);  // 注意:如果receive没有接收到响应数据,那就会阻塞等待。

            // 4.打印响应结果
            // 注意1:打印返回的响应结果,不能用toString,因为你无法为DatagramPacket类重写toString方法
            // 注意2:接收数据报时使用DatagramPacket的getLength方法获取数据报长度
            String response = new String(responsePacket.getData(),0, responsePacket.getLength());
            System.out.println(response);
        }
    }
    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);
        client.start();
    }
}

客户端代码步骤:

  1. 用一个空的DatagramPacket类型的 requestPacket接收用户从控制台输入的数据(接收字符串)
  2. 根据给出的服务器的ip和端口,发送这个DatagramPacket类型的 requestPacket给服务器处理数据
  3. 用DatagramPacket类型的空的 responsePacket接收服务器发来的处理后的数据(接收数据报)
  4. 把数据报类型的响应内容 requestPacket转换成字符串 request,便于打印

服务器

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

public class UdpEchoServer {
    // 注意:1.这个socket对象在操作系统内核中操作时,是当成文件的方式操作,把这个对象当成网卡的抽象
    private DatagramSocket socket = null;
    // 注意:2.服务器端需要手动指定一个端口,避免客户端找不到服务器
    public UdpEchoServer(int port) throws SocketException {
        this.socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true){
            // 1.给一个空的DatagramPacket,用于接收客户端发来的数据报
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);   // 注意:如果receive没有接收到请求数据,那就会阻塞等待。
            
            // 注意1:为了便于处理,把DatagramPacket这个特殊的对象转化成字符串的形式,但是不能用toString,因为你无法为DatagramPacket类重写toString方法
            // 注意2:接收数据报时使用DatagramPacket的getLength方法获取数据报长度
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            // 2.对请求内容进行业务处理(这里是回显服务器直接返回)
            String response = process(request);
            
            // 3.构造好响应的DatagramPacket,并把它发回客户端。
            // (注意1:这里也可以直接使用requestPacket.getSocketAddress()同时获取IP和端口,客户端的端口和ip是requestPacket自带的。
            //  注意2:第二个参数必须是字节数组长度response.getBytes().length,而不是字符串的长度
            //        使用String的getBytes().length方法获取数据报长度)
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length
                    ,requestPacket.getAddress(),requestPacket.getPort());
            socket.send(responsePacket);

            // 4.为了观察,打印一下客户端发来的的信息
            System.out.printf("[%s,%d] req:%s; resp:%s\n",requestPacket.getAddress(),requestPacket.getPort()
                                                          ,request, response);
        }
    }

    private String process(String request) {
        return request;
    }
    public static void main(String[] args) throws IOException {
        UdpEchoServer sever = new UdpEchoServer(9090);
        sever.start();
    }
}

服务器代码步骤:

  1. 用一个空的DatagramPacket类型的 requestPacket接收 客户端发来的数据(接收数据报)
  2. 把这个requestPacket转换成字符串 request,然后进行业务处理得到字符串 response
  3. 用一个空的DatagramPacket类型的 requestPacket接收响应字符串 response(接收响应字符串)
    然后根据requestPacket自带的客户端的ip和端口,把响应发给客户端。
  4. 打印中间过程,客户端的ip和端口,服务器的处理请求和响应。

执行顺序:

1.服务器先启动,进行到receive进行阻塞,等待客户端发送请求数据报(服务器)
2.客户端读取用户输入内容到请求数据报(客户端)
3.客户端执行send把请求数据报发给服务器(客户端)
4.客户端发送请求数据报后立即执行到receive,等待服务器发来响应数据报(客户端)
  服务器接收到请求数据报,从服务器的receive阻塞中返回(服务器)
5.服务器根据请求数据报计算响应数据报(服务器)
6.服务器执行send,发送响应数据报给客户端(服务器)
7.客户端从receive阻塞中返回,读到响应数据报(客户端)

2.3、查词典服务器代码

  • 注意:需要复用上面 UDP客户端+服务器 中的代码。
    操作是:使用UdpDictSever继承UdpEchoSever,再重写process方法。
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

public class UdpDictSever extends UdpEchoServer{
    // 使用一个集合来存放单词集合
    private Map<String,String> dict = new HashMap<>();

    public UdpDictSever(int port) throws SocketException {
        super(port);
        dict.put("cat","猫");
        dict.put("beautiful","美丽的");
        dict.put("perfect","完美的");
    }

    @Override
    public String process(String request){
        return dict.getOrDefault(request,"没有你要查的单词!");
    }

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

三、Tcp版本客户端服务器

1、ServerSocket和Socket

ServerSocket:专门给服务器使用的Socket对象。
Socket:既可以给客户端使用,也可以给服务器使用的Socket对象。

注意1:
ServerSocket 用于服务器端本身的ServerSocket对象的创建;
Socket 用于客户端本身的Socket对象的创建(指定服务器的ip和端口) 和 服务器端accept与客户端连接后返回的Socket对象

注意2:
服务器端accept后,返回得到一个Socket对象,通过这个Socket对象和客户端 使用字节流进行 发送/接收。

ServerSocket类的相关方法:
构造方法:

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

普通方法:

普通方法说明
ServerSocket(int port)创建一个服务端流套接字ServletSocket类的对象,并绑定到指定端口
Socket accept()开始监听服务器端的绑定的端口,有客户端连接后,返回给服务器端一个Socket对象,并基于该Socket对象建立与客户端的连接,如果没有客户端连接则accept阻塞等待
void close()关闭此套接字(Socket)

Socket类的相关方法:
构造方法:

构造方法说明
Socket(String host, int port)创建一个客户端流的Socket类的对象,和对应IP的主机上的对应端口建立连接

普通方法:

普通方法说明
InetAddress getInetAddress()返回调用该方法的Socket对象的对应连接的Ip
int getPort()返回调用该方法的Socket对象的对应连接的端口
InputStream getInputStream()返回调用该方法的Socket对象的输入流
OutputStream getOutputStream()返回调用该方法的Socket对象的输出流

2、TcpEchoServer&&TcpEchoClient

2.1、Tcp客户端

Tcp版本的客户端和Udp版本的客户端的区别:

Udp版本的客户端(端口和DatagramSocket建立关联) 使用两个成员变量来表示指定服务器的serverIp和serverPort,且发送数据时需要把点分十进制的目标服务器的ip(serverIp)转换成32位的二进制数据,使用数据报进行发送/接收。
Tcp版本的客户端(端口和Socket建立关联) 要先根据指定服务器的serverIp和serverPort建立连接客户端的new Socket时传入serverIp和serverPort,它可以自动识别点分十进制为32位二进制数,然后使用字节流读写网卡(即接收/发送信息),其实是在协议栈里处理,然后交由网卡发送和接收。

客户端:

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 {
    private Socket socket = null;
    public TcpEchoClient(String serverIp, int serverPort) throws IOException {
        // 注意1:在客户端new一个Socket对象的时候,就连接服务器。
        // 注意2:Socket对象可以字节把点分十进制的serverIp转换成32位二进制数
        socket = new Socket(serverIp,serverPort);
    }

    public void start(){
        System.out.println("客户端启动!");
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()){
            Scanner scanner = new Scanner(System.in);
            while (true){
                // 1.客户端从控制台读取用户输入的内容
                System.out.print(">");
                String request = scanner.next();
                if (request.equals("exit")){
                    System.out.println("客户端关闭!");
                    break;
                }

                // 2.客户端把请求写入网卡,发送给服务器处理
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);   //注意:要写入"\n"
                printWriter.flush();  // 冲刷,保证数据写入网卡

                // 3.客户端读取服务器响应写回到网卡上的数据
                Scanner respScan = new Scanner(inputStream);
                String response = respScan.next();

                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
        client.start();
    }
}
客户端代码步骤:
1. 客户端从控制台读取用户输入的内容
2. 客户端把请求写入网卡,发送给服务器处理
3. 客户端读取服务器响应写回到网卡上的数据
4. 打印响应的结果

2.2、Tcp服务器

Tcp版本的服务器和Udp版本的服务器的区别:

Udp版本服务器不需要建立连接,使用数据报传输,如果客户端访问量不是很多,可以不用多线程,可以多个客户端同时访问,直接while就搞定。
Tcp版本需要建立连接,使用字节流传输,使用多线程(或者线程池),如果不用多线程,那么因为每个客户端访问都需要连接,在有客户端连接时,其它客户端则无法连接,导致无法使用,导致效率问题。

Tcp版本的服务器需要注意的点:

  • 1> Tcp版本的服务器需要在发送消息时在数据后面加上\n。因为接收端读取数据时使用Scanner的next方法读取,next方法规则是:读到换行符/空格/tab时结束,读到的数据不包含以上符号。所以发送端可以在数据的结尾加上\n,表示读取数据结束。这个点客户端也是一样。如下图:printWriter.println(outputStream)表示在发送数据outputStream后面加上一个\n。发送outputStream后,一定记得flash,把信息真正的发送。
    在这里插入图片描述
    在这里插入图片描述

  • 2> 在Tcp版本的服务器端中,需要关闭客户端访问时创建的Socket资源。每次有一个客户端访问服务器,就会创建一个Socket对象和客户端的Socket连接。服务器端每创建一个Socket对象,就在服务器的这个进程上的文件描述符表上占用一个空间,而客户端访问量应该是很多的。因此如果连接完成后,不关闭这个Socket,到了文件描述符表位置被占满时,其它客户端就无法再访问服务器了,因此,在每个客户端连接完成后,我们需要关闭服务器端的这个Socket资源,释放这个Socket占用的文件描述符表的位置。
    那么为什么Udp版本的服务器不需要关闭?Udp版本服务器端的的DatagramSocket的生命周期是整个进程。而Tcp版本的clientSocket的生命周期是每个客户端连接时,断开连接,这个Socket就没用了,且因为每创建一个客户端连接,服务器就会创建一个clientSocket,所以数量上也会很多!

  • 3> 短连接和长连接:下列代码的processConnection中的while去掉就是短连接,即传输一次就断开连接,每次访问都得先连接再发送请求;长连接即用while,当一个客户端连接好服务器然后发送请求后,先不断开连接,等待用户再次发送请求,等用户自己退出时才断开连接。
    在这里插入图片描述

  • 4> IO多路复用,如果客户端访问量很大,即使使用多线程服务器压力还是很大,就需要用IO多路复用。比如C10K问题(1w个客户端),C10M问题(1kw个客户端访问)。IO多路复用,可以使用一个线程处理多个客户端的任务。原理:在这个线程中使用一个集合来存放连接对象,这个线程就负责监听这个集合,在集合中哪个连接有数据来了,线程就处理这个连接。在操作系统中提供了select,epoll就可以监听。

服务器:

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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoSever {
    private ServerSocket serverSocket = null;
    // 注意:服务器本身使用ServerSocket和端口绑定连接
    public TcpEchoSever(int Port) throws IOException {
        serverSocket = new ServerSocket(Port);
    }


    public void start() throws IOException {
        System.out.println("启动服务器!");
        // 注意:使用while保证每次有客户端连接时都能连接到
        while (true){
             版本一:使用多线程
//            // 注意:每当有一个客户端连接服务器时,创建一个Socket对象和客户端的Socket进行通信
//            Socket clientSocket = serverSocket.accept();
//            // 注意:建立连接使用当前线程,放在我们创建的线程外;使用多线程去处理客户端发来的请求(处理业务)
//            Thread t = new Thread(()->{
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            t.start();

            // 版本二:使用线程池
            Socket clientSocket = serverSocket.accept();
            ExecutorService pool = Executors.newCachedThreadPool();
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    // 注意:一个连接对应一个客户端,
    private void processConnection(Socket clientSocket) throws IOException {
        // 注意:服务器的每一个Socket对应一个客户端
        System.out.printf("[%s:%d] 客户端上线!\n",
                clientSocket.getInetAddress().toString(),
                clientSocket.getPort());

        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()){
            // 注意:由于一个客户端可能要处理多个请求和响应,所以使用循环进行
            while (true){
                // 1.服务器读取客户端写入网卡的字节流数据
                Scanner reqScan = new Scanner(inputStream);
                if (!reqScan.hasNext()){
                    System.out.printf("[%s:%d] 客户端下线!\n",
                            clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                String request = reqScan.next();
                // 注意:next读到换行符/空格/tab结束,但是读取的内容不包含换行符/空格等
                //    我们这里是从客户端的请求内容就读取,所以客户端发来的请求中应当有以上结束符

                // 2.对请求进行业务处理
                String response = process(request);

                // 3.服务器把响应内容写回网卡,响应给客户端
                //   操作:用outputStream构造一个PrintWriter字符流对象,便于把"\n"一并写入网卡
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();   // 冲刷,保证数据写入网卡

                // 4.打印日志
                System.out.printf("[%s:%d] req:%s; resp:%s\n",
                        clientSocket.getInetAddress(),clientSocket.getPort(),request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            clientSocket.close();
        }
    }

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

    public static void main(String[] args) throws IOException {
        TcpEchoSever sever = new TcpEchoSever(9090);
        sever.start();
    }
}
服务器代码步骤:
1. 服务器读取客户端写入网卡的字节流数据
2. 对请求进行业务处理
3. 服务器把响应内容写回网卡,响应给客户端

执行顺序:

1.服务器先启动,进行到accept进行阻塞,等待客户端new Socket从而建立连接(服务器)
2.客户端从控制台读取用户输入内容(客户端)
3.客户端使用OutputStream把请求发给服务器(客户端)
4.服务器Socket感知到请求并使用InputStream接收请求(服务器)
5.服务器根据请求计算响应(服务器)
6.服务器使用OutputStream把响应发回客户端(服务器)
7.客户端Socket感知到请求并使用InputStream接收请求(客户端)
8.客户端打印响应结果

三、UDP和TCP总结

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

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

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

相关文章

大力出奇迹——GPT系列论文学习(GPT,GPT2,GPT3,InstructGPT)

目录说在前面1.GPT1.1 引言1.2 训练范式1.2.1 无监督预训练1.2.2 有监督微调1.3 实验2. GPT22.1 引言2.2 模型结构2.3 训练范式2.4 实验3.GPT33.1引言3.2 模型结构3.3 训练范式3.4 实验3.4.1数据集3.5 局限性4. InstructGPT4.1 引言4.2 方法4.2.1 数据收集4.2.2 各部分模型4.3 …

【轻NAS】Windows搭建可道云私有云盘,并内网穿透公网访问

文章目录1.前言2. Kodcloud网站搭建2.1. Kodcloud下载和安装2.2 Kodcloud网页测试3. cpolar内网穿透的安装和注册4. 本地网页发布4.1 Cpolar云端设置4.2 Cpolar本地设置5. 公网访问测试6.结语1.前言 云存储作为近些年兴起的概念&#xff0c;成功吸引了各大互联网厂商下场&…

thingsboard ARM网关

G5501边缘计算网关 G5501是采用中高端的通用型 SOC&#xff0c;一款4 核 arm 架构 A55 处理器的 网关设备。标配处理器为 Cortex-A55 四核&#xff0c;最高主频 2GHz 的处理器&#xff0c; 内置 4GB DDR4 内存&#xff0c;32GB eMMC 存储。 集成Mali G52 2EE 图形处理器GPU&am…

matplotlib设置中文字体为微软雅黑

matplotlib无法设置任何中文字体怎么办&#xff1f; 如何在linux系统下让matplotlib显示中文&#xff1f; 下载微软雅黑字体&#xff0c;把它放在某个目录下。 链接&#xff1a; https://pan.baidu.com/s/1SCLYpH_MzY7vn0HA0wxxAw?pwdft2j 提取码&#xff1a;ft2j 在代码中加…

Learning C++ No.18【STL No.8】

引言&#xff1a; 北京时间&#xff1a;2023/3/18/21:47&#xff0c;周末&#xff0c;不摆烂&#xff0c;但是欠钱终于还是遭报应了&#xff0c;导致坐牢7小时&#xff08;上午3.5&#xff0c;下午3.5&#xff09;&#xff0c;难受&#xff0c;充分意识到行哥是那么的和蔼可亲…

DLRover: 云上自动扩缩容 DeepRec 分布式训练作业

背景 如今&#xff0c;深度学习已广泛应用在搜索、广告、推荐等业务中&#xff0c;这类业务场景普遍有两个特点&#xff1a; 1&#xff09;训练样本量大&#xff0c;需要分布式训练提升训练速度&#xff1b; 2&#xff09;模型稀疏&#xff0c;即模型结构中离散特征计算逻辑占…

强训之【走方格的方案数和另类加法】

目录1.走方格的方案数1.1题目1.2思路讲解1.3代码展示2.另类加法2.1题目2.2思路讲解2.3代码展示3.选择题1.走方格的方案数 1.1题目 链接: link 描述 请计算n*m的棋盘格子&#xff08;n为横向的格子数&#xff0c;m为竖向的格子数&#xff09;从棋盘左上角出发沿着边缘线从左上…

第⑦讲:Ceph集群RGW对象存储核心概念及部署使用

文章目录1.RadosGW对象存储核心概念1.1.什么是RadosGW对象存储1.2.RGW对象存储架构1.3.RGW对象存储的特点1.4.对象存储中Bucket的特性1.4.不同接口类型的对象存储访问对比2.在集群中部署RadosGW对象存储组件2.1.部署RGW组件2.2.集群中部署完RGW组件后观察集群的信息状态2.3.修改…

【2023】Kubernetes之Pod与容器状态关系

目录简单创建一个podPod运行阶段&#xff1a;容器运行阶段简单创建一个pod apiVersion: v1 kind: pod metadata: name: nginx-pod spec:containers:- name: nginximages: nginx:1.20以上代码表示创建一个名为nginx-pod的pod资源对象。 Pod运行阶段&#xff1a; Pod创建后&am…

搜索引擎测试报告

文章目录一、项目背景二、项目功能三、测试目的四、测试环境五、测试计划1、功能测试2、自动化测试六、测试结果一、项目背景 java官方文档是我们在学习java语言中不可或缺的权威资料。相比于各种网站的Java资料&#xff0c;官方文档无论是语言表达还是组织方式都要更加全面和…

ThingsBoard开源物联网平台智慧农业实例快速部署教程(Ubuntu、CentOS适用)

ThingsBoard部署教程文档 文章目录ThingsBoard部署教程文档1. JDK环境安装2. 安装thingsBoard2.1 ThingsBoard软件包安装2.2 PostgreSQL安装2.3 PostgreSQL初始化配置3. 修改ThingsBord的配置4. 运行安装脚本测试5. 访问测试6. 导入一个仪表盘库6.1 导出仪表盘并导入自己的项目…

Spring —— Spring Boot 配置文件

JavaEE传送门JavaEE Spring —— Bean 作用域和生命周期 Spring —— Spring Boot 创建和使用 目录Spring Boot 配置文件Spring Boot 配置文件格式properties配置文件properties 基本语法properties 缺点yml 配置文件yml 基本语法yml 配置不同类型数据及 nullyml 配置对象yml…

【SQL Server】数据库开发指南(一)数据库设计

文章目录一、数据库设计的必要性二、什么是数据库设计三、数据库设计的重要性五、数据模型5.1 实体-关系&#xff08;E-R&#xff09;数据模型5.2 实体&#xff08;Entity&#xff09;5.3 属性&#xff08;Attribute&#xff09;5.5 关系&#xff08;Relationship&#xff09;六…

windows搭建ftp及原理(小白向)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录环境一、实验步骤1.1安装ftp二、ftp实验引发的思考1.简单阐述ftp的原理2.ftp建立的流程总结环境 windwos任意环境不需要server windows10 提示&#xff1a;以下是本…

〖Python网络爬虫实战⑤〗- Session和Cookie介绍

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;目前专栏免费订阅&#xff0c;在转为付费专栏前订阅本专栏的&#xff0c;可以免费订阅付费…

Linux的诞生过程

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。座右铭&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石&#xff0c;故能成其高。个人主页&#xff1a;小李会科技的…

走近阿里Apsara Clouder云计算的蓝图

文章目录一&#xff1a;"什么是Apsara Clouder 云计算"二&#xff1a;"Apsara Clouder 云计算"厉害在哪里&#xff1f;三&#xff1a;"Apsara Clouder 云计算"认证一&#xff1a;“什么是Apsara Clouder 云计算” &#x1f496;&#x1f496; A…

【云原生Docker】09-Docker网络详解

【云原生|Docker】09-Docker网络详解 文章目录【云原生|Docker】09-Docker网络详解前言网络详解bridge网络基于bridge网络的容器访问外部网络外部网络访问基于bridge网络的容器host网络none网络container网络自定义网络自定义bridge网络容器的互通两个相同的bridge网络容器互通…

多层多输入的CNN-LSTM时间序列回归预测(卷积神经网络-长短期记忆网络)——附代码

目录 摘要&#xff1a; 卷积神经网络(CNN)的介绍&#xff1a; 长短期记忆网络&#xff08;LSTM&#xff09;的介绍&#xff1a; CNN-LSTM&#xff1a; Matlab代码运行结果&#xff1a; 本文Matlab代码数据分享&#xff1a; 摘要&#xff1a; 本文使用CNN-LSTM混合神经网…

spring快速连接mybatis

spring快速连接mybatisspring整合mybatis1.maven依赖配置2.数据库sql设计3.数据库连接配置4.实体类设计5.Dao层开发6.SqlMapConfig.xml7.运行程序进行crudspring整合mybatis 1.maven依赖配置 配置pom.xml如下 <?xml version"1.0" encoding"UTF-8"?…