Socket学习记录

news2024/9/21 22:29:49

本次学习Socket的编程开发,该技术在一些通讯软件,比如说微信,QQ等有广泛应用。

网络结构

这些都是计算机网络中的内容,我们在这里简单回顾一下:

在这里插入图片描述

UDP(User Datagram Protocol):用户数据报协议;TCP(Transmission ControlProtocol):传输控制协议。

在这里插入图片描述

TCP协议
特点:面向连接、可靠通信。
TCP的最终目的:要保证在不可靠的信道上实现可靠的传输。
TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。

在这里插入图片描述

四次握手是为了确保收发数据都已完成。

在这里插入图片描述

我们首先了解一下关于获取主机地址的相关方法:

		//获取本机IP地址对象的地址
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost.getHostName());
        System.out.println(localHost.getHostAddress());
        //获取指定IP域名的IP地址对象
        InetAddress ip = InetAddress.getByName("www.baidu.com");
        System.out.println(ip.getHostName());//输出ip主机名称
        System.out.println(ip.getHostAddress());//输出指定域名的ip地址
        //判断6秒内能否与百度联通,相当于ping
        System.out.println(ip.isReachable(6000));

在这里插入图片描述

UDP通信开发

随后我们进行客户端与服务端的数据发送与接收:
首先是客户端的定义:

		//创建客户端对象,这里可以选择使用无参构造,当然也可以指定端口进行有参构造,在不指定端口时系统会默认分配
        DatagramSocket socket = new DatagramSocket();
        //创建数据包封装对象,存储数据信息
//        public DatagramPacket(byte buf[], int offset, int length,
//        InetAddress address, int port) {
//            setData(buf, offset, length);
//            setAddress(address);
//            setPort(port);
//        }
        byte[] bytes = "客户端消息:我是鹏翔".getBytes();
        //客户端发送的数据包,需要指明接收的服务端的IP地址以及端口
        DatagramPacket packet =
                new DatagramPacket(bytes,bytes.length,InetAddress.getLocalHost(),8888);
        //发送数据包
        socket.send(packet);
        System.out.println("客户端数据发送完毕");
        socket.close();

服务端的开发设计

		System.out.println("服务端启动");
        //创建一个服务端对象,并指定端口
        DatagramSocket socket = new DatagramSocket(8888);
        //定义所能够接收的数据的大小
        byte[] buffer = new byte[1024*64];
        //服务器接受的数据包
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);

        //接收数据
        socket.receive(packet);
        int length = packet.getLength();
        String string = new String(packet.getData(), 0, length);//发送多少数据则接收多少数据
        System.out.println(string);
        socket.close();

至此,我们的客户端与服务端便开发完成了,在实验中,我们需要先启动服务端,再启动客户端。

在这里插入图片描述
至此,完成客户端与服务端的消息发送与接收。

UTP通信多发多收

但这只是完成了一次消息的发送与接收,而在实际情况中我们往往需要进行多发多收,那么该如何实现呢?
客户端设计:

public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        //创建一个服务端对象,并指定端口
        DatagramSocket socket = new DatagramSocket(8888);
        //定义所能够接收的数据的大小
        byte[] buffer = new byte[1024*64];
        //服务器接受的数据包
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
        while(true){
            socket.receive(packet);
            int length = packet.getLength();
            String string = new String(packet.getData(), 0, length);
            System.out.println(string);
            System.out.println("--------------------------");
        }
    }

客户端设计:

