Socket套接字编程(实现TCP和UDP的通信)

 

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习!

欢迎志同道合的朋友一起加油喔🦾🦾🦾
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心


目录

一.Socket概述

​ Socket通信是有两种方式的:TCP和UDP

TCP与UDP区别

socket之send和recv原理剖析

二. TCP通信客户端Socket

三. TCP通信服务器端ServerSocket

四.基于TCP的Socket通信

五.UDP相关类DatagramPacket类和DatagramSocket类

数据包类DatagramPacket

发送数据包类DatagramSocket

InetAddress类(无构造方法)

六.基于UDP的Socket通信

七. TCP和UCP的缓冲区

1.TCP的缓冲区

2.UDP的缓冲区



一.Socket概述

​ Socket(套接字),是网络上两个程序之间实现数据交换的一端,它既可以发送请求,也可以接受请求,一个Socket由一个IP地址和一个端口号唯一确定,利用Socket能比较方便的实现两端(服务端和客户端)的网络通信。

​ 在Java中,有专门的Socket类来处理用户请求和响应,学习使用Socket类方法,就可以实现两台机器之间通信。

​ Socket通信是有两种方式的:TCP和UDP

​ TCP通信:客户端提供了java.net.Socket类,服务器端提供了java.net.ServerSocket类。

​ UDP通信:UDP通信不建立逻辑连接,使用DatagramPacket类打包数据包,使用DatagramSocket类发送数据包。

TCP与UDP区别

  • TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接。
  • TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。
  • UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
  • 每一条TCP连接只能是点到点的:UDP支付一对一,一对多和多对一和多对多的交互通信。
  • TCP对系统资源要求较多,UDP对系统资源要求较少。

Socket通信模型如下图:

socket之send和recv原理剖析

当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的就是内存中的一片空间。

send原理剖析

send发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡。

recv原理剖析

应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间),应用程序再从接收缓存区获取客户端发送的数据。

 

二. TCP通信客户端Socket

​ Java中专门用来实现Socket客户端的类就叫Socket,这个类实现了客户端套接字,用于向服务器发出连接请求等。

  • 构造方法

    Socket(String host, int port):创建一个流套接字并将其连接到指定IP地址的指定端口号。

    如果host为null,则相当于指定地址为回送地址。

    127.x.x.x是本机的回送地址,即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,协议软件立即返回之,不进行任何网络传输。

  • 主要方法:

    • InputStream getInputStream():返回此套接字的输入流。

      关闭生成的InputStream也将关闭相关的Socket。

    • OutputStream getOutputStream():返回此套接字的输出流。

      关闭生成的OutputStream也将关闭相关的Socket。

    • void close():关闭此套接字

三. TCP通信服务器端ServerSocket

​ Java中专门用来建立Socket服务器的类叫ServerSocket,这个类实现了服务器套接字,该对象等待通过网络的请求。

  • 构造方法:

    ServerSocket(int port):创建绑定到特定端口的服务器套接字。

  • 主要方法:

    • Socket accept():监听并接受连接,返回一个新的Socket对象,用于和客户端通信,该方法会一直阻塞直到建立连接。

    • void close():关闭此套接字。

四.基于TCP的Socket通信

  1. 步骤分析:

    • 服务端先启动,创建ServerSocket对象,等待连接。
    • 客户端启动,创建Socket对象,请求连接。
    • 服务器端接收请求,调用accept方法,并返回一个Socket对象,连接成功
    • 客户端的Socket对象通过调用getOutputStream()方法获取OutputStream对象,并使用write()方法将数据写入到发送缓冲区。随后,通过调用flush()方法确保数据已被发送出去
    • 服务器端Socket对象通过调用getInputStream()方法获取与该socket关联的InputStream实例,然后使用read()方法从接收缓冲区中读取数据。
    • 客户端释放资源,断开连接。

服务器端:

