【计算机网络】网络编程 Socket

news2024/11/16 3:48:33

目录

1.TCP和UDP的区别

2.基于UDP的 Socket API

总结

3.基于TCP的Socket API

服务器程序的问题


网络编程的目的:通过网络,让不同主机之间能够进行通信。

在进行网络编程的时候,需要操作系统提供一组API,也就是Socket API,才能完成编程。Socket API可以认为是应用层和传输层之间交互的路径。

Socket:也被称为套接字,就是网络中不同主机上的应用程序之间进行双向通信的端点的抽象。其本质上是一种特殊的文件与普通文件不同的是,普通文件都是在同一台计算机上,两个进程之间传输数据。而Socket可以实现在不同计算机之间传输数据,也就是网络传输数据。比如说打开QQ,打开一个网页,这些都是socket来实现通信的,而网络通信又必须遵守的两个协议,tcp/ip协议和udp协议,Socket里面已经封装好了这两个协议,我们直接拿来用即可。Socket就属于是把"网卡"这个设备给抽象成文件了。往Socket文件中写数据,就相当于通过网卡发送数据;从Socket文件读数据,就相当于通过网卡接收数据。

传输层提供的网络协议主要有两个:TCP和UDP。


1.TCP和UDP的区别

  1. TCP是有连接的,UDP是无连接的。
  2. TCP是可靠传输的,UDP是不可靠传输的。
  3. TCP是面向字节流的,UDP是面向数据报的。
  4. TCP和UDP都是全双工的。

详细来说这四个区别。

1. TCP是有连接的,UDP是无连接的。

  • 这里的连接只是抽象的说法,就是连接双方各自保存了对方的信息。两台计算机连接就是双方彼此保存了对方的关键信息。TCP想要通信,就要先建立连接,如果一方请求建立连接,而另一方拒绝连接,则无法连接,也就无法通信。而UDP想要通信,直接发送数据即可,不需要关心对方是否同意,UDP自身也不会保存对方的信息。

2. TCP是可靠传输的,UDP是不可靠传输的。

举个例子:A给B发送消息,发送成功还是失败,若A这边都能及时知道,那这就是可靠传输,否则就是不可靠传输。但是要想可靠传输,也得付出一些代价:

  • 可靠传输的机制更复杂。
  • 可靠传输的传输效率会降低。

3. TCP是面向字节流的,UDP是面向数据报的。

  • 即TCP是以字节为基本单位来进行传输的。
  • UDP是以数据报为基本单位进行传输的。
  • 网络通信数据的基本单位有多种说法:数据报(Datagram),数据包(Packet),数据帧(Frame),数据段(Segment)。

4. TCP和UDP都是全双工的。

一个信道,允许双向通信,就是全双工。

一个信道,只能单向通信,就是半双工。

双向通信的原因:与网线有关,一根网线里通常有8根网线,4个为一组,每一组又是单向传输的,这样这一组负责数据传过来,另一组负责数据传过去。就构成了双向通信。


2.基于UDP的 Socket API

在Java中也提供了一组API,用来操作Socket,具体使用DatagramSocket这个类操作。其常用方法有接收数据receive()方法,发送数据send()方法,关闭文件close()方法。DatagramPacket这个类用来创建数据包每次发送数据或者是接收数据都要以数据包的形式传递且发送数据时数据包还要加上发送目的地(IP,端口等)。接收数据时还要提前创建好空的数据报。

为啥要以数据包的形式接收和发送?

因为UDP是面向数据包的。是以数据报为基本单位。


接下来我们实现一个简单的基于UDP的客户端/服务器通信的程序。(回显服务器)

服务器程序

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