public static void main(String[] args) throws IOException {
        //创建客户端对象
        DatagramSocket socket = new DatagramSocket();
        Scanner scanner=new Scanner(System.in);
        while(true){
            System.out.println("请说:");
            String msg = scanner.nextLine();
            if ("exit".equals(msg)) {
                System.out.println("客户端数据发送完毕");
                socket.close();
                break;
            }
            byte[] bytes = msg.getBytes();
            //客户端发送的数据包,需要指明接收的服务端的IP地址以及端口
            DatagramPacket packet =
                    new DatagramPacket(bytes,bytes.length,InetAddress.getLocalHost(),8888);
            //发送数据包
            socket.send(packet);
        }

此外,服务器是否能够接收多个客户端发送的消息呢,当然可以,只需要将客户端程序设置为允许多开即可。

在这里插入图片描述

TCP通信开发

在这里插入图片描述

public ServerSocket(int port) 为服务端程序注册端口

public Socket accept()方法:阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象。
客户端设计实现

		Socket socket = new Socket("127.0.0.1",8088);
        //从socket中获取一个字节输出流,用于给服务端发送
        OutputStream outputStream = socket.getOutputStream();
        //原本的字节输出流并不好用,将其封装为高级的数据输出流
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        //开始写数据
        dataOutputStream.writeUTF("我爱你!");
        dataOutputStream.close();
        socket.close();

服务端设计实现

		System.out.println("服务端启动");
        //创建ServerSocket对象,并指明端口号,方便接收客户端数据
        ServerSocket serverSocket = new ServerSocket(8088);
        //调用accept方法,等待客户端的连接请求
        Socket accept = serverSocket.accept();
        //从socket的通信管道中得到一个字节输入流
        InputStream inputStream = accept.getInputStream();
        //将原始的字节输入流包装成高级的数据输入流
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        //使用数据输入流读取客户端发送的数据
        String string = dataInputStream.readUTF();//通信很严格,要保持一致
        System.out.println(string);
        System.out.println(accept.getInetAddress());//输出发送客户端的IP地址
        dataInputStream.close();
        serverSocket.close();

在这里插入图片描述

在这里插入图片描述

同样的,我们只是完成一条消息的发送与接收,那么该如何实现数据的多发多收呢?

TCP通信多发多收

其实实现与UDP时的完全相同,只需要一个循环即可。
全选要加入循环的语句,按住Ctrl+Alt+T

多发多收服务端设计:

public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        //创建ServerSocket对象,并指明端口号,方便接收客户端数据
        ServerSocket serverSocket = new ServerSocket(8088);
        //调用accept方法,等待客户端的连接请求
        Socket accept = serverSocket.accept();
        //从socket的通信管道中得到一个字节输入流
        InputStream inputStream = accept.getInputStream();
        //将原始的字节输入流包装成高级的数据输入流
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        //使用数据输入流读取客户端发送的数据
        while (true) {
            String string = dataInputStream.readUTF();//通信很严格,要保持一致
            System.out.println(string);
            System.out.println(accept.getInetAddress());//输出发送客户端的IP地址
        }
    }

多发多收客户端设计:

 public static void main(String[] args) throws IOException {
        //创建Socket对象,并同时请求服务端程序的连接,声明服务器的IP与端口号
        Socket socket = new Socket("127.0.0.1",8088);
        //从socket中获取一个字节输出流,用于给服务端发送
        OutputStream outputStream = socket.getOutputStream();
        //原本的字节输出流并不好用,将其封装为高级的数据输出流
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        //开始写数据
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("请输入内容:");
            String string = scanner.nextLine();
            if ("exit".equals(string)) {
                dataOutputStream.close();
                socket.close();
                System.out.println("退出成功!");
                break;
            }
            dataOutputStream.writeUTF(string);
            dataOutputStream.flush();//将数据刷新出去,防止数据还留在内存中
        }
    }

在这里插入图片描述

同时需要注意,TCP作为可靠连接,一旦服务端挂掉了,那么就会抛出异常

在这里插入图片描述

我们可以通过捕获抛出的异常,来判断是否客户端退出。

public class Servers {
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        //创建ServerSocket对象,并指明端口号,方便接收客户端数据
        ServerSocket serverSocket = new ServerSocket(8088);
        //调用accept方法,等待客户端的连接请求
        Socket accept = serverSocket.accept();
        //从socket的通信管道中得到一个字节输入流
        InputStream inputStream = accept.getInputStream();
        //将原始的字节输入流包装成高级的数据输入流
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        //使用数据输入流读取客户端发送的数据
        while (true) {
            try {
                String string = dataInputStream.readUTF();//通信很严格,要保持一致
                System.out.println(string);
            } catch (IOException e) {
                System.out.println(accept.getInetAddress()+"客户端退出了!");//输出发送客户端的IP地址
                dataInputStream.close();
                serverSocket.close();
                break;
            }
        }

    }
}

