简单实现基于UDP与TCP的回显服务器

news2024/11/18 17:36:37

目录

  • 前言
  • UDP 版的回显服务器
    • 需要用到的 api
    • 服务端
    • 客户端
      • UDP 版本的字典客户端和字典服务器
  • TCP 版的回显服务器
    • 需要用到的 api
    • 服务器
    • 客户端
      • 对服务器进行改进(使用线程池)
      • TCP 版本的字典客户端和字典服务器

前言

我们写网络程序, 主要编写的是应用层代码.
真正要发送这个数据, 还需要上层协议调用下层协议, 也就是应用层调用传输层.
传输层给应用层提供了一组 api, 统称为 socket api, 系统给程序提供的 api 是C 风格的, JDK 针对这些 api 进行封装, 封装成 Java 风格的 api.

提供的 socket api 主要是这两组 :

  1. 基于 UDP 的 api
  2. 基于 TCP 的 api

因为 UDP 与 TCP 的协议差别很大, 所以这两组 api 差别也很大.

那这两个协议都有啥特点呢?

UDP :

  1. 无连接 (使用 UDP 的双方不需要刻意保存对端的相关信息)
  2. 不可靠传输 (消息发送完了就行, 不关注结果)
  3. 面向数据报 (以一个 UDP 数据报为基本单位)
  4. 全双工 (一条路径, 双向通信)

TCP :

  1. 有连接 (使用 TCP 的双方要刻意保存对端的相关信息)
  2. 可靠传输 (发送消息后, 知道对方是否接收到)
  3. 面向字节流 (以字节为传输的基本单位, 读写方式非常灵活)
  4. 全双工 (一条路径, 双向通信)

UDP 版的回显服务器

需要用到的 api

  1. DatagramSocket API

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

DatagramSocket()

创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)

DatagramSocket(intport)

创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)

主要用到的方法 :

void receive(DatagramPacket p)

从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)

void send(DatagramPacketp)

从此套接字发送数据报包(不会阻塞等待,直接发送)

void close()

关闭此数据报套接字

  1. DatagramPacket API

DatagramPacket是UDP Socket发送和接收的数据报.
主要用到的构造方法 :

DatagramPacket(byte[] buf, int length)

构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数 length)

DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)

构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从offset到指定长度(第三个参数length)。address指定目的主机的IP和端口号

主要用到的方法 :

InetAddress getAddress()

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

int getPort()

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

byte[] getData()

获取数据报中的数据

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

服务端

public class UdpEchoServer {
    //首先定义一个 socket 对象, 通过 socket 对象来发送读取信息
    private DatagramSocket socket = null;

    //绑定一个端口, 如果绑定的端口被别的进程占用了, 这里就会报错.
    //同一个主机上, 一个端口只能被一个进程绑定.
    public UdpEchoServer(int port) throws SocketException {
        //构造时, 指定要绑定的端口号.
        socket = new DatagramSocket(port);
    }

