网络编程套接字(2)

news2024/11/21 0:15:00

UDP数据报套接字编程

API介绍

DatagramSocket

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

操作系统中有一类文件,就叫做socket文件(普通文件/目录文件:在硬盘上的)

socket文件:抽象的表示了网卡这样的硬件设备

DatagramSocket就是对socket文件进行读写,也就是借助网卡发送数据.

通过网卡发送数据,就是写socket文件;通过网卡读取数据,就是读socket文件.

DatagramSocket构造方法:

方法签名方法说明
DatagramSocket()

创建一个UDP数据报套接字的Socket,绑定到本机

任意一个随机端口(一般用于客户端)

DatagramSocket(int port)

创建一个UDP数据报套接字的Socket,绑定到本机

指定的端口(一般用于服务端)

DatagramSocket方法:

方法签名方法说明
void receive(DatagramPacket p)

从此套接字接收数据报(如果没有收到数据报,该

方法会阻塞等待)

void send(DatagramPacket p)从此套接字发送数据包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

DatagramPacket

UDP数据报,每次接收数据的基本单位,就是一个UDP数据报

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

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方法:

方法签名方法说明
InetAddress getAddress()

从接收的数据报中,获取发送端主机IP地址;

或从发送的数据报中,获取接收端主机IP地址

int getPort()

从接收的数据报中,获取发送端主机的端口号;

或从发送的数据报中,获取接收端主机的端口号

byte[] getData()获取数据报中的数据

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

InetSocketAddress

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

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

代码示例

UDP Echo Server

下面以一个简单的回显服务器作为代码示例的程序(回显服务器:客户端发啥请求返回啥响应)

 警告:文本解析巨长无比

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

public class UdpEchoServer {
    /*
    最开始的一步:先创建DdtagramSocket对象
    接下来需要操作网卡,操作网卡是通过socket对象完成的
    socket对象是在内存中的,通过这个来影响网卡(类似遥控)
     */
    private DatagramSocket socket = null;

    /*
    一个主机上的一个端口号只能被一个进程绑定,反过来,一个进程可以绑定多个端口
    创建对象时,手动指定一个端口号(在运行服务器程序的时候,主动指定端口)
    程序一启动就需要关联上/绑定上一个操作系统中的端口号
    端口号也是一个整数,用来区分一个主机上进行网络通信的程序.
     */

    //参数时服务器要绑定的端口
    public UdpEchoServer(int port) throws SocketException {
        /*
        SocketException是网络编程中的常见异常,通常表示
        socket创建失败,比如端口号被别的进程占用,就会失败
         */
        socket = new DatagramSocket(port);
    }

    //使用这个方法启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true) {
            //反复的,长期的执行针对客户端请求处理的逻辑.(不停的收到请求,返回响应)
            //一个服务器,运行的过程中,要做的事情,主要是三个核心环节.
            //1.读取请求,并解析.  (一个服务器单位时间处理的请求返回响应越多,服务器越厉害)
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            /*
            receive就从网卡中读出了一个数据报,就被放入了requestPacket对象中.
            其中UDP数据报的载荷部分就被放到requestPacket内置的字节数组中了.
            除了UDP报头,还有其它信息,比如收到的数据:源IP
             */
            //如果执行到这个地方,没有客户端请求的话,就会阻塞.
            socket.receive(requestPacket);
            //转成字符串,方便逻辑处理(前提:后续客户端发的就是一个文本字符串)
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength()); //有效长度
            //2.根据请求,计算出响应(对于回显服务器,这一步什么都不用做)
            String response = process(request);
            //3.把响应写回给客户端
            //此时需要告知网卡,要发的内容是啥,要发给谁
            //requestPacket是客户端发来的数据报.
            //通过getSocketAddress()得到InetAddress对象,这个对象就包含了和服务器的通信对端(对应客户端IP)
            //此时就起到了把消息返回给客户端的效果(还可以看到UDP是无连接通信,socket不包含对端IP,端口)
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length, requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //记录日志,方便观察程序执行效果
            System.out.printf("[%s : %d] req: %s, resq: %s\n", requestPacket.getAddress()
            requestPacket.getPort(), request, response);
        }
    }
    /*
    上述代码中,可以看到UDP是无连接的通信~UDPsocket自身不保存对端的IP和端口
    而是在每个数据报中有一个.另外代码中也"建立连接","接受连接"的擦做
    面向数据报,send和receive都是以DatagramPacket为主
     */
    
    //根据请求计算响应
    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        //指定任何想要的端口,但要确保这个端口在机器上未被其它进程占用
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}

 UDP Echo Client

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;
    private String serverIp;
    private int serverPort;

    //服务器的ip和服务器的端口 (因为发起请求的前提就是知道服务器在哪).
    public UdpEchoClient(String ip, int port) throws SocketException {
        serverIp = ip;
        serverPort = port;
        //这个new操作,就不再指定端口了.让系统分配一个空闲端口
        socket = new DatagramSocket();
    }

    /*
    在服务器中,在代码中需要手动指定端口号,才能保证端口始终固定
    如果不手动指定,依赖系统自动分配导致服务器重启之后,端口号就
    变了,客户端可能找不到服务器在哪了
    服务器这个机器,是在程序员手里的,是可控的.
    程序员能知道服务器有哪些端口被使用,客户端是在普通用户的机器上
     */

    //让这个客户端反复的从控制台读取用户输入的内容,把这个内容构成UDP请求,发送给服务器
    //最终显示在客户端的屏幕上
    public void start() throws IOException {
        Scanner sc = new Scanner(System.in);
        System.out.println("客户端启动!");
        while(true) {
            //1.从控制台读取用户输入内容
            System.out.println("->"); //命令提示符,提示用户要输入的字符串
            String request = sc.next(); //从控制台读取,最好使用next而不是nextLine
            //2.构造请求对象,并发给服务器
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
                    request.getBytes().length, InetAddress.getByName(serverIp), serverPort);
            socket.send(requestPacket);
            //3.读取服务器的响应,解析出响应内容.
            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", 9090);
        client.start();
    }
}