//服务器程序
public class  MyServer {
    private DatagramSocket socket = null;
    //port 端口号 服务器需要手动指定端口号
    public MyServer(int port) throws SocketException {
        //折磨写就是手动指定端口
        socket = new DatagramSocket(port);
        //这么写就是让系统自动分配端口
        //socket = new DatagramSocket();
    }
    //服务器启动方法start
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true) {
            // 1.读取请求并解析
            //创建一个空的数据报,用来存储服务器接收客户端传来的请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            //讲数据报传入socket中
            socket.receive(requestPacket);
            //将数据报中存储的客户端的请求数据从字节流转成字符串
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            // 2.根据请求计算响应
            String response = process(request);
            //将计算后的响应数据一个传入新创建的数据报 参数有 字节数组 字节数组长度 数据报的发送地址
            DatagramPacket responsePacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    requestPacket.getSocketAddress());
            // 3.把响应写回给客户端 以数据包形式
            socket.send(responsePacket);
            // 4.打印日志
            System.out.printf("[%s:%d] request=%s response=%s\n",requestPacket.getAddress().toString(),
                    responsePacket.getPort(),request,response);
        }
    }

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

    public static void main(String[] args) throws IOException {
        //设置服务器的端口号为9090
        MyServer server = new MyServer(9090);
        server.start();
    }

}

客户端程序 

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 MyClient {
    private DatagramSocket socket = null;
    //服务器IP
    private String serverIp =null;
    //服务器端口
    private int serverPort = 0;

    //构造方法 参数为服务器IP和服务器端口号
    public MyClient(String IP,int port) throws SocketException {
        //客户端端口号不需要手动指定 由系统自动分配
        socket = new DatagramSocket();
        this.serverIp = IP;
        this.serverPort = port;
    }
    //客户端启动程序
    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner sc = new Scanner(System.in);
        while(true) {
            // 1.将客户端的请求数据发给服务器
            System.out.print("->");
            String request = sc.next();
            //创建数据报 将请求数据以字节流形式装入包中
            //因为是充当发送的数据报 所以数据包中要有 数据+发送目的地
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIp),serverPort);
            //将数据报发送给服务器
            socket.send(requestPacket);
            // 2.接收服务器响应回的数据
            //再创建一个空的数据包接收服务器返回的数据
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            //接收服务器返回的响应
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.println(response);
        }
    }
    public static void main(String[] args) throws IOException {
        //参数为服务器IP地址和端口号
        MyClient client = new MyClient("127.0.0.1",9090);
        client.start();
    }
}

总结

       基于UCP协议的话:服务器和客户端之间的来回receive和send都是以数据包形式传递数据 所以要经常创建数据包。数据包中可包含数据 发送的目的地。充当发送的数据包中是要有发送的地址的,充当接收的数据包要预先创建一个空的数据包。这就是两种方式下数据包的不同的用法。


上述代码中的几个细节:

1.既然说Socket是文件,为啥使用完后不进行close操作呢?不怕文件资源泄露吗?

  • 原因是Socket在整个程序的运行过程中都是需要使用的,并且也没有去频繁地打开文件。我们都知道打开文件,就会占用一个文件描述符表,造成文件资源泄露的原因也是因为文件描述符表满了。而文件描述符表又是在PCB上的,当整个程序运行结束,进程结束了,PCB就会还给操作系统,被销毁,那么文件描述符也就会被回收。

文件资源泄露的原因:在一个程序中,频繁地打开文件而不去关闭。会使文件描述符表满。


2.服务器和客户端的端口号都需要手动添加吗?

  • 服务器端口号需要手动指定添加,而客户端端口号不需要指定。
  • 因为服务器是在程序员手里的,是可控的,而不同的用户,其电脑上下载的程序,占用的端口号都不同,如果给客户端去指定端口号可能会引发冲突。而最好的做法是让系统去自动分配一个端口号,这样就可以保证分配的这个端口号一定是不冲突的。

3.这段代码服务器和客户端各自的工作流程?

  1. 服务器先启动,启动之后,就会进入while循环,执行到receive这里,若是未收到客户端的请求,就会一直阻塞,直到客户端有请求发送过来,才会继续往下执行。
  2. 客户端后启动,也先进入while循环,执行.next(),等待用户输入请求,这里也会阻塞等待。当用户输入请求后,以字节形式封装进数据报,将请求数据包发送(send)给服务器。然后继续往下执行到receive(接收)处,等待服务器响应。
  3. 服务器接收(receive)到请求后,将接收到的数据报里的数据又从字节流转成(解析成)字符串,再执行process操作来处理请求,处理完成后,先将响应数据转成字节数组,再封装入数据报,再返回给客户端。
  4. 客户端收到响应后,从receive处向下执行,再次将数据包中的数据解析成字符串并进行打印。
  5. 服务器执行完一次循环后,又执行到receive这里,等待客户端的请求。客户端执行完一次循环后,又执行到next()这里,等待用户输入请求。也就是双双进入阻塞。