在这里插入图片描述

TCP通信聊天室

如何实现一个服务器与多个客户端通信呢?现在的肯定是不行的,因为我们在判断客户端关闭后也将服务端关闭了,事实上,此时服务端只能和一个客户端建立可靠连接,归根接地,是因为在建立连接后,服务端一直在等待某一个客户端发送的消息,这就导致会停留在那,从而无法与其他客户端建立连接。怎么办呢?可以使用多线程来解决。

在这里插入图片描述
改进后的服务端:

package IPAddress.TCP;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerMany {
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        //创建ServerSocket对象,并指明端口号,方便接收客户端数据
        ServerSocket serverSocket = new ServerSocket(8088);
        //调用accept方法,等待客户端的连接请求
        Socket accept = null;
        while (true) {
            accept = serverSocket.accept();
            new ServerReadThread(accept).start();
        }
    }
}

多开线程实现服务端接收数据

package IPAddress.TCP;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class ServerReadThread extends Thread{
    private Socket socket;
    public ServerReadThread(Socket accept) {
        this.socket=accept;
    }

    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();
            //将原始的字节输入流包装成高级的数据输入流
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            System.out.println(socket.getInetAddress()+"客户端上线了!");//输出发送客户端的IP地址
            //使用数据输入流读取客户端发送的数据
            while (true) {
                try {
                    String string = dataInputStream.readUTF();//通信很严格,要保持一致
                    System.out.println(string);
                } catch (IOException e) {
                	System.out.println(socket.getInetAddress()+"客户端下线了!");
                    dataInputStream.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            System.out.println("服务异常!" + socket.getInetAddress() + "连接中断!");
        }
    }
}

在这里插入图片描述

最后,我们可以通过一个聊天室的案例来简单检验一下成果:

在这里插入图片描述
首先我们需要在服务端定义一个集合,用于保存连接的socket,同时由主线程负责创建socket连接,一旦有新的客户端开启,则开启一个新的子线程,用于该客户端与服务端之间的通信:

public class Server {
    public static List<Socket> sockets=new ArrayList<>();
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        //创建ServerSocket对象,并指明端口号,方便接收客户端数据
        ServerSocket serverSocket = new ServerSocket(8088);
        //调用accept方法,等待客户端的连接请求
        Socket accept = null;
        while (true) {
            accept = serverSocket.accept();
            sockets.add(accept);
            new ServerReadThread(accept).start();
        }
    }
}

在服务端的子线程中,负责将接收的信息输出,并将接收的信息转发给其他客户端(端口转发)

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

public class ServerReadThread extends Thread{
    private Socket socket;
    public ServerReadThread(Socket accept) {
        this.socket=accept;
    }

    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();
            //将原始的字节输入流包装成高级的数据输入流
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            System.out.println(socket.getInetAddress()+"客户端上线了哟!");//输出发送客户端的IP地址
            //使用数据输入流读取客户端发送的数据
            while (true) {
                try {
                    String string = dataInputStream.readUTF();//通信很严格,要保持一致
                    System.out.println(string);
                    sendMsg(string);
                } catch (IOException e) {
                    System.out.println(socket.getInetAddress()+"客户端下线了!");
                    Server.sockets.remove(socket);
                    dataInputStream.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            System.out.println("服务异常!" + socket.getInetAddress() + "连接中断!");
        }
    }

    private void sendMsg(String string) throws IOException {
        //发送给所有Socket管道去接收
        System.out.println("转发数据");
        for (Socket online:Server.sockets) {
            OutputStream outputStream = online.getOutputStream();
            DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
            dataOutputStream.writeUTF(string);
            dataOutputStream.flush();
        }
    }
}

在客户端设计中,除了原本的输入数据发送信息外,还要开启一个线程用于接收服务器转发的数据:

public class Clients {
    public static void main(String[] args) throws IOException {
        //创建Socket对象,并同时请求服务端程序的连接,声明服务器的IP与端口号
        Socket socket = new Socket("127.0.0.1",8088);
        new ClientReadThread(socket).start();
        //从socket中获取一个字节输出流,用于给服务端发送
        OutputStream outputStream = socket.getOutputStream();
        //原本的字节输出流并不好用,将其封装为高级的数据输出流
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        //开始写数据
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("请输入内容:");
            String string = scanner.nextLine();
            if ("exit".equals(string)) {
                dataOutputStream.close();
                socket.close();
                System.out.println("退出成功!");
                break;
            }
            dataOutputStream.writeUTF(string);
            dataOutputStream.flush();//将数据刷新出去,防止数据还留在内存中
        }
    }
}

客户端接收转发信息的线程设计如下:

public class ClientReadThread extends Thread{
    private Socket socket;
    public ClientReadThread(Socket accept) {
        this.socket=accept;
    }
    @Override
    public void run() {
        try {
            InputStream inputStream = socket.getInputStream();
            //将原始的字节输入流包装成高级的数据输入流
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            //使用数据输入流读取客户端发送的数据
            while (true) {
                try {
                    String string = dataInputStream.readUTF();//通信很严格,要保持一致
                    System.out.println(string);
                } catch (IOException e) {
                    System.out.println("自己客户端下线了!");
                    dataInputStream.close();
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            System.out.println("服务异常!" + socket.getInetAddress() + "连接中断!");
        }
    }
}

在这里插入图片描述

BS架构通信开发

首先了解一下BS架构的基本原理:

在这里插入图片描述
BS架构下,我们并不需要开发客户端程序。只需要开发服务端即可

public class Server {
    public static List<Socket> sockets=new ArrayList<>();
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        //创建ServerSocket对象,并指明端口号,方便接收客户端数据
        ServerSocket serverSocket = new ServerSocket(9090);
        while (true) {
            Socket accept = serverSocket.accept();
            System.out.println("子线程启动");
            sockets.add(accept);
            new ServerReadThread(accept).start();
        }
    }
}

随后进行服务端子进程的设计,用于向浏览器响应一段文字,注意,要想向浏览器响应内容,就需要遵循固定的HTTP协议规定,即符合下面的要求:

在这里插入图片描述
服务端子线程设计如下:

public class ServerReadThread extends Thread{
    private Socket socket;
    public ServerReadThread(Socket accept) {
        this.socket=accept;
    }

    @Override
    public void run() {
        try {
            System.out.println("上线访问了");
            OutputStream outputStream = socket.getOutputStream();
            //将原始的字节输出流包装成高级的打印流
            PrintStream printStream = new PrintStream(outputStream);
            printStream.println("HTTP/1.1 200 OK");
            printStream.println("Content-Type:text/html;charset=UTF-8");
            printStream.println();
            printStream.println("<div style='color:red;font-size:40px;text-align:center'>服务端响应</div>");
            printStream.close();
            socket.close();
        } catch (IOException e) {
            System.out.println("服务异常!" + socket.getInetAddress() + "连接中断!");
        }
    }
}

前面的BS架构设计中,每当浏览器发起一次访问,就会创建一个线程,然而,当我们的网站访问量十分大时,即面对一些高并发情况,就会出现宕机现象。对于这种情况,可以通过线程池来进行优化。

在这里插入图片描述

在主线程中设计一个线程池来控制线程数量

public class Server {
    public static List<Socket> sockets=new ArrayList<>();
    public static void main(String[] args) throws IOException {
        System.out.println("服务端启动");
        //创建ServerSocket对象,并指明端口号,方便接收客户端数据
        ServerSocket serverSocket = new ServerSocket(9090);
        //通过线程池来控制执行线程的数量与任务队列数量
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                16 * 2, 16 * 2,
                0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(8),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        while (true) {
            Socket accept = serverSocket.accept();
            System.out.println("子线程启动");
            threadPoolExecutor.execute(new ServerReadRunnable(accept));
        }
    }
}

将原本的线程改造为任务。

public class ServerReadRunnable implements Runnable{
    private Socket socket;
    public ServerReadRunnable(Socket accept) {
        this.socket=accept;
    }