public class TcpEchoServer {
    // serverSocket 就是外场拉客的小哥
    // clientSocket 就是内场服务的小姐姐.
    // serverSocket 只有一个. clientSocket 会给每个客户端都分配一个~
    private ServerSocket serverSocket = null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("服务器启动!");
        while (true) {
            Socket clientSocket = serverSocket.accept();
            //服务器的主线程(main 线程)负责运行 while 循环,用于接收客户端的连接请求。
            // 与此同时,针对每个接收到的连接请求,都会创建一个新线程处理与该客户端的数据通信。这些新线程与主线程是并发执行的。
            //由于主线程和新创建的线程并发执行,服务器可以在处理一个客户端连接的同时,继续接收其他客户端的连接请求。
            // 这使得服务器可以并发处理多个客户端连接,提高了服务器的处理能力。
            // 创建新的线程, 用新线程来调用 processConnection
//            Thread t = new Thread(() -> {
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            t.start();

            //使用线程池
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    // 通过这个方法来处理一个连接.
    // 读取请求
    // 根据请求计算响应
    // 把响应返回给客户端
    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(),
                clientSocket.getPort());
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            // 没有这个 scanner 和 printWriter, 完全可以!! 但是代价就是得一个字节一个字节扣, 找到哪个是请求的结束标记 \n
            // 不是不能做, 而是代码比较麻烦.
            // 为了简单, 把字节流包包装成了更方便的字符流~~
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true) {
                // 1. 读取请求
                if (!scanner.hasNext()) {
                    // 读取的流到了结尾了 (对端关闭了)
                    System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                // 直接使用 scanner 读取一段字符串.
                String request = scanner.next();
                // 2. 根据请求计算响应
                String response = process(request);
                // 3. 把响应写回给客户端. 不要忘了, 响应里也是要带上换行的.
                printWriter.println(response);
                //数据此时还在缓存区,使用 flush() 方法,可以确保数据立即发送
                printWriter.flush();
                //clientSocket.getInetAddress().toString() 返回客户端的 IP 地址,clientSocket.getPort() 返回客户端的端口号,
                // request 是客户端发送的请求,response 是服务器响应的数据。
                // 通过格式化输出的方式将这些信息打印出来,方便程序员进行调试和查看。
                System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(),
                        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 {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.start();
    }
}

在服务器端我们使用了try-with-resources语句,确保在代码块执行完毕后自动关闭资源,无论代码执行过程中是否发生异常。

当程序执行到try语句块结束时,如果resource实现了AutoCloseableCloseable接口,那么close()方法将被自动调用。

同时在服务器端引入了多线程的写法,保证服务器能连接多个客户端,同时与多个客户端保持通信,具体实现逻辑如下:

服务器的主线程(main 线程)负责运行 while 循环,用于接收客户端的连接请求。
与此同时,针对每个接收到的连接请求,都会创建一个新线程处理与该客户端的数据通信。这些新线程与主线程是并发执行的。由于主线程和新创建的线程并发执行,服务器可以在处理一个客户端连接的同时,继续接收其他客户端的连接请求。 这使得服务器可以并发处理多个客户端连接,提高了服务器的处理能力。

 当然我们也可以引入线程池来优化这段代码:

  1. 线程池中的线程数量是动态调整的。
  2. 当有新任务提交时,如果线程池中有空闲线程,那么会复用空闲线程来执行新任务;如果没有空闲线程,则会创建一个新线程来执行新任务。
  3. 当线程池中的线程空闲时间超过一定时间(默认为 60 秒)时,线程池会回收这个空闲线程。

好处:线程池可以复用已经创建的线程,避免了频繁地创建和销毁线程所带来的性能开销。当有新任务到来时,线程池会优先使用空闲的线程,从而提高系统资源的利用率。

 客户端:

public class TcpEchoClient {
    private Socket socket = null;

    public TcpEchoClient(String serverIp, int port) throws IOException {
        // 这个操作相当于让客户端和服务器建立 tcp 连接.
        // 这里的连接连上了, 服务器的 accept 就会返回.
        socket = new Socket(serverIp, port);
    }

    public void start() {
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {
            //outputStream 是一个字节输出流,通过 PrintWriter 对象的构造方法将其包装成字符输出流,以便能够方便地写入字符数据。
            PrintWriter printWriter = new PrintWriter(outputStream);
            //scannerFromSocket 对象是将输入流对象包装成一个 Scanner 对象,以便能够方便地读取输入流中的数据。
            // Scanner 对象会自动解析和分隔输入流中的数据,并将其转换为相应的数据类型或字符串。
            Scanner scannerFromSocket = new Scanner(inputStream);

            while (true) {
                // 1. 从键盘上读取用户输入的内容.
                System.out.print("-> ");
                String request = scanner.next();
                // 2. 把读取的内容构造成请求, 发送给服务器.
                //在 Java 中,可以通过 PrintWriter 对象的 println() 方法发送带有换行符的数据包。
                // 该方法会将指定的字符串添加一个换行符,并将其发送到输出流中。
                printWriter.println(request);
                //数据此时还在缓存区,使用 flush() 方法,可以确保数据立即发送
                printWriter.flush();
                // 3. 从服务器读取响应内容
                //next() 方法会读取下一个标记,而不是一行数据。标记通常是以空格、制表符或换行符为分隔符的单词或符号。
                // 因此,在读取数据时,如果数据包中只有一个标记,则可以使用 next() 方法读取该标记。
                String response = scannerFromSocket.next();
                // 4. 把响应结果显示到控制台上.
                // request 是客户端发送的请求,response 是服务器响应的数据。
                // 通过格式化输出的方式将这些信息打印出来,方便程序员进行调试和查看。
                System.out.printf("req: %s; resp: %s\n", request, 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();
    }
}

 PrintWriter printWriter = new PrintWriter(outputStream);           

outputStream 是一个字节输出流,通过 PrintWriter 对象的构造方法将其包装成字符输出流,以便能够方便地写入字符数据。

Scanner scannerFromSocket = new Scanner(inputStream);

scannerFromSocket 对象是将输入流对象包装成一个 Scanner 对象,以便能够方便地读取输入流中的数据。
Scanner 对象会自动解析和分隔输入流中的数据,并将其转换为相应的数据类型或字符串。

 

五.UDP相关类DatagramPacket类和DatagramSocket类

  1. 数据包类DatagramPacket

    • 作用:用来封装发送端或接收端要发送或接收的数据。
    • 构造方法
      • DatagramPacket(byte[] buf, int length):构造DatagramPacket,用来接收长度为length的数据包。
      • DatagramPacket(byte[] buf, int length, InetAddress address, int port):构造数据报包,用来将长度为length的包发送到指定主机上的指定端口号。
    • 常用方法
      • public int getLength():获得发送端实际发送的字节数或接收端世界接收的字节数
      • public int getPort():获得发送端或接收端端口号
  2. 发送数据包类DatagramSocket

    • 作用:用来发送和接收数据包对象
    • 构造方法
      • DatagramSocket():构造数据报套接字并将其绑定到本地主机上任何可用的端口。
      • DatagramSocket(int port):创建数据包套接字并将其绑定到本地主机上指定端口。
    • 常用方法
      • public void send(DatagramPacket p):从此套接字发送数据报包
      • public void receive(DatagramPacket p):从此套接字接收数据报包
      • public void close():关闭此数据报套接字
  3. InetAddress类(无构造方法)

    • 作用:代表一个IP地址
    • 静态方法
      • public static InetAddress getLocalHost():返回本地主机
      • public static InetAddress getByName():在给定主机名的情况下确定主机的 IP 地址。
    • 普通方法
      • public String getHostName(): 获取此 IP 地址的主机名。
      • public String getHostAddress():返回 IP 地址字符串(以文本表现形式)

六.基于UDP的Socket通信

  1. 步骤分析

    • 服务器端先启动,创建DatagramSocket对象,监听端口,用于接收
    • 服务器端创建DatagramPacket对象,打包用于接收的数据包
    • 服务器阻塞等待接收
    • 客户端启动,创建DatagramSocket对象,监听端口,用于接收
    • 客户端创建DatagramPacket对象,打包用于发送的数据包
    • 客户端发送数据,服务端接收
    • 服务端接收数据后,创建DatagramPacket对象,打包用于发送的数据包,发送数据
    • 客户端创建DatagramPacket对象,打包用于接收的数据包,阻塞等待接收
    • 客户端接收服务端数据,断开连接,释放资源

服务器端:

public class UdpEchoServer {
    // 需要先定义一个 socket 对象.
    // 通过网络通信, 必须要使用 socket 对象.
    private DatagramSocket socket = null;

    // 绑定一个端口, 不一定能成功!!
    // 如果某个端口已经被别的进程占用了, 此时这里的绑定操作就会出错.
    // 同一个主机上, 一个端口, 同一时刻, 只能被一个进程绑定.
    public UdpEchoServer(int port) throws 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);
            //    食堂大妈给饭盒里盛饭. (饭从网卡上来的)
            socket.receive(requestPacket);
            //    为了方便处理这个请求, 把数据包转成 String
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            // 2. 根据请求计算响应(此处省略这个步骤)
            String response = process(request);
            // 3. 把响应结果写回到客户端
            //    根据 response 字符串, 构造一个 DatagramPacket .
            //    和请求 packet 不同, 此处构造响应的时候, 需要指定这个包要发给谁.
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    // requestPacket 是从客户端这里收来的. getSocketAddress 就会得到客户端的 ip 和 端口
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }

    // 这个方法希望是根据请求计算响应.
    // 由于咱们写的是个 回显 程序. 请求是啥, 响应就是啥!!
    // 如果后续写个别的服务器, 不再回显了, 而是有具体的业务了, 就可以修改 process 方法,
    // 根据需要来重新构造响应.
    // 之所以单独列成一个方法, 就是想让同学们知道, 这是一个服务器中的关键环节!!!
    public String process(String request) {
        return request;
    }

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

UdpEchoServer(UDP 服务器):

a. 首先,创建一个 DatagramSocket 对象,并绑定到指定的端口。这个端口用于服务器与客户端之间的通信。

b. 在服务器的主循环中,首先创建一个空的 DatagramPacket 对象,用于接收客户端发来的请求数据。

c. 调用 socket.receive(requestPacket) 方法接收客户端发来的数据包。

d. 将收到的数据包中的数据转换成字符串形式,并调用 process() 方法生成响应。在这个例子中,响应就是原请求。

e. 创建一个新的 DatagramPacket 对象,包含响应数据和客户端的地址信息。

f. 使用 socket.send(responsePacket) 方法将响应数据包发送回客户端。

g. 打印请求和响应信息。

 客户端:

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

    // 客户端启动, 需要知道服务器在哪里!!
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        // 对于客户端来说, 不需要显示关联端口.
        // 不代表没有端口, 而是系统自动分配了个空闲的端口.
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }

    public void start() throws IOException {
        // 通过这个客户端可以多次和服务器进行交互.
        Scanner scanner = new Scanner(System.in);
        while (true) {
            // 1. 先从控制台, 读取一个字符串过来
            //    先打印一个提示符, 提示用户要输入内容
            System.out.print("-> ");
            String request = scanner.next();
            // 2. 把字符串构造成 UDP packet, 并进行发送.
            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);
            // 4. 把响应数据转换成 String 显示出来.
            //这行代码将接收到的字节流数据按照指定的编码格式转换成字符串,方便我们查看和处理数据。
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.printf("req: %s, resp: %s\n", request, response);
        }
    }

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

UdpEchoClient(UDP 客户端):

a. 创建一个 DatagramSocket 对象,不需要显式地关联端口,系统会自动分配一个空闲的端口。

b. 在客户端的主循环中,从控制台读取用户输入的字符串作为请求。

c. 创建一个 DatagramPacket 对象,包含请求数据、服务器的 IP 地址和端口信息。

d. 调用 socket.send(requestPacket) 方法将请求数据包发送给服务器。

e. 创建一个空的 DatagramPacket 对象,用于接收服务器返回的响应数据。

f. 调用 socket.receive(responsePacket) 方法接收服务器发来的响应数据包。

g. 将收到的响应数据包中的数据转换成字符串形式,并打印请求和响应信息。

总结:这两段代码实现了一个简单的 UDP 回显服务器和客户端。客户端将用户输入的请求数据通过 UDP 协议发送给服务器,服务器接收到请求后原样返回响应,客户端接收响应并打印信息。整个过程使用无连接的 UDP 协议进行通信。

七. TCP和UCP的缓冲区

1.TCP的缓冲区

创建一个TCP的socket,同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;
调用write时,数据会先写入发送缓冲区中;
如果发送的字节数太长,会被拆分成多个TCP的数据包发出;
如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适
的时机发送出去;
接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区;
然后应用程序可以调用read从接收缓冲区拿数据;
另一方面,TCP的另一个连接,既有发送缓冲区,也有接收缓冲区,那么对于这一个连接,既
可以读数据,也可以写数据。这个概念叫做 全双工

2.UDP的缓冲区

UDP只有接收缓冲区,没有发送缓冲区:
UDP没有真正意义上的 发送缓冲区。发送的数据会直接交给内核,由内核将数据传给网络层协议
进行后续的传输动作;
UDP具有接收缓冲区,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一
致;如果缓冲区满了,再到达的UDP数据就会被丢弃;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(链表)移除链表元素(双指针法)

文章目录前言:问题描述:解题思路(双指针法):代码实现:总结:前言: 此篇是针对链表的经典练习题。 问题描述: 给你一个链表的头节点 head 和一个整数 val ,请…

Js:apply/call/bind、作用域/闭包、this指向(普通,箭头,JS/Vue的this)

目录1、apply/call/bind2、作用域、作用域链和闭包核心1、预处理(解析阶段)——JS执行“代码段”之前2、生成执行上下文环境——对代码段(全局/函数体)进行处理3、执行上下文环境小结4、多个执行上下文环境5、作用域6、作用域和执行上下文7、从【自由变量…

小米万兆路由器里的 Docker 安装 Gitea

小米万兆路由器里的 Docker 安装 Gitea准备工作创建存储查看Docker Hub镜像信息拉取 gitea 镜像和运行容器配置通过 ssh 访问(Optional)其他小米2022年12月份发布了万兆路由器,里面可以使用Docker。 今天尝试在小米的万兆路由器里安装Gitea。 准备工作 先将一块US…

Java企业级开发学习笔记(2.1)MyBatis实现简单查询

该文章主要为完成实训任务,详细实现过程及结果见【http://t.csdn.cn/zi0wB】 文章目录零、创建数据库与表一、基于配置文件方式使用MyBatis基本使用1.1 创建Maven项目 - MyBatisDemo1.2 在pom文件里添加相应的依赖1.3 创建与用户表对应的用户实体类 - User1.4 创建用…

没有他们,人工智能只能死翘翘

我过去写过一篇文章《很多所谓伟大的贡献,其实都是狗屎运》,今天我也写写人工智能。(1)人才深度神经网络如果不从明斯基和罗森布拉特说起,那就应该可以从1965年Ivakhnenko发明前馈神经网络说起。但关键里程碑是出自Rum…

SpringBoot2核心功能 --- 原理解析

一、Profile功能 为了方便多环境适配,springboot简化了profile功能。 1.1、application-profile功能 默认配置文件 application.yaml;任何时候都会加载指定环境配置文件 application-{env}.yaml激活指定环境配置文件激活 命令行激活:java -…

【快乐手撕LeetCode题解系列】—— 环形链表 II

【快乐手撕LeetCode题解系列】—— 环形链表 II😎前言🙌环形链表 II🙌画图分析:😍思路分析:😍源代码分享:😍总结撒花💞😎博客昵称:博客…

STM32与Python上位机通过USB虚拟串口通信

文章目录前言1. 查看原理图2. 新建工程3.添加代码与烧录4. python代码编写总结问题解决思路前言 在详细阅读广大网友的教程之后,我对STM32和Python通过USB通信的流程烂熟于心。 尝试用ST公司的NUCLEO-L476RG板子进行简单的回环通信测试,发现还是存在网上…

Linux·异步IO编程框架

hi,大家好,今天分享一篇Linux异步IO编程框架文章,对比IO复用的epoll框架,到底性能提高多少?让我们看一看。 译者序 本文组合翻译了以下两篇文章的干货部分,作为 io_uring 相关的入门参考: Ho…

【RocketMQ】顺序消息实现原理

全局有序 在RocketMQ中,如果使消息全局有序,可以为Topic设置一个消息队列,使用一个生产者单线程发送数据,消费者端也使用单线程进行消费,从而保证消息的全局有序,但是这种方式效率低,一般不使用…

Web 攻防之业务安全:接口未授权访问/调用测试(敏感信息泄露)

Web 攻防之业务安全:接口未授权访问/调用测试 业务安全是指保护业务系统免受安全威胁的措施或手段。广义的业务安全应包括业务运行的软硬件平台(操作系统、数据库,中间件等)、业务系统自身(软件或设备)、业…

ViT/vit/VIT详解

参考: Vision Transformer详解: https://blog.csdn.net/qq_37541097/article/details/118242600 目录: x.1 (论文中)模型理解x.2 代码理解 建议阅读时间:10min x.1 模型理解 ViT是发表在ICLR2021上的一篇文章,通过将图片分割…

Java并发控制 学习笔记1

一、并发控制的方法 1、悲观锁:常用的互斥锁都属于悲观锁,一个线程访问共享资源时其他线程不能访问。 2、乐观锁:允许同时访问共享数据,只有在提交时利用如版本号检查是否有冲突,应用github。 3、什么时候用乐观锁、什…

携程平台增长部总经理王绩强:原生互联网企业正在经历一场数字升级丨数据猿专访...

‍数据智能产业创新服务媒体——聚焦数智 改变商业以大数据和人工智能为核心,众多新兴技术开始赋能数字营销。于是,智能营销已然从工具化走向了业务化。如今,数字化营销已经成为了企业数字化转型中的重要一环。相较于传统营销逻辑&#xff0…

新版新款影视直播粉红色UI的麻豆CMS源码/带教程/支付已接

基于苹果CMS v10影视系统框架开发的前端模板,带会员中心,可设置试看付费观看等功能。 经过测试及修复,这套源码功能还是很强大的,可以设置一键采集,并且支付我们给他接到了易支付,拓展性强,基本…

【压测】通过Jemeter进行压力测试(超详细)

文章目录背景一、前言二、关于JMeter三、准备工作四、创建测试4.1、创建线程组4.2、配置元件4.3、构造HTTP请求4.4、添加HTTP请求头4.5、添加断言4.6、添加察看结果树4.7、添加Summary Report4.8、测试计划创建完成五、执行测试计划总结背景 通过SpringCloudGateway整合Nacos进…

如何下载ChatGPT-ChatGPT如何写作

CHATGPT能否改一下文章 ChatGPT 作为一种自然语言处理技术,生成的文章可能存在表达不够准确或文风不符合要求等问题。在这种情况下,可以使用编辑和修改来改变输出的文章,使其符合特定的要求和期望。 具体来说,可以采用以下步骤对…

超越竞争对手:利用Facebook A/B测试优化广告效果!

随着社交媒体广告的普及,Facebook已经成为了许多公司推广业务的重要平台。但是,在Facebook上发布广告并不意味着成功,这也让许多公司开始关注如何优化广告效果。 在这篇文章中,我将介绍如何使用A/B测试来优化Facebook广告&#x…

纳米软件关于集成电路测试的分类介绍

集成电路测试可以按照测试目的、测试内容、按照器件开发和制造阶段分类。参照需要达到的测试目的对集成电路测试进行分类,可以分为:验证测试、制造测试、老化测试、入厂测试等。按照测试所涉及内容,集成电路测试可分为:参数测试、功能测试、结构测试等。…

2023/4/4总结

题解: Problem ​​​​​​ A - Codeforces 1.这道题目我们需要判断。 2.如果是奇数,亦或出来的总值不为0,那么每一个数字再去亦或任何一个数字,都不会为0。 3.如果是偶数并且亦或总值为0,那么我们亦或的总值不满…