4.为啥接收数据的时候需要提前手动创建一个有大小的数据报?

  • 因为DatagramPacket不能自己分配内存。因此需要提前手动创建好一个字节数组和长度。

基于上述代码再写一个简单的翻译器程序(继承)


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

public class udpTranServer extends udpServer{
    private Map<String,String> map = new HashMap<>();
    public udpTranServer(int port) throws SocketException {
        super(port);
        map.put("cat","猫");
        map.put("dog","狗");
        map.put("tiger","老虎");
        //......
    }

    //重写process方法 服务器处理请求
    @Override
    public String process(String request) {
        //英译汉
        String response = map.getOrDefault(request,"没有这个单词!");
        return response;
    }

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

3.基于TCP的Socket API

有两个关键的类:

ServerSocket:只给服务器使用,用来绑定端口号。

Socket:既给服务器用,也给客户端用,用来接收(receive)和发送(send)数据(字节流)。


常用的四个方法:

  • 得到对端的网络地址和端口号

  • 得到本地的网络地址和端口号 

 

 


由于TCP是面向字节流的,以字节为基本单位。所以传输过程中就会用到InputStream和OutputStream流进行接收和发送。通过InputStream进行read操作,就是接收,通过OutputStream进行write操作,就是发送。

服务器

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 tcpServer {

    private ServerSocket serverSocket = null;
    public tcpServer(int port) throws IOException {
        //服务器手动指定端口号
        serverSocket = new ServerSocket(port);
    }

    //这个方法用来启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true) {
            //服务器接收请求
            Socket requestSocket = serverSocket.accept();
            //先需要建立连接 细节流程是由内核完成的 只需要调用即可
            processConnection(requestSocket);
        }
    }

    private void processConnection(Socket requestSocket) throws IOException {
        //打印日志
        System.out.printf("[%s:%d] 上线啦! %s\n",requestSocket.getInetAddress(),requestSocket.getPort());
        //处理请求
        try(InputStream is = requestSocket.getInputStream();
            OutputStream os = requestSocket.getOutputStream()) {
            Scanner sc = new Scanner(is);
            PrintWriter writer = new PrintWriter(os);
            while(true) {
                // 字节流转字符流
                // 先读 Scanner
                if (!sc.hasNext()) {
                    System.out.printf("[%s:%d] 下线啦!\n",requestSocket.getInetAddress(),requestSocket.getPort());

                    break;
                }
                String request = sc.next();
                // 处理请求
                String response = process(request);
                //打印日志
                System.out.printf("[%s:%d] req=%s res=%s\n",requestSocket.getInetAddress(),requestSocket.getPort(),request,response);

                // 将请求处理结果返回给客户端
                // 再写 PrintWriter 或者将 字符串转成字节数组
                writer.println(response);
                // 字符串转成字节数组
                // os.write(response.getBytes());
                // 刷新缓冲
                writer.flush();
                // os.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            requestSocket.close();
        }

    }

    private String process(String request) {
        //这里负责处理请求并返回响应
        return request;
    }

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

客户端

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 tcpClient {
    Socket socket = null;
    //需要明确访问服务器的IP地址和端口号
    public tcpClient(String serverIp,int serverPort) throws IOException {
        //具体链接的细节,不需要我们手动干预,内核自动负责
        //当我们new这个对象的时候,操作系统就进行三次握手 具体细节,完成建立的细节
        socket = new Socket(serverIp,serverPort);
    }
    public void start() {
        System.out.println("客户端启动!");
        Scanner sc = new Scanner(System.in);
        try(InputStream is = socket.getInputStream();
            OutputStream os = socket.getOutputStream()) {
            Scanner res = new Scanner(is);
            PrintWriter writer = new PrintWriter(os);
            while(true) {
                System.out.print("->");
                String request = sc.next();
                //将请求以字节流形式发送给服务器
                //os.write(request.getBytes());
                writer.println(request);
                //刷新缓冲区
                writer.flush();
                //os.flush();
                //接收服务器传回来的响应
                //传回来的是字节流 进一步要转成字符流
                String response = res.next();
                System.out.println(response);

            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

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

服务器程序的问题

问题一:为啥光Socket对象要进行close操作而ServerSocket不用close,为啥这个会资源泄露?

原因是这个对象在while循坏中,每次有一个客户端来请求都会创建一个,这个Socket对象也就会占据文件描述符的位置,久而久之,不及时关闭就会造成文件资源泄露,而同一个代码中ServerSocket不用close,是因为它的对象只有一个,且生命周期贯穿整个程序。

但是我们不是加了try()语句吗,这种语句形式不是会自动关闭的吗?

原因是这只是关闭了Socket对象自带的流对象,本身本体Socket对象并没有被关闭。


问题二:上面这种代码如果启动多个客户端会有什么问题,如何解决?

虽然客户端是多个,但其实进线程只有一个。 最先启动的客户端是可以正常交互的。而后面启动的这些客户端没有响应。并且当第一个客户端结束后,第二个又可以正常运行,并且会将之前积压的请求由服务器全部正常处理。以此类推,即同一时间只能正常运行一个,仿佛是其他客户端都在等待自己的前一个客户端结束后才活过来。这是与我们服务器的代码逻辑有关,在代码中,我们有两层嵌套while(true)循环,这样第一个客户端上线后,进程就会阻塞在里面的while循环的判断语句hasNext这块,还是上面所说的虽然客户端是多个,但其实线程只有一个。所以只有这个用户下线后,里面的while循环才能跳出来,继续执行。


解决办法:使用多线程并发执行。 

1. 把连接操作放在Thread里

  • 这样会有效,但是这样每个客户进来,都会创建一个线程,如果客户端多了,并且频繁地建立连接/断开连接,就会频繁地创建和销毁线程,开销就会很大。还可以采用线程池(2)。

2. 采用线程池

线程池的方式虽然可以降低线程的频繁创建与销毁的开销。但如果同一时刻大量用户进行访问,线程池也要同时创建多个线程,也会招架不住。还可以采用协程(3)或IO多路复用(4)。


3.协程


4.IO多路复用/IO多路转接

IO多路复用/IO多路转接:用一个线程可以同时处理多个客户端的Socket。在Java中是NIO,其底层就是IO多路复用。


代码如下:

只需要改服务器程序。

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 tcpServer {

    private ServerSocket serverSocket = null;
    //使用线程池 保证多个客户端正常运行
    ExecutorService service = Executors.newCachedThreadPool();
    public tcpServer(int port) throws IOException {
        //服务器手动指定端口号
        serverSocket = new ServerSocket(port);
    }

    //这个方法用来启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true) {
            //服务器接收请求
            Socket requestSocket = serverSocket.accept();
            //先需要建立连接 细节流程是由内核完成的 只需要调用即可

            //第一种 最简单的多线程处理
            /*
            Thread thread = new Thread(() -> {
                try {
                    processConnection(requestSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            thread.start();

             */
            //第二种 线程池
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(requestSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            //协程和IO多路复用以后实现
        }
    }

    private void processConnection(Socket requestSocket) throws IOException {
        //打印日志
        System.out.printf("[%s:%d] 上线啦! \n",requestSocket.getInetAddress(),requestSocket.getPort());
        //处理请求
        try(InputStream is = requestSocket.getInputStream();
            OutputStream os = requestSocket.getOutputStream()) {
            Scanner sc = new Scanner(is);
            PrintWriter writer = new PrintWriter(os);
            while(true) {
                // 字节流转字符流
                // 先读 Scanner
                if (!sc.hasNext()) {
                    System.out.printf("[%s:%d] 下线啦!\n",requestSocket.getInetAddress(),requestSocket.getPort());

                    break;
                }
                String request = sc.nextLine();
                // 处理请求
                String response = process(request);
                //打印日志
                System.out.printf("[%s:%d] req=%s res=%s\n",requestSocket.getInetAddress(),requestSocket.getPort(),request,response);

                // 将请求处理结果返回给客户端
                // 再写 PrintWriter 或者将 字符串转成字节数组
                writer.println(response);
                // 字符串转成字节数组
                // os.write(response.getBytes());
                // 刷新缓冲
                writer.flush();
                // os.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            requestSocket.close();
        }

    }

    private String process(String request) {
        //这里负责处理请求并返回响应
        return request;
    }

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

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

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

相关文章

用Node.js开发基于稳定扩散的AI应用

在本文中&#xff0c;我们将介绍如何构建一个 Web 应用程序&#xff0c;该应用程序使用 ChatGPT 和 Stable Diffusion 为你提供的任何网站描述生成徽标和合适的域名。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 1、介绍 人工智能正在接管世界。 这些技术每天都在震…

基于GRU的 电影评论情感分析 - python 深度学习 情感分类 计算机竞赛

文章目录 1 前言1.1 项目介绍 2 情感分类介绍3 数据集4 实现4.1 数据预处理4.2 构建网络4.3 训练模型4.4 模型评估4.5 模型预测 5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于GRU的 电影评论情感分析 该项目较为新颖&#xff0c;适合作为竞…

Hadoop3教程(二十六):(生产调优篇)NameNode核心参数配置与回收站的启用

文章目录 &#xff08;143&#xff09;NameNode内存配置&#xff08;144&#xff09;NN心跳并发配置&#xff08;145&#xff09;开启回收站参考文献 &#xff08;143&#xff09;NameNode内存配置 每个文件块&#xff08;的元数据等&#xff09;在内存中大概 占用150byte&…

Android 12.0 Launcher3定制化功能之抽屉式(双层)app列表排序功能实现

1.概述 在12.0的系统开发中,在定制Launcher3的开发中,对于抽屉式即双层桌面的workspace的app列表排序的功能,也是常有的需求,把常用的app图标放在前面,其他的可以放在列表后面做个整体的排序,这就需要了解app列表排序的流程,然后根据需求来实现功能 如图: 2.Launcher3 …

并发编程-延时队列DelayQueue

数据结构学习网站&#xff1a; Data Structure Visualization 思维导图 DelayQueue &#xff08;延时队列&#xff09; DelayQueue 是一个支持延时获取元素的阻塞队列 &#xff0c; 内部采用优先队列 PriorityQueue 存储元素&#xff0c;同时元素必须实现 Delayed 接口&#x…

Elasticsearch 8.X 分词插件版本更新不及时解决方案

1、关于 Elasticsearch 8.X IK 分词插件相关问题 球友在 ElasticSearch 版本选型问题中提及&#xff1a;如果要使用ik插件&#xff0c;是不是就使用目前最新的IK对应elasticsearch的版本“8.8.2”&#xff1f; https://github.com/medcl/elasticsearch-analysis-ik/releases/ta…

C++入门3+类和对象上

C入门3类和对象上 一.内联函数1.宏函数的缺点2.宏函数的优点3.内联函数的语法4.内联函数的优缺点5.内联函数的使用条件6.内联函数的展开7.内联函数的一大注意事项1.内联函数声明跟定义分离2.内联函数声明跟定义分离的"奇怪"现象 二.C11对于C语法的补充1.auto关键字1.…

【Nginx34】Nginx学习:安全链接、范围分片以及请求分流模块

Nginx学习&#xff1a;安全链接、范围分片以及请求分流模块 又迎来新的模块了&#xff0c;今天的内容不多&#xff0c;但我们都进行了详细的测试&#xff0c;所以可能看起来会多一点哦。这三个模块之前也从来都没用过&#xff0c;但是通过学习之后发现&#xff0c;貌似还都挺有…

python模块之feapder 爬虫框架

一、简介 官网&#xff1a;https://feapder.com/#/ feapder是一款上手简单&#xff0c;功能强大的Python爬虫框架&#xff0c;内置AirSpider、Spider、TaskSpider、BatchSpider四种爬虫解决不同场景的需求&#xff0c;但像任何工具一样&#xff0c;它也有其优点和缺点。以下是…

如何利用考培系统进行个性化学习和评估

考培系统作为一种现代化的学习和评估工具&#xff0c;可以为学生提供个性化的学习和评估服务。它利用先进的技术和算法&#xff0c;根据学生的学习情况和需求&#xff0c;为其量身定制学习计划&#xff0c;并提供相应的评估反馈。 1. 个性化学习 考培系统通过分析学生的学习情…

QML(25)——文本输入框组件的区别(TextField TextInput TextArea TextEdit)

目录 效果展示适用场景文本组件TextLabelText和Label的区别 单行文本输入框TextFieldTextInputTextField 和 TextInput的区别 多行文本输入框TextAreaTextArea 和 TextEdit 的区别 效果展示 适用场景 场景组件属性短文本Text长文本 末尾省略Textelide: Text.ElideRight文本设置…

通用FIFO设计深度8宽度64,verilog仿真,源码和视频

名称&#xff1a;通用FIFO设计深度8宽度64&#xff0c;verilog仿真 软件&#xff1a;Quartus 语言&#xff1a;verilog 本代码为FIFO通用代码&#xff0c;其他深度和位宽可简单修改以下参数得到 reg [63:0] ram [7:0];//RAM。深度8&#xff0c;宽度64 代码功能&#xff1a…

ArmSoM-RK3588编解码之mpp解码demo解析:mpi_dec_test

1. 简介 [RK3588从入门到精通] 专栏总目录 mpi_dec_test 是rockchip官方解码 demo 本篇文章进行mpi_dec_test 的代码解析&#xff0c;解码流程解析 2. 环境介绍 硬件环境&#xff1a; ArmSoM-W3 RK3588开发板 软件版本&#xff1a; OS&#xff1a;ArmSoM-W3 Debian11 3.…

低代码助力软件开发

低代码开发工具正在日益变得强大&#xff0c;它正不断弥合着前后端开发之间的差距。对于后端来说&#xff0c;基于低代码平台开发应用时&#xff0c;完全不用担心前端的打包、部署等问题&#xff0c;也不用学习各种框架&#xff08;Vue、React、Angular等等&#xff09;&#x…

GO 语言如何用好变长参数?

函数重载 对于函数重载相信编码过的 xdm 肯定不会陌生&#xff0c;函数重载就是在同一个作用域内定义多个具有相同名称但参数列表不同的函数 此处的参数列表不同&#xff0c;可以是参数的类型不同&#xff0c;参数的个数不同 那么我们一起分别来看看 C 语言&#xff0c;C 语…

物联网专业前景怎么样?

物联网专业前景怎么样&#xff1f; 物联网专业在当今技术发展迅速的背景下具有广阔的前景。以下是物联网专业的一些优势和就业前景&#xff1a; 1.市场需求大&#xff1a;物联网作为人工智能、云计算和大数据等技术的结合&#xff0c;已经成为许多行业的核心需求。各行各业都需…

【智能指针】

目录&#xff1a; 前言智能指针&#xff08;一&#xff09;智能指针初始了解内存泄漏1. 内存泄漏分类2. 如何检测内存泄漏3. 如何避免内存泄漏使用智能指针之前&#xff0c;异常安全的处理 &#xff08;二&#xff09;智能指针实现既原理智能指针RAII使用智能指针之后&#xff…

060:mapboxGL点击某处,通过flyTo,以动画的形式聚焦到此点

第060个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中点击某处,通过flyto,以动画的形式聚焦到此点。这里用到了flyTo的方法,里面可以设置bearing,zoom,pitch等众多的属性内容。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示…

数据结构与算法-(10)---列表(List)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

Linux 最大可以打开多少文件描述符?

Linux 最大可以打开多少文件描述符&#xff1f; 在日常开发中&#xff0c;对文件的操作可谓是再寻常不过的意见事情。那么你是否有这样一个疑问&#xff0c; 我最多可以打开多少个文件呢&#xff1f; 在Linux系统中&#xff0c;当某个程序打开文件时&#xff0c;内核返回相应…