    @Override
    public void run() {
        try {
            System.out.println("上线访问了");
            OutputStream outputStream = socket.getOutputStream();
            //将原始的字节输出流包装成高级的打印流
            PrintStream printStream = new PrintStream(outputStream);
            printStream.println("HTTP/1.1 200 OK");
            printStream.println("Content-Type:text/html;charset=UTF-8");
            printStream.println();
            printStream.println("<div style='color:red;font-size:40px;text-align:center'>服务端响应</div>");
            printStream.close();
            socket.close();
        } catch (IOException e) {
            System.out.println("服务异常!" + socket.getInetAddress() + "连接中断!");
        }
    }
}

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

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

相关文章

04-18 周四 为LLM_inference项目配置GitHub CI过程记录

04-18 周四 为LLM_inference项目配置GitHub CI过程记录 时间版本修改人描述2024年4月18日10:30:13V0.1宋全恒新建文档 简介和相关文档 04-15 周一 GitHub仓库CI服务器配置过程文档actions-runner 是托管与GitHub上的仓库&#xff0c;下载最新的客户端程序即可。self hosted r…

spring高级篇(十)

1、内嵌tomcat boot框架是默认内嵌tomcat的&#xff0c;不需要手动安装和配置外部的 Servlet 容器。 简单的介绍一下tomcat服务器的构成&#xff1a; Catalina&#xff1a; Catalina 是 Tomcat 的核心组件&#xff0c;负责处理 HTTP 请求、响应以及管理 Servlet 生命周期。它包…

考研数学|基础跟张宇,强化直接1000题还是先做660?

跟宇哥用1000题的&#xff0c;我愿称之为卷王之王&#xff01;660对基础阶段是绝佳的查漏补缺&#xff0c;必做&#xff01; 自我介绍一下&#xff1a;我21年一战数学83&#xff0c;总分没过线&#xff0c;22年二战143&#xff0c;逆袭上岸211&#xff01;660是我的心头好&…

Python测试框架Pytest的参数化详解

上篇博文介绍过&#xff0c;Pytest是目前比较成熟功能齐全的测试框架&#xff0c;使用率肯定也不断攀升。 在实际工作中&#xff0c;许多测试用例都是类似的重复&#xff0c;一个个写最后代码会显得很冗余。这里&#xff0c;我们来了解一下pytest.mark.parametrize装饰器&…

【intro】图卷积神经网络(GCN)-续

本文为【intro】图卷积神经网络&#xff08;GCN&#xff09;-CSDN博客后续&#xff08;因为经验告诉我超过2w字编辑器就会卡……&#xff09; 第一部分还是进一步再看看GCN 图卷积神经网络GCN_哔哩哔哩_bilibili 回顾 图神经网络的基本原理就是把图中的节点编码映射成一个低…

4.堆_树(汇总版)

目录 1.树概念及结构 1.1树的概念 1.2 树的相关定义 1.3 树的表示 2.二叉树概念及结构 2.1概念 2.2现实中的二叉树 2.3 特殊的二叉树 2.4 二叉树的性质 2.5 二叉树的存储结构 3.二叉树的顺序结构及实现 3.1 二叉树的顺序结构--堆 3.2 堆的实现 3.2.1打印 3.2.2 …

怎么通过Java语言实现远程控制无人售货柜

怎么通过Java语言实现远程控制无人售货柜呢&#xff1f; 本文描述了使用Java语言调用HTTP接口&#xff0c;实现控制无人售货柜&#xff0c;独立控制售货柜、格子柜的柜门。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称厂商1智能WiFi控…

CellMarker | 人骨骼肌组织细胞Marker大全!~(强烈建议火速收藏!)

1写在前面 分享一下最近看到的2篇paper关于骨骼肌组织的细胞Marker&#xff0c;绝对的Atlas级好东西。&#x1f44d; 希望做单细胞的小伙伴觉得有用哦。&#x1f60f; 2常用marker&#xff08;一&#xff09; general_mrkrs <- c( MYH7, TNNT1, TNNT3, MYH1, MYH2, "C…

2024.05.06作业

自由发挥应用场景&#xff0c;实现登录界面。 要求&#xff1a;尽量每行代码都有注释。 #include "yuanshen.h"yuanshen::yuanshen(QWidget *parent): QWidget(parent) {//窗口相关设置this->resize(1600,910);this->setFixedSize(1600,910);//窗口标题this-…

vue3中标签的ref属性

组合API-ref属性 在vue2.x中&#xff0c;可以通过给元素添加refxxx属性&#xff0c;然后在代码中通过this.$refs.xxx获取到对应的元素 然而在vue3中时没有$refs这个东西的&#xff0c;因此vue3中通过ref属性获取元素就不能按照vue2的方式来获取。 目标&#xff1a;掌握使用re…

java基础之Netty(主要用来开发像微信,qq等聊天软件)

一、Netty的定义 Netty是一个基于Java NIO的网络编程框架&#xff0c;提供了一套高效的、事件驱动的异步网络通信机制。简化了网络应用程序的开发过程&#xff0c;提供了可靠的、高性能的网络传输 二、Netty的特点是什么 异步和事件驱动&#xff1a;Netty使用异步的、非阻塞…

链表经典面试题01

目录 引言 面试题01:返回倒数第k个节点 题目描述: 思路分析: 代码展示: 面试题02:链表的回文结构 题目描述: 描述 思路分析: 代码展示: 面试题03:相交链表 题目描述: 思路分析: 代码展示: 小结: 引言 这次的题均来自力扣和牛客有关链表的经典面试题,代码只会展示…

【C++】命名空间namespace

为什么要有namespace&#xff1f; namespace是用来解决命名冲突&#xff08;名字污染&#xff09;的问题的。看看下面的代码&#xff1a; #include <cstdlib>int rand 10;int main() {return 0; }可以看到&#xff0c;这段代码什么都没干&#xff0c;就定义了一个全局…

[leetcode] 64. 最小路径和

文章目录 题目描述解题方法动态规划java代码复杂度分析 相似题目 题目描述 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例 1&#…

【字符串】Leetcode 43. 字符串相乘

题目讲解 43. 字符串相乘 算法讲解 class Solution { public:void AddZero(string& temp, int cnt) {while (cnt--) {temp 0;} }string Add(string num1, string num2) {string result;int carry 0;int i num1.size() - 1;int j num2.size() - 1;while (i > 0 ||…

制作微信在线答题步骤是什么

在信息爆炸的时代&#xff0c;学习不再只是书本和课堂的代名词。随着移动互联网的飞速发展&#xff0c;微信作为人们日常生活中不可或缺的沟通工具&#xff0c;也为我们提供了全新的学习平台。今天&#xff0c;就让我们一起探讨如何制作微信在线答题&#xff0c;让学习变得更加…

五种主流数据库:窗口函数

SQL 窗口函数为在线分析系统&#xff08;OLAP&#xff09;和商业智能&#xff08;BI&#xff09;提供了复杂分析和报表统计的功能&#xff0c;例如产品的累计销量统计、分类排名、同比/环比分析等。这些功能通常很难通过聚合函数和分组操作来实现。 本文比较了五种主流数据库实…

如何使用摇摆交易?fpmarkets实例讲解

各位投资者五一节后快乐&#xff01;祝愿投资者在接下来的日子里每次交易都以盈利结算。 五一节日也是劳动节&#xff0c;在这个特殊的日子里fpmarkets澳福和各位勤劳的投资者一起学习如何使用摇摆交易策略进行交易&#xff1f; 其实很简单&#xff0c;首先判断出买卖点&#x…

淡茶怎么泡?

很多人都知道喝浓茶对身体不好&#xff0c;但是怎么冲泡淡茶却一知半解。按照《品深淡茶冲泡标准》中对绿茶冲泡淡茶的规定&#xff0c;冲泡的茶汤中咖啡碱不得高于31.67mg/100mL&#xff0c;可可碱不得高于2.67mg/mL&#xff0c;茶碱不得高于1.50mg/100mL&#xff0c;茶多酚不…

无经验计科应届生前端面试遇到的问题整理

js数据类型有几种&#xff0c;分别是 原始数据类型&#xff08;Primitive data types&#xff09;: 字符串&#xff08;String&#xff09;: 用于表示文本数据&#xff0c;使用单引号&#xff08;‘’&#xff09;或双引号&#xff08;“”&#xff09;括起来。 数字&#xff…