【JavaEE初阶】网络编程

news2024/11/27 2:29:07

在这里插入图片描述

在这里插入图片描述

文章目录

  • 网络编程基础
    • 网络资源
    • 网络编程
    • 网络编程中的基本概念
      • 发送端和接收端
      • 请求和响应
      • 客户端和服务端
        • 常见的客户端服务端模型
  • Socket套接字
    • 了解UDP和TCP
    • UDP数据报套接字编程
      • DatagramSocket API
      • DatagramPacket API
      • InetSocketAddress API
      • UDP版本的客户端服务器程序
    • TCP流套接字编程
      • ServerSocket API
      • Socket API
      • TCP版本的客户端服务器程序
      • TCP中的长短连接

网络编程基础

网络资源

网络资源,其实就是在网络中可以获取的各种数据资源。
而所有的网络资源,都是通过网络编程来进行数据传输的。
在这里插入图片描述

网络编程

网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。
我们只要满足进程不同就行;所以即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程。
特殊的,对于开发来说,在条件有限的情况下,一般也都是在一个主机中运行多个进程来完成网络编程。
但是,我们一定要明确,我们的目的是提供网络上不同主机,基于网络来传输数据资源:

  • 进程A:编程来获取网络资源
  • 进程B:编程来提供网络资源

网络编程中的基本概念

发送端和接收端

在一次网络数据传输时:
发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机
接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机
收发端:发送端和接收端两端,也简称为收发端。
注意:发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。

请求和响应

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

  • 第一次:请求数据的发送
  • 第二次:响应数据的发送。

客户端和服务端

服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务。
客户端:获取服务的一方进程,称为客户端。

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

  • 客户端获取服务资源
  • 客户端保存资源在服务端

常见的客户端服务端模型

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

  1. 客户端先发送请求到服务端
  2. 服务端根据请求数据,执行相应的业务处理
  3. 服务端返回响应:发送业务处理结果
  4. 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)

Socket套接字

网络编程中的核心就是Socket(套接字) API,这是操作系统给应用程序提供的网络编程API。
socket api是和传输层密切相关的。
传输层里提供了两个最核心的协议。UDP和TCP。
在这里插入图片描述
因此,socket api也提供了两种风格UDP TCP

了解UDP和TCP

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

有无连接:

打电话就是有连接的没需要连接建立了才能通信,连接建立需要对方来“接受”。如果连接没建立好,就无法通信。
发短信/发微信就是无连接的,直接发送即可。

可靠不可靠传输:

网络环境是复杂的,不可能保证传输的数据百分百就能到达。
可靠传输:发送发能知道自己的消息是不是发过去了,还是丢了。比如抖音的已读功能。打电话功能。
不可靠传输:发短信/发微信。(QQ或者微信等没有已读功能的)

面向数据报/字节流:

面向字节流:数据传输就和文件读写类似“流式”的。
面向数据报:数据则以一个个的“数据报”为基本单位(一个数据报可能是若干字节)

全/半双工:

全双工:一个通信通道,可以双向传输。(既可以发送也可以接收)
半双工:比如一跟水管,是单向传输的,是半双工。

UDP数据报套接字编程

DatagramSocket API

DatagramSocket使用这个类,表示一个Socket对象,把这个socket对象也是当成一个文件来处理的。相当于是文件描述表上的一项。

普通的文件对应的硬件设备是硬盘。
socket文件,对应的硬件设备是网卡。

DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。
DatagramSocket 构造方法:

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

注意:DatagramSocket()中没有指定端口时,系统则会自动分配一个空闲的端口。
DatagramSocket(intport)这个版本是要传入一个端口号,此时就是让当前的socket对象和这个指定的端口之间关联起来.

DatagramSocket 方法:

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

注意:receive(DatagramPacket p)此处传入的相当于是一个空的对象。recive方法内部,会对参数的这个空对象进行内容填充。从而构造出数据结果。参数也是一个“输出型参数”。
close()使用完后释放资源。

DatagramPacket API

DatagramPacket 表示UDP中传输的一个报文,构造这个对象,可以指定一些具体的数据进去。

DatagramPacket 构造方法:

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

注意:DatagramPacket(byte[] buf, int length)是把buf缓冲区设置进去了
DatagramPacket(byte[] buf, int offset, int length(构造缓冲区+地址), SocketAddress address(使用此类表示IP+port))

DatagramPacket 方法:

方法签名方法说明
InetAddress()/getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
byte[] getData()获取数据报中的数据