    //启动服务器的主逻辑
    public void start() throws IOException {
        System.out.println("服务器启动!!!");
        //因为服务器是要时刻读取客户端信息的, 所以使用while循环来重复读取并处理信息.
        while(true) {
            //每次循环都只做三件事
            // 1.读取请求并解析
            //   下面这是构造一个数据包, 就可以理解为一个餐盘, 这个餐盘里指定要放入的数据类型及大小
            //   空的餐盘构建好了就得装东西了
            DatagramPacket requestPacket = new DatagramPacket(new byte[666], 666); 
            //   1kb=1024byte, UDP最多发送64kb(包含UDP首部8byte)
            //   通过 socket 对象来接收信息(也就是填充餐盘)
            socket.receive(requestPacket); //如果没接收到信息就会阻塞等待
            //   为了方便我们处理这个请求, 将数据包转为 String
            String request = new String(requestPacket.getData(),0, requestPacket.getLength());
            // 2.根据请求来计算响应(这里直接返回了)
            String response = process(request);
            // 3.把响应结果写回客户端
            //   根据 response 来构造一个 DatagramPacket
            //   和之前构造不同, 本次构造相当于将餐盘填满, 注意还要指定发送给谁
            //   requestPacket.getSocketAddress()是获取客户端的IP和端口号(填充的 requestPacket 就包含了客户端的IP和地址)
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    0,response.getBytes().length,requestPacket.getSocketAddress());
            //   把结果发送给客户端
            socket.send(responsePacket);
            //打印一下客户端的IP和端口号, 还有请求和响应
            System.out.printf("[%s : %d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }

    //根据请求计算响应
    public String process(String request) {
        return request;
    }

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

客户端

public class UdpEchoClient {
    //客户端也需要 socket 对象来进行数据交互
    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 {
        System.out.println("启动客户端!!!");
        //通过 Scanner 来读取用户输入
        Scanner scanner = new Scanner(System.in);
        while(true) {
            // 1.先从控制台读取一个字符串
            //   打印一个提示符, 提示用户输入
            System.out.print("-> ");
            String request = scanner.next();
            // 2.把字符串构造成 UDP packet, 并进行发送
            //   InetAddress.getByName() 确定主机的IP地址
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIP), serverPort);
            socket.send(requestPacket);
            // 3.客户端尝试读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[666],666);
            socket.receive(responsePacket);
            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",8888);
        udpEchoClient.start();
    }
}

启动看看效果 :
客户端 :
在这里插入图片描述
服务端 :
在这里插入图片描述

UDP 版本的字典客户端和字典服务器

要想实现单词查找功能, 首先要有一个数据结构来对单词进行存储, 一般用哈希表来存储, key 来存储单词, value 存储单词意思.

其实客户端都是一样的, 只是服务器根据请求, 返回的响应不一样了.
所以我们主要就是重写 process 方法.

服务端 :

public class UdpDictServer extends UdpEchoServer{
    private HashMap<String,String> dict = new HashMap<>();
    public UdpDictServer(int port) throws SocketException {
        super(port);
        dict.put("pen","笔");
        dict.put("dog","狗");
        dict.put("cat","猫");
        //...可以无限添加, 其实网上的翻译词典什么的,就是样本比这个大
    }

    @Override
    public String process(String request) {
        return dict.getOrDefault(request,"未找到该单词.");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer udpDictServer = new UdpDictServer(8888);
        udpDictServer.start();
    }
}

客户端 :
在这里插入图片描述
服务端 :
在这里插入图片描述

TCP 版的回显服务器

需要用到的 api

  1. ServerSocket API

ServerSocket 是创建TCP服务端 Socket 的 API (给服务器用的)

ServerSocket 一定要绑定具体端口号. (服务器得绑定的端口号才能提供服务)

主要用到的构造方法 :

ServerSocket(int port)

创建一个服务端流套接字Socket,并绑定到指定端口.

主要用到的方法 :

Socket accept()

开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端 Socket 对象,并基于该Socket建立与客户端的连接,否则阻塞等待.

void close()

关闭此套接字 (大多情况下, ServerSocket 的生命周期都会跟随整个程序, 所以不调 close 也问题不大)

accept 就是接受的意思.
客户端主动发起连接, 服务端被动接受连接.
其实 tcp 的连接是在内核就完成了的, 这里的 accept 是应用层层面的接受, 并返回一个 Socket 对象, 通过这个对象就可以与客户端进行交互了.

  1. Socket API

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

Socket 和 DatagramSocket 类似, 都是构造的时候指定一个具体的端口, 让服务器绑定该端口.

主要用到的构造方法 :

Socket(String host, int port)

两个参数表示 ip 和 端口号, TCP是有连接的, 在客户端 new Socket 时, 就会尝试和指定 ip 端口的目标建立连接.

主要用到的方法 :

InetAddress getInetAddress()

返回套接字所连接的地址

InputStream getInputStream()

返回此套接字的输入流

OutputStream getOutputStream()

返回此套接字的输出流

void close()

关闭此套接字

TCP 是面向字节流的, 所以我们可以通过上述字节流对象进行数据传输.
从 InputStream 读数据, 就相当于从网卡接收.
往 OutputStream 写数据, 就相当于从网卡发送.

服务器

public class TcpEchoServer {
    // serverSocket 就是外场拉客的小哥哥
    // serverSocket 只有一个.
    private ServerSocket serverSocket = null;
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        // 服务器不止是对一个客户端进行服务
        // 加上 while 循环, 是让服务器随时可以获取到新的客户端的请求连接
        while(true) {
            // clientSocket 就是内场服务的小姐姐.
            // clientSocket 会给每个客户端都分配一个.(可以通过它来获取客户端的信息)
            Socket clientSocket = serverSocket.accept();
            // 我们对多客户端的交互应该是并行的
            // 所以创建线程来对每个客户端进行响应
            Thread t = new Thread(() -> {
                try {
                    // 该方法就是对客户端进行交互了
                    processConnection(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            t.start();
        }
    }

    private void processConnection(Socket clientSocket) throws IOException {
        // 提醒客户端已上线.
        System.out.printf("[%s : %d] 客户端已上线!\n",
                clientSocket.getInetAddress(),clientSocket.getPort());
        // 将输入,输出流放入 try() 中, 可以不用考虑资源释放.
        // 通过 clientSocket 获得输入, 输出流.
        try(InputStream inputStream = clientSocket.getInputStream();
                OutputStream outputStream = clientSocket.getOutputStream()) {
            // 因为 tcp 是面向字节流的, 所以为了方便读取, 给输入流套一个外壳
            // Scanner 是面向字符流.
            Scanner scanner = new Scanner(inputStream);
            // 同样的, 为了方便写入, 给输出流套一个壳
            // PrintWriter 是面向字符流
            PrintWriter printWriter = new PrintWriter(outputStream);
            // 因为服务器与客户端交互不是一次就完了, 所以得加个循环
            while(true) {
                // 判断是否还有数据在输入流中,如果没有了就认为客户端下线了,退出循环
                if(!scanner.hasNext()) {
                    System.out.printf("[%s : %d] 客户端已下线!\n",
                            clientSocket.getInetAddress(),clientSocket.getPort());
                    break;
                }
                // 通过 scanner 读取网卡(客户端的请求)
                String request = scanner.next();
                // 通过请求计算响应
                String response = process(request);
                // 将响应写入缓存区, 当缓存区满了 就会写入网卡
                // 因为 Scanner 读取是不会读空格和回车的
                // 所以写入的时候要加换行, 以便客户端区分请求和响应
                printWriter.println(response);
                // 因为写入缓存区时, 缓存区并不一定会刷新
                // 所以我们要手动刷新, 将缓存中的数据刷入网卡
                printWriter.flush();
                System.out.printf("[%s %d] req: %s resp: %s\n", clientSocket.getInetAddress(),clientSocket.getPort(),
                                    request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            // 因为 clientSocket 在客户端下线后就没用了,所以要释放
            // serverSocket 之所以不要释放, 是因为它的生命周期伴随整个程序
            // 程序结束, 它自然被释放了
            clientSocket.close();
        }
    }

    //计算响应
    public String process(String request) {
        return request;
    }

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

客户端

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 scanner = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream();
                OutputStream outputStream = socket.getOutputStream()) {
            PrintWriter printWriter = new PrintWriter(outputStream);
            // 这里的 scannerFromSocket 是用来读取网卡(服务器响应)
            Scanner scannerFromSocket = new Scanner(inputStream);
            // 通过循环来对服务器进行多次请求
            while(true) {
                // 提示用户输入
                System.out.printf("-> ");
                // 读取键盘输入
                String request = scanner.next();
                // 将键盘输入写入缓存, 当缓存满了就自动刷新 并写入网卡
                // 注意带回车, 不然服务器读不到空白符,会一直读
                printWriter.println(request);
                // 手动刷新缓存, 将数据写入网卡
                printWriter.flush();
                // 读取服务器写入网卡的响应
                String response =  scannerFromSocket.next();
                System.out.printf("req: %s resp: %s\n", request,response);
            }

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

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

服务端 :

在这里插入图片描述

客户端 :

在这里插入图片描述
当然了, 我们还可以多启动几个客户端, 与服务器进行交互.

服务端 :
在这里插入图片描述

对服务器进行改进(使用线程池)

这里主要是修改 start 方法里的线程创建, 就单独拿出来修改 :

    public void start() throws IOException {
        while(true) {
            ExecutorService executorService = Executors.newCachedThreadPool();
            Socket clientSocket = serverSocket.accept();
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

效果还是一样的.

TCP 版本的字典客户端和字典服务器

其实和 UDP 版的基本一样, 只需要对服务端进行修改 :

public class TcpDictServer extends TcpEchoServer{
    private HashMap<String,String> dict = new HashMap<>();
    public TcpDictServer(int port) throws IOException {
        super(port);
        dict.put("pen","笔");
        dict.put("dog","狗");
        dict.put("cat","猫");
    }

    public String process(String request) {
        return dict.getOrDefault(request,"未找到该单词");
    }

    public static void main(String[] args) throws IOException {
        TcpDictServer tcpDictServer = new TcpDictServer(8080);
        tcpDictServer.start();
    }
}

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

浅谈联网汽车安全漏洞

​“智能网联汽车存在内生共性问题&#xff0c;即软硬件的漏洞后门&#xff0c;基于此进行的网络攻击可以直接带来勒索、盗窃、大规模车辆恶意操控风险&#xff0c;还有数据泄露等网络安全事件。如果内生的漏洞后门问题不解决&#xff0c;系统自身难保&#xff0c;很难谈系统安…

浙大数据结构第四周之二叉搜索树与平衡二叉搜索树(AVL)

题目详情&#xff1a;04-树4 是否同一棵二叉搜索树 给定一个插入序列就可以唯一确定一棵二叉搜索树。然而&#xff0c;一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树&#xff0c;都得到一样的结果。于是…

【Python系列】一个简单的抽奖小程序

序言 很开心你能在万千博文中打开这一篇&#xff0c;希望能给你带来一定的帮助&#xff01;&#x1f44d;&#x1f3fb; 如果有什么问题&#xff0c;都可以添加下方我的联系方式&#xff0c;联系我噢~&#x1f601; ⭐️⭐️⭐️⭐️⭐️沟通交流&#xff0c;一起成为技术达人&…

SpringMVC底层原理源码解析

SpringMVC的作用毋庸置疑&#xff0c;虽然我们现在都是用SpringBoot&#xff0c;但是SpringBoot中仍然是在使用SpringMVC来处理请求。 我们在使用SpringMVC时&#xff0c;传统的方式是通过定义web.xml&#xff0c;比如&#xff1a; <web-app><servlet><servle…

jenkins配置笔记

文章目录 1.装Gogs插件2.配置截图及说明3.其他1.装Gogs插件 Gogs 插件是一个专门为集成 Gogs(一种类似于 Git 的开源版本控制系统)与 Jenkins 进行交互的插件。它允许 Jenkins 与 Gogs 之间进行双向通信,提高了 Jenkins 对于代码管理和构建的支持。 2.配置截图及说明 目前…

【操作系统】第二章进程与线程、处理机调度与调度算法

文章目录 &#x1f337; 一、进程的定义、组成、组织方式及特征⭐️ 1、 进程的定义⭐️ 2、 进程实体的组成⭐️ 3、 进程的组织⭐️ 4、 进程的特征 &#x1f337; 二、进程的状态与转换⭐️ 1、 状态⭐️ 2、 进程状态间的转换 &#x1f337; 三、进程控制⭐️ 1、 定义⭐️…

SwiftUI 新 Alert 弹出窗口圆你文本输入之梦

概览 小伙伴们都知道&#xff0c;弹出 Alert 不能包含文本输入框是 SwiftUI 的阿喀琉斯之踵(Achilles’ Heel) 。当然&#xff0c;这说的有些夸张了。&#x1f609; 不过&#xff0c;Alert 不能包含 TextField 在某些情况下着实不方便。于是乎&#xff0c;从 SwiftUI 3.0&…

JAVA模拟堆

堆的性质 堆是一种特殊的树。 只要满足以下两点&#xff0c;它就是一个堆&#xff1a; 堆是一个完全二叉树。堆中每一个节点的值都必须大于等于&#xff08;或小于等于&#xff09;其子树中每个节点的值。 第一点&#xff0c;堆必须是一个完全二叉树。完全二叉树要求&#…

O2OA (翱途) 平台 V8.0 发布新增数据台账能力

亲爱的小伙伴们&#xff0c;O2OA (翱途) 平台开发团队经过几个月的持续努力&#xff0c;实现功能的新增、优化以及问题的修复。2023 年度 V8.0 版本已正式发布。欢迎大家到 O2OA 的官网上下载进行体验&#xff0c;也希望大家在藕粉社区里多提宝贵建议。本篇我们先为大家介绍应用…

Android 输入系统

概述 Android 输入系统的工作原理概括来说&#xff0c;内核将原始事件写入到设备节点中&#xff0c;InputReader 不断地通过 EventHub 将原始事件取出来并翻译加工成 Android 输入事件&#xff0c;然后交给 InputDispatcher。 InputDispatcher 根据 WMS 提供的窗口信息将事件…

24.Stream流

Stream流 一、什么是Stream流 Stream流操作是Java 8提供一个重要新特性&#xff0c;它允许开发人员以声明性方式处理集合&#xff0c;其核心类库主要改进了对集合类的 API和新增Stream操作。Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中&…

2023全栈开发人员职业路线图

0. 全栈开发人员职业路线图 全栈开发人员是IT行业中薪资最高的职业之一。 如果您想成为一名全栈开发人员&#xff0c;以下是2023年全栈开发人员路线图上的十一个步骤&#xff1a; 掌握敏捷开发和Scrum学习浏览器技术&#xff0c;如HTML和CSS熟练掌握JavaScript或TypeScript了…

单月涨粉303.72w,反差感才是主流吗?

据新抖「直播带货风向」数据显示&#xff0c;抖音4月的直播商品数量达到1021.32w&#xff0c;较上月的695.91w环比增长50.18%&#xff0c;直播销量环比增加16.81%。从这几个数值就可以看出4月的抖音电商依旧如火如荼...... 那么&#xff0c;4月&#xff0c;抖音又出现哪些新的看…

“世界中医药之都” 亳州市医保局领导一行莅临万民健康交流指导

为进一步推进智慧医疗、智慧服务、智慧管理“三位一体”为主旨的“智慧中医、健康社区”项目建设。2023 年 5 月 3 日&#xff0c;“世界中医药之都” 亳州市医保局 局长 吴旭春 、 医保中心主任秦克靖 、 办公室主任徐伟 等一行 5 人莅临 万民健康交流 指导工作 &#xff0c…

day27_mysql

今日内容 零、 复习昨日 一、单表查询 二、多表联查 零、 复习昨日 1 DDL,DML,DQL是啥 DDL 数据定义语言&#xff08;库&#xff0c;表&#xff0c;列&#xff09;DML 数据操作语言&#xff08;表内数据的操作增删改&#xff09;DQL 数据查询语言&#xff08;表内数据的查询&am…

酷游浅谈网站Javas cript型别

最近整理了一下&#xff0c;【酷游娜娜手机&#x1d54d;找看看nay3989提供】就决定跟大家讨论一下最近对于Javascripet的型别认识。 弱型别&#xff36;&#xff33; 强型别 Javascripet是一种「弱型别」的语言&#xff0c;所以会产生很多你意想不到恶心的事情 至于什么是弱…

软件测试、测试和开发、测试和调试【详细介绍】

目录 一、什么是软件测试 1.软件测试的定义 2.软件测试的目的 3.软件测试的不可穷性 二、开发和测试的区别 三、测试和调试的区别 一、什么是软件测试 在日常生活中&#xff0c;测试是无处不在的。比如新买的手机是否好用、新买的衣服穿着是否合身等等场景&#xff0c;均…

点成案例丨细胞培养芯片用于构建肠模型实例分享

器官芯片是一种利用微芯片制造技术制造的微流体细胞培养设备。该设备包含多个连续灌注腔室&#xff0c;具有多细胞层结构、组织界面、物理化学微环境以及人体血管循环等特征&#xff0c;可以模拟和重构人体器官的生理功能&#xff0c;为相关研究提供了可靠的平台。器官芯片技术…

java中设计模式总结

设计模式是实际工作中写的各种代码进行高层次抽象的总结&#xff0c;其中最出名的当属 Gang of Four (GoF) 的分类了&#xff0c;他们将设计模式分类为 23 种经典的模式&#xff0c;根据用途又可以分为三大类&#xff0c;分别为创建型模式、结构型模式和行为型模式。 有一些重…

【6D位姿估计】Point Pair Feature (PPF)

论文链接:Drost et al. Model Globally, Match Locally: Efficient and Robust 3D Object Recognition. CVPR, 2010. Model Globally, Match Locally 论文名字用 4 个词高度总结了 PPF 算法的精髓:“整体建模,局部匹配”。 下面这张图反应了论文的基本思想(算法概要): …