让我们结合一下服务器和客户端,来看一下执行的流程:

 1.服务器启动,启动之后,立即进入while循环,执行到receive(),进入阻塞等待,这时还未收到客户端的请求.

2.客户端启动,启动之后进入while循环,执行到输入那里堵塞,此时用户未输入内容.

3.用户在客户端输入字符串回车.此时阻塞解除,next会返回刚才的内容.基于用户输入内容,构造一个DatagramPacket对象,并进行send.send执行完之后,继续receive操作,等待服务器响应数据(此时服务器还没响应,就会阻塞).

4.服务器收到请求之后,就会从receive阻塞中返回.返回之后,就会根据读到的DatagramPacket对象,构造String request,通过process方法构造出一个String response,再根据response构造一个DatagramPacket表示响应对象.再通过send来进行发送给客户端.(执行这个的过程中,客户端也在阻塞等待).

5.客户端从receive中返回执行,就能获取到服务器返回的响应,并且打印到控制台上,与此同时,服务器进入下一个环节,也就是进入到第二轮receive阻塞.等待下一个请求了.

UDP Dict Server

我们之前写的服务器是回显服务器,我们来扩展以下,写一个处理简单英译汉功能的服务器.

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

public class UdpDictServer extends UdpEchoServer{
    private Map<String, String> dict = new HashMap<>();

    public UdpDictServer(int port) throws SocketException {
        super(port);

        dict.put("cat", "小猫");
        dict.put("dog", "小狗");
        dict.put("fuck", "我爱你");
        //可以在这里添加千千万万个单词.使每一个单词都有对应的翻译
    }

    @Override
    public String process(String request) {
        //把请求对应单词的翻译,给返回回去
        return dict.getOrDefault(request, "该单词没有查询到!");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer server = new UdpDictServer(9090);
        server.start(); //在此处会除法多态
    }
}

TCP流套接字编程 

 和刚才的UDP类似.但是TCP是面向字节流的,传输的基本单位是字节

API介绍

ServerSocket

这个Socket类对应到网卡,只能给服务器使用.

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

ServerSocket构造方法:

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

ServerSocket方法:

方法签名方法说明
Socket accept()

开始监听指定端口(创建时绑定的端口),有客户端

连接后,返回一个服务端socket对象,并基于该

Socket建立与客户端的连接,否则阻塞等待

void close()关闭此套接字

 Socket

对应到网卡,既可以给客户端使用,也可以给服务器使用.

Socket是客户端Socket,或服务端接收到客户端建立的连接(accept方法)的请求后,返回的服务端Socket.

不管是客户端还是服务端Socket,都是双方建立连接之后,保存对端信息,及用来与对方收发数据的.

Socket构造方法:

方法签名方法说明
Socket(String host, int port)

创建一个客户端流套接字Socket,并与对应IP的主机

上,对应端口的进程进行连接

Socket方法:

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

代码示例