InetSocketAddress API

InetSocketAddressSocketAddress 的子类 )构造方法:

方法签名方法说明
InetSocketAddress(InetAddress addr, int port)创建一个Socket地址,包含IP地址和端口号

UDP版本的客户端服务器程序

此处的服务器是 回显服务器
一个普通的服务器:收到请求,根据请求计算响应,返回响应
回显服务器echo server:省略了其中还的“根据请求计算响应”,请求是什么,就返回什么。
作为一个真正的服务器,最重要的环节就是“根据请求计算响应”。

接下来我们实现一个UDP 版本的回显服务器

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

//UDP版本的回显服务器
public class UdpEchoServe {
    //网络编程,本质上是操作网卡
    //但是网卡不方便直接操作,在操作系统内核中,使用了一种特殊的叫做“socket”这样的文件来抽象表示网卡。
    //因此进行网络传输中,必须首先要有一个socket对象
    private DatagramSocket socket = null;

    //对于服务器来说,创建socket对象的同时,要让他绑定上一个端口号
    //服务器一定要关联上一个具体的端口号!
    //服务器是网络传输中,被动的一方,如果是操作系统随机分配端口,此时客户端就不知道这个端口时什么了,也就无法进行通信了
    public UdpEchoServe(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        //服务器不是只给一个客户端提供服务,而是需要给多个客户端提供服务,所以需要写一个循环
        while(true){
            //只要有客户端过来,就可以提供服务
            //1.读取客户发过来的请求是什么
            // receive 方法的参数是一个输出型参数,需要先构造好一个空白的DatagramPacket对象,交给receive来进行填充
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            //(receive内部会针对参数对象填充数据,填充的数据来自于网卡)
            socket.receive(requestPacket);

            //此时这个DatagramPacket是一个特殊的对象,并不方便直接进行处理,可以把这里包含的数据拿出来,构造成一个字符串。
            String request = new String(requestPacket.getData(),0, requestPacket.getLength());

            //2.根据请求计算响应
            String response = process(request);

            //3.把响应写回到客户端,send的参数也是DatagramPacket,需要把这个Packet对象构造好
            //此处构造的响应对象,不能是用空的字节数组构造了,而是要使用响应数据来构造
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);

            //4.打印当前请求响应的处理中间结果
            System.out.printf("[%s:%d] req: %s;resp %s\n",requestPacket.getAddress().toString(),
                    requestPacket.getPort(),request,response);
        }
    }

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

    public static void main(String[] args) throws IOException {
        //端口号的指定,从1024-》65535之间选择一个数字
        UdpEchoServe serve = new UdpEchoServe(1988);
        serve.start();
    }
}

在这里插入图片描述
因此,构造字符串,那些用了就构造那些部分。就通过getLength获取到实际的数据报的长度,只是把这个实际的有效部分给构造成字符串即可。
在这里插入图片描述
在这里插入图片描述

数据到达网卡,经过内核层层分用,最终到达UDP传输层协议。
调用receive相当于是执行到了内核中的相关的udp的代码,就会把这个udp数据报里面的载荷部分取出来,拷贝到用户提供的byte[]数组中。
在这里插入图片描述
接下来,编写客户端代码:

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

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String severIp = null;
    private int serverPort = 0;

    //一次通信,需要有两个ip,两个端口
    //客户端的ip是127.0.0.1已知的
    //客户端的port是系统自动分配的
    //服务器的ip和端口也需要告诉客户端,才能顺利把消息发给服务器
    public UdpEchoClient(String severIp, int serverPort) throws SocketException {
        socket = new DatagramSocket();
        this.severIp = severIp;
        this.serverPort = serverPort;
    }
    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        while(true){
            //1.从服务台读取要发送的数据
            System.out.println(">");
            String request = scanner.next();
            if(request.equals("exit")){
                System.out.println("Goodbye~");
                break;
            }
            //2.构造成UDP这个请求并发送
            //构造这个Packet的时候,需要把serveIp和port都传过来,但是此处IP地址需要的是一个32位的整数形式
            //上述的IP地址是一个字符串,需要使用InetAddress.getByName()来进行一个转换
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(severIp),serverPort);
            socket.send(requestPacket);
            //3.读取服务器的UDP响应,并解析
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
           //4.把解析好的结果显示出来
            System.out.println(response);
        }
    }

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

在这里插入图片描述
在一次通信中,涉及到的ip和端口有两组:

  • 源ip
  • 源端口
  • 目的ip
  • 目的端口

假设是让客户端给服务器发个数据:
此时源ip就是客户端的ip地址
目的ip就是服务器的ip地址(上述代码中,由于客户端和服务器都在同一主机上,所以ip都是“127.0.0.1”(环回ip))
源端口就是客户端端口(即系统随机分配的空闲的端口)
目的端口就是服务器的端口(在上述程序中指定的是1988)

端口号用来表示/区分一个进程
因此不允许一个端口同时被多个进程使用(前提是在同一主机上。)一个端口通常情况下不能被多个进程使用,但是一个进程可以绑定多个端口。(准确来说,socket和端口号是一对一的,进程和socket是一对多的)

对于服务器来说,端口号必须是确定好的,对于客户端来说,端口可以使系统随机分配的。

客户端是否可以自己手动指定端口?
一般来说是不可以的,客户端如果显示指定端口,可能就和客户端电脑上的其他程序的端口冲突了,这一冲突就可能导致程序无法正确通信了。(比如客户端想要8888这个端口,结果,客户端另外一个程序已经使用8888,此时程序就无法正常正确运行:运行就会抛异常提示绑定端口失败。)
但是对于服务器这里的端口是程序员可控的,我们可以自己安排使用哪个端口。

在这里插入图片描述
DatagramSocket这个类的receive能阻塞,是因为操作系统原生提供的。
API(receive)就是阻塞的函数。

在这里插入图片描述
在这里插入图片描述
稍微调整后可以开启多个客户端。

在这里插入图片描述

TCP流套接字编程

TCP提供的API主要是两个类:
ServerSocket:专门给服务器使用的Socket对象
Socket:既是会给客户端使用,也会给服务器使用
注意:TCP不需要一个类来表示“TCP数据报”
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版本的客户端服务器程序

服务器端代码:

package nettwork;

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;

    public TcpEchoSever(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("启动服务器!");
        // 此处使用 CachedThreadPool, 使用 FixedThreadPool 不太合适 (线程数不太应该是有固定的....)
        ExecutorService threadPool = Executors.newCachedThreadPool();
        while (true) {
            // 使用这个 clientSocket 和具体的客户端进行交流.
            Socket clientSocket = serverSocket.accept();
            // 此处使用多线程来处理.
            // 这里的多线程版本的程序, 最大的问题就是可能会涉及到频繁申请释放线程.
//            Thread t = new Thread(() -> {
//                processConnection(clientSocket);
//            });
//            t.start();

            // 使用线程池.
            threadPool.submit(() -> {
                processConnection(clientSocket);
            });
        }
    }

    //使用这个方法来处理一个连接
    //一个连接对应到一个客户端,但是这里可能会涉及到多次交互
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d]客户端上线!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //基于上述socket对象和客户端进行通信
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            //处理多个请求和响应,可以使用循环来进行
            while(true){
                //1.读取请求
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()){
                    //没有下一个数据了,说明读完了(客户端关闭了连接)
                    System.out.printf("[%s:%d]客户端下线!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //注意:此处使用next是一直读取到换行符/空格/其他空白符结束,但是最终返回结果中不包含上述空白符
                String request = scanner.next();
                //2.根据请求构造响应
                String response = process(request);
                //3.返回响应结果
                //    OutputSrteam 没有  writr String 这样的功能,可以把String 里字节数组拿出来进行写入,
                //    也可以用字符流来转化一下
                PrintWriter printWriter = new PrintWriter(outputStream);
                //此处使用println 来写入,让结果中带有一个\n换行,方便对端来接受解析
                printWriter.println(response);
                //flush刷新缓冲区,保证当前写入的数据被发送出去了。
                printWriter.flush();

                System.out.printf("[%s:%d] req: %s resp: %s \n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),
                        request,response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

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

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

在这里插入图片描述
此代码中,用到了一个clientSocket,此时任意一个客户端连上来吗,都会返回/创建一个Socket对象(Socket就是文件),每次创建一个clientSocket对象,就会占用一个文件描述符表的位置
因此在使用完毕之后,就需要进行“释放”。
前面的socket都没有释放,是因为前面的socket生命周期长。但是此处的clientSocket数量多,每个客户端都有一个,生命周期也更短。

在这里插入图片描述
注意:此处使用println来进行发送数据,是因为println会在发送的数据后面自动带上\n换行。如果不使用println,就无法正常运行。因为TCP协议,是面向字节流的协议。(字节流的特性是:一次读多少字节都行。而结束是\n)此处的代码中,隐式约定了使用\n来作为当前代码的请求/响应分割约定。
在这里插入图片描述

客户端代码:

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 severIp,int serverPort) throws IOException {
        //Socket构造方法,能够识别点分十进制格式的IP地址,比DatagramPacket更加方便
        //new 这个对象的同时,就会进行TCP连接操作
        socket = new Socket(severIp,serverPort);
    }

    public void start(){
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()) {
            while(true){
                //1.先从键盘上获取用户输入的内容
                System.out.println(">");
                String request = scanner.next();
                if(request.equals("exit")){
                    System.out.println("Goodbye~");
                    break;
                }
                //2.把读到的内容构造成请求,发送给服务器
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                //此处加上flush确保数据被发送出去
                printWriter.flush();
                //3.读取服务器的响应
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();
                //4.把响应内容显示到界面上
                System.out.println(response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

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

运行结果:在这里插入图片描述
多线程模式下的运行结果:
在这里插入图片描述

TCP中的长短连接

TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:
短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。

  1. 对比以上长短连接,两者区别如下: 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时 的,长连接效率更高。
  2. 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送 请求,也可以是服务端主动发。
  3. 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于 客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

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

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

相关文章

try catch finally 里面有return的执行顺序

目录 实例结论 实例 1.try和catch中有return时,finally里面的语句会被执行吗 我们可以来分别看看 (1)执行try中的return时 public class Solution {public static int show() {try {return 1;}finally{System.out.println("finally模块被执行");}}publi…

第7章链接:如何动态连接共享库、从应用程序中加载和链接共享库

文章目录 7.10 动态链接共享库静态库的缺点何为共享库共享库的"共享"的含义动态链接过程 7.11 从应用程序中加载和链接共享库运行时动态加载和连接共享库的接口 dlopen函数 dlsym函数 dlclose函数 dlerror动态加载和链接共享库的应用程序示例 7.10 动态链接共享库 静…

强化学习路线规划之深度强化学习代码

虽然说很多代码都有问题,但是不管它们,我不是为了去debug,紧盯住自己的目标,目标是整理出一条通常的强化学习之路,让自己以及看到这些博客的大家在学习的时候能够少走一些弯路。所以从q-learning和Sarsa开始&#xff0…

buuctf9

目录 web [ZJCTF 2019]NiZhuanSiWei misc [BJDCTF2020]认真你就输了 刷新过的图片 crypto 篱笆墙的影子 RSA web [ZJCTF 2019]NiZhuanSiWei 1.启动环境 <?php $text $_GET["text"]; $file $_GET["file"]; $password $_GET["password…

UE4与MATLAB联合仿真环境配置中遇到的问题及解决办法

UE4与MATLAB联合仿真环境配置中遇到的问题及解决办法 目录 UE4与MATLAB联合仿真环境配置中遇到的问题及解决办法前言问题及解决办法1. The following modules are missing or built with a different engine version: MathWorksAerospace MathWorksSimulation MathWorksUAV Eng…

IMX6Ull内核移植详细过程讲解

文章目录 一、安装相应依赖包二、下载相应的内核版本库&#xff08;1&#xff09;讲解官网内核分支&#xff08;2&#xff09;下载内核版本库&#xff08;3&#xff09;内核目录文件讲解 三、开始内核移植过程&#xff08;1&#xff09;步骤一、修改默认架构和默认交叉编译器&a…

modbus协议与nodejs modbus-serial实现

nodejs可通过modbus-serial库来实现modbus协议 npm install modbus-srial 安装完后在examples目录下有例子说明如何使用&#xff0c;本文主要讲述作为客户端的使用方式。 polling_RTU是使用485串口来实现modbus通信&#xff0c; polling_TCP和 polling_UDP则使用TCP/UDP来实现…

HBuilderX使用

HBuilderX使用&#xff08;Vue前后端分离&#xff09; 概述&#xff1a;DCloud开发者后台 DAccount Service 1、官网下载开发工具&#xff1a;HBuilderX-高效极客技巧 注意&#xff1a;安装目录路径中不能出现中文特殊字符&#xff0c;否则会造成项目无法编译。比如C:/Progr…

learn_C_deep_10 深刻认识C语言重点操作符

目录 注释符号 其他注释方法 注释建议 接续符和转义符 在续行符\之前或者之后带上空格&#xff0c;行不行&#xff1f; \n&#xff1a;换行符与\r&#xff1a;回车符之间的区别 两个小程序(旋转光标、倒计时) 单引号和双引号 ​编辑abcd这样写有问题吗? 特殊情况 为…

ThinkPHP6表单上传的数据获取的四种方式【请求对象调用,静态调用,助手函数调用,原生的get|post】

ThinkPHP6表单上传的数据获取 首先一个form表单中的Input控件需要存在对应的name值&#xff0c;然后method为post|get,action为要提交到的控制器的哪个方法 例如(本文的前端视图代码均为下面的html)&#xff1a; <form method"post" action"/index.php/lo…

Hard Patches Mining for Masked Image Modeling

摘要 蒙面图像建模&#xff08;MIM&#xff09;因其在学习可伸缩视觉表示方面的潜力而引起了广泛的研究关注。在典型的方法中&#xff0c;模型通常侧重于预测掩码补丁的特定内容&#xff0c;并且它们的性能与预定义的掩码策略高度相关。直观地说&#xff0c;这个过程可以被看作…

应用架构总结

架构目标 高可用性 整体系统可用性最低99.9%&#xff0c;目标99.99%。全年故障时间整个系统不超过500分钟&#xff0c;单个系统故障不超过50分钟。 高可扩展性 系统架构简单清晰&#xff0c;应用系统间耦合低&#xff0c;容易水平扩展&#xff0c;业务功能增改方便快捷。 低成…

IOS APP universal links 申请流程

一、背景 项目框架&#xff1a;APP h5 需求&#xff1a;APP首页可以选择微信&#xff0c;进行授权登录。 问题&#xff1a;在APP跳转到微信进行授权的时候&#xff0c;提示“由于应用universal link校验不通过&#xff0c;无法完成微信登录”。 二、申请流程 1. 苹果后台管…

XML语言简介和语法介绍

XML语言简介 文章目录 XML语言简介用途各部分注解声明元素属性注释CDATA转义字符 可拓展标记语言&#xff1b;是SGML的子集&#xff0c;可以描述很复杂的数据关系 用途 用于组织和存储数据&#xff0c;除此之外都和XML本身无关 配置文件(例子&#xff1a;Tomcat的web.xml,se…

Android 消息机制

消息机制相关API Message(消息) 可理解为线程之间通讯的数据单元, 可以通过message携带需要的数据 创建对象: Message.obtain(what) 封装数据 public int what public int arg public Object obj Handler(处理器) Handler是Message的处理器, 同时也负责消息的发送和移除的…

Linux内核(十四)Input 子系统详解 IV —— 配对的input设备与input事件处理器 input_register_handle

文章目录 input_handle结构体详解配对的input设备与input事件处理器实例input核心层对驱动层和事件层之间的框架建立流程图 本文章中与input子系统相关的结构体可参考input子系统结构体解析 input函数路径&#xff1a;drivers/input/input.c input_handle结构体详解 input_ha…

二十六:交易详细信息

功能需求 用户在交易主页面&#xff0c;点击交易名称超级链接&#xff0c;跳转到交易明细页面&#xff0c;完成查看交易明细的功能。 *显示交易的基本信息 *显示交易的备注信息 *显示交易的历史信息 *显示交易的阶段图标信息 流程图 后端代码实现 1.tran TranMapper /*…

实现Fast sigmoid和Softmax

Sigmoid 函数介绍 Sigmoid 函数&#xff08;Logistic 函数&#xff09;是神经网络中非常常用的激活函数&#xff0c;它的数学表示如下: 由于 e x e^x ex幂运算是非常耗时的计算&#xff0c;因此尝试通过替换sigmoid中的 e x e^x ex运算&#xff0c;来提高运行效率&#xff0c;同…

Linux篇4

Shell常用命令 1. 日期时间类1.1 date&#xff1a;日期时间类1.1.1 查看当前日期时间1.1.2 查看非当前日期时间1.1.3 设置系统日期时间 1.2 cal&#xff1a;日历类 2. 用户管理命令2.0 id&#xff1a;查看用户是否存在2.1 useradd&#xff1a;添加新用户2.2 passwd&#xff1a;…

Linux相关问题

中英文切换 super空格切换中英文&#xff1b;super指键盘上的Win键&#xff1b; 开机自启动服务设置 可视化方式&#xff1a;输入setup命令进入自启动服务配置&#xff1b;通过上下键选中服务&#xff0c;通过空格选择是否自启动该服务&#xff1b; 开启不同的终端 CTRLALT…