TCP Echo Server

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 {
     private ServerSocket serverSocket = null;

    //这个操作就会绑定端口号
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    //启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true) {
            //这是个建立链接的过程,就相当于接地阿努好的时候这边拨完号打过去,另一边在响铃
            //通过accept()操作接听电话,然后进行通信
            //clientSocket负责后续通信交互
            Socket clientSocket = serverSocket.accept();
            /*accept可能会产生阻塞.没有客户端,就会阻塞.有客户端的时候再处理
            有一个客户端来了, accept一次就能返回一次.
            有多个客户端连过来了,accept就会执行多次
            */
            processConnection(clientSocket);
        }
    }

    //通过这个方法来处理一个连接的逻辑
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
        //接下来就可以读取请求,根据请求计算响应,返回响应这三步走了.
        //Socket对象包含两个字节流对象,可以把这两字节流对象获取到,完成后续的读写
        /*
        inputStream是从网卡中读,OutputStream是向网卡中写
        TCP是面向字节流的.和文件操作以一样的类和方法来完成tcp socket的读写
         */
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            //一次连接中,可能会涉及到多次请求/响应
            while(true) {
                //1.读取请求并解析..为了读取方便,直接使用Scanner
                Scanner sc = new Scanner(inputStream);
                //客户端退出的时候就会触发Tcp的"断开连接"流程.
                //服务器这边的代码也会感知到,对应Scanner就在hasNext()处返回false.
                if(!sc.hasNext()) {
                    //读取完毕,客户端下线.
                    System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
                    break;
                }
                //这个代码暗含一个约定,客户端发过来的请求,得是文本数据. next读的时候要督导空白符才会结束.
                // 因此就要客户端发来的请求必须带有空白符结尾.比如\n或者空格.
                String request = sc.next();
                //2.根据请求计算相应
                String response = process(request);
                //3.把响应写回客户端
                // 通过这种方式可以写回,但是这种方式不方便给返回的响应中添加\n
                //outputStream.write(response.getBytes(), 0, response.getBytes().length);
                // 也可以给outputStream套上一层,完成更方便的写入.
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                //这里还需要加一个"刷新缓冲区"的操作
                printWriter.flush();
                
                //日志,打印当前的请求详细.
                System.out.printf("[%s:%d] req: %s, resq: %s\n", clientSocket.getInetAddress(),
                        clientSocket.getPort(), request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //在finally中加上close操作,确保当前socket被正确关闭
            /*
            tcp的clientSocket是每个客户端都有一个.随着客户端
            越来越多.这里消耗的socket也会越来越多(如果不释放,就可能把文件操作符表占满)
             */
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

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

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

Tcp Echo Client

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 {
        //这个new操作完成之后,就完成了tcp连接的建立
        socket = new Socket(serverIp, serverPort);
    }

    public void start() {
        System.out.println("客户端启动!");
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {
            Scanner scannerConsole = new Scanner(System.in);
            Scanner scannerNetwork = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while(true) {
                //1.从控制台中读取字符串
                System.out.println("->");
                if(!scannerConsole.hasNext()) {
                    break;
                }
                String request = scannerConsole.next();
                //2.把请求发给服务器
                //使用println带上换行, 后续服务器读取请求,就使用scanner.next()读取
                printWriter.println(request);
                printWriter.flush();
                // 不要忘记flush, 确保数据是真的发出去了
                //3.从服务器中读取响应
                String response = scannerNetwork.next();
                //4.把响应打印出来
                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();
    }
}

服务器引入多线程

如果只是单个线程,无法同时响应多个客户端.

虽然第二个客户端和服务器在内核层面上建立tcp连接了,但是应用程序这里无法把连接拿到应用程序里处理(人家给你打电话,电话一直响,但是不接)

这个问题,不是tcp引起的,而是因为代码结构不够好,两层循环所引起.UDP服务器只有一层循环.就不涉及该问题.之前UDP服务器天然可处理多个客户端请求.

所以主要的问题就是无法既能给客户端循环提供服务,又能去快速调用到第二个accept.

这时我们就想到了多线程的方法,此处给每个客户端都分配一个线程.

//启动服务器
public void start() throws IOException {
    System.out.println("服务器启动!");
    while(true) {
        Socket clientSocket = serverSocket.accpet();
        //每次来一个客户端,就创建一个新线程.每次走一个,就销毁一个线程
        Thread t = new Thread(() -> {
            //此时,就把processConnection交给新线程负责了.
            //主循环就会快速执行完一次后,回到accept这里阻塞等待新的客户端来
            processConnection(clientSocket);
        });
        t.start();
    }
}

但是,我们不由得要思考一个新的问题,就是引入多线程,免不了频繁的线程销毁与创建,在资源申请与释放上开销比较大.所以有什么更好的方法可以解决呢>

服务器引入线程池

为了避免频繁创建销毁线程,也可以引入线程池.

//启动服务器
public void start() throws IOException {
    System.out.println("服务器启动!");
    ExecutorService service = Executors.newCachedThreadPool();
    while(true) {
        Socket clientSocket = serverSocket.accept();
        //使用线程池来解决上述问题
        service.submit(new Runnable() {
            @Override
            public void run() {
                processConnection(clientSocket);
            }
        });
    }
}

线程,解决的是线程的频繁销毁创建问题.如果,当前的场景是线程频繁的创建,而不是销毁呢

就比如像游戏服务器这种,服务器可能处理的时间非常长.

此时如果继续使用线程池/多线程,就会导致服务器一下积累大量线程,对于服务器的负担也非常重.

长短连接

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

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

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

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

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

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

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

扩展了解:

基于BIO(同步阻塞IO)的长连接会一直占用系统资源.对于并发很高的服务端系统来说,这样的消耗是不能承受的.

由于每个连接都需要不停的阻塞等待接收数据,所以每个连接都会在一个线程中运行.

一次阻塞等待对应着一次请求,响应,不停处理也就是长连接的特性:一直不关闭连接,不停的处理请求.

实际应用时,服务端一般是基于NIO(即同步非阻塞IO)来实现长连接,性能可以极大的提升.

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

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

相关文章

【Java】Spring注解开发

一、Spring注解开发 1 注解开发定义Bean对象【重点】 目的&#xff1a;xml配置Bean对象有些繁琐&#xff0c;使用注解简化Bean对象的定义 问题导入 问题1&#xff1a;使用什么标签进行Spring注解包扫描&#xff1f; 问题2&#xff1a;Component注解和Controller、Service、R…

2024-1-26学习任务:堆实现算法和topK问题

前言 本文的学习任务&#xff1a;关于堆的实现以及相关的基础操作&#xff0c;包括向上调整算法和向下调整算法&#xff0c;同时利用该算法解决常见的topk问题&#xff0c;之后再对两种算法的时间复杂度进行分析&#xff0c;加深理解。 1.堆的实现 前面提到过&#xff0c;堆…

Jmeter学习系列之一:Jmeter的详细介绍

目录 一、Jmeter的介绍 二、Jemeter的特点 三、Jemter相关概念 3.1采样器&#xff08;Samplers&#xff09; 3.2逻辑控制器&#xff08;Logic Controllers&#xff09; 3.3监听器&#xff08;Listeners&#xff09; 3.4配置元件&#xff08;Configuration Elements&#…

算法沉淀——滑动窗口(leetcode真题剖析)

算法沉淀——滑动窗口 01.长度最小的子数组02.无重复字符的最长子串03.最大连续1的个数 III04.将 x 减到 0 的最小操作数05.水果成篮06.找到字符串中所有字母异位词07.串联所有单词的子串08.最小覆盖子串 滑动窗口算法是一种用于解决数组或列表中子数组或子序列问题的有效技巧。…

不确定优化入门:用简单实例讲明白随机规划、鲁棒优化和分布鲁棒优化

文章目录 1 引言2 学习动机3 经典问题4 解决方案4.1 忽略不确定性4.2 随机规划4.3 鲁棒优化4.4 分布鲁棒优化 5 总结相关阅读 1 引言 按2024的原定计划&#xff0c;今年开始要学习不确定优化了。 粗略翻阅了一些相关的书籍和教程&#xff0c;大都包含许多数学公式&#xff0c…

xxl-job相关面试题整理

什么是xxl-job&#xff1f; ​ xxl-job是一个分布式的任务调度平台&#xff0c;其核心设计目标是&#xff1a;学习简单、开发迅速、轻量级、易扩展&#xff0c;现在已经开放源代码并接入多家公司的线上产品线&#xff0c;开箱即用。xxl是xxl-job的开发者大众点评的许雪里名称的…

腾讯云幻兽帕鲁4核16G14M服务器性能测评和价格

腾讯云幻兽帕鲁服务器4核16G14M配置&#xff0c;14M公网带宽&#xff0c;限制2500GB月流量&#xff0c;系统盘为220GB SSD盘&#xff0c;优惠价格66元1个月&#xff0c;277元3个月&#xff0c;支持4到8个玩家畅玩&#xff0c;地域可选择上海/北京/成都/南京/广州&#xff0c;腾…

在windows环境下安装hadoop

Hadoop是一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下&#xff0c;开发分布式程序。但这个架构是基于java语言开发的&#xff0c;所以要先进行jdk的安装&#xff0c;如果电脑已经配置过jdk或者是曾经运行成功过java文件&#xff0c;那就可以跳过第一步。 …

单入双出高电压信号隔离变送器

定义&#xff1a;一路高电压信号输入&#xff0c;双路国际标准模拟量信号输出的小型仪器设备。 单入双出高电压模拟量信号隔离变送器 型号&#xff1a;JSD TAH-1002 特征&#xff1a; ◆薄体积&#xff0c;低成本&#xff0c;国际标准DIN35mm导轨安装方式 ◆五端隔离(输入、…

实验2:DEBUG基本命令使用

目录 1、实验目的&#xff1a; 2、实验内容&#xff1a; 3、实验要求&#xff1a; 4、源代码&#xff1a; 5、实验结果 1、实验目的&#xff1a; 熟悉汇编语言程序设计的上机过程&#xff0c;掌握DEBUG的基本命令和功能。 2、实验内容&#xff1a; 从键盘键入一个大写英…

解锁多模态独特魅力-“机器人+Agent+多传感器融合+3DLLM”诠释终极组合大招!

01-Multiply算法背景 01.01-触觉传感器 触觉传感器是一种用于感知和测量物体接触力、形状、纹理和其他相关参数的传感器。它们模拟人类触觉系统&#xff0c;通过收集和解释物体与传感器之间的相互作用来获取信息。工作原理&#xff1a;触觉传感器使用不同的原理来感知接触力和…

Spring 事务原理二

该说些什么呢&#xff1f;一连几天&#xff0c;我都沉溺在孤芳自赏的思维中无法自拔。不知道自己为什么会有这种令人不齿的表现&#xff0c;更不知道这颗定时炸弹何时会将人炸的粉身碎骨。好在儒派宗师曾老夫子“吾日三省吾身”的名言警醒了我。遂潜心自省&#xff0c;溯源头以…

springIoc以及注解的使用

注解 注解的定义 注解&#xff08;Annotation&#xff09;是一种在 Java 程序中以元数据的形式对代码进行标记和说明的机制。它可以被添加到类、方法、字段、参数等程序元素上&#xff0c;用于提供额外的信息和指示。 也就是说注解是一种标记 注解怎么生效呢&#xff1f; 通…

Element-Plus如何实现表单校验和表单重置

一&#xff1a;页面布局介绍&#xff1a; 这是我刚刚用基于vue3element-plus写好的一个部门管理的页面 基本的增删改查已经写好&#xff0c;下面我只提供页面的template和style的代码&#xff1a; template <template><el-card class"box-card"><…

静态代理IP该如何助力Facebook多账号注册运营?

在Facebook运营中&#xff0c;充分利用静态代理IP是多账号运营的关键一环。通过合理运用静态代理IP&#xff0c;不仅可以提高账号安全性&#xff0c;还能有效应对Facebook的算法和限制。以下是这些关键点&#xff0c;可以帮助你了解如何运用静态代理IP进行Facebook多账号运营&a…

BGP:04 fake-as

使用 fake-as 可以将本地真实的 AS 编号隐藏&#xff0c;其他 AS 内的对等体在指定本端对等体所在的AS 编号时&#xff0c;应该设置成这个伪AS 编号。 这是实验拓扑&#xff0c;IBGP EBGP 邻居都使用物理接口来建立 基本配置&#xff1a; R1: sys sysname R1 int loo0 ip add…

网络原理,网络通信以及网络协议

​​​​&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录专栏&#xff1a;网络原理,网络通信以及网络协议 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录 网络原理概念网络通信局域网LAN广域网WAN 网络通信IP地址端口号…

第17节-高质量简历写作求职通关-投递反馈

&#xff08;点击即可收听&#xff09; 投递跟进和感谢信 如果对一家公司特别心仪&#xff0c;但是投递简历后一直得不到回复怎么办&#xff1f; 面试之后觉得自己没有表现好怎么办&#xff1f; 面试完几天了&#xff0c;依然没有得到回应怎么办&#xff1f; 这个时候你需要写一…

OkHttp完全解读

一&#xff0c;概述 OkHttp作为android非常流行的网络框架&#xff0c;笔者认为有必要剖析此框架实现原理&#xff0c;抽取并理解此框架优秀的设计模式。OkHttp有几个重要的作用&#xff0c;如桥接、缓存、连接复用等&#xff0c;本文笔者将从使用出发&#xff0c;解读源码&am…

sqli-labs靶场第七关

7、第七关 id1 --单引号报错,id1" --双引号不报错,可以判断是单引号闭合 id1) --也报错&#xff0c;尝试两个括号闭合&#xff0c;id1)) --不报错 接下来用脚本爆库 import stringimport requestsnumbers [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] letters2 list(string.ascii_…