JavaFx学习--chapter02(网络对话)

news2024/10/21 11:00:46

chapter02(网络对话)

简单网络对话程序

设计任务:客户端向服务器发送字符串,并能读取服务器返回的字符串。

知识点:TCP套接字技术,C/S软件架构程序设计

重点理解:Java客户套接字类Socket和服务器套接字类ServerSocket,以及配套使用流的读/写类BufferedReader/PrintWriter

C/S软件架构程序设计技术中,实现网络通信的两个应用进程,一个叫做服务进程,另一个叫做客户进程,如图所示。服务进程首先被动打开一个 监听端口,如8008,客户进程主动访问这个端口,完成对话聊天前的TCP三 次握手连接。

在这里插入图片描述

Java 的 TCP/IP 套接字编程将底层的细节进行了封装,其编程模型如图

在这里插入图片描述

socket套接字类

Java TCP/IP 编程模型中,有两个套接字类:服务进程中的是 ServerSocket 类客户进程中的是Socket类。 服务进程首先开启一个或多个监听端口,客户进程向服务进程发起TCP三次握手连接。

TCP 连接成功后,逻辑上可理解为通信进程的双方具有两个流(输出流和输入流)。逻辑上可将两个流理解为两个通信管道的全双工通信模式,一个用于向对方发送数据,另一个用于接收对方的数据。

成员方法

套接字类有两个基本的方法可以获得两个通信管道:

  1. socket.getInputStream() 方法可获得输入字节流的入口;
  2. socket.getOutputStream() 方法可获得输出字节流的出口;
监听套接字和通信套接字

在网络编程中,监听套接字(Listening Socket)和通信套接字(Communication Socket)是两个重要的概念,通常用于实现客户端和服务器之间的通信。以下是对这两个概念的详细解释:

1. 监听套接字(Listening Socket)
  • 定义:监听套接字是服务器端创建的套接字,用于监听来自客户端的连接请求。它不直接用于数据传输,而是用于接受连接。
  • 创建:在服务器端,首先需要创建一个套接字并将其绑定到一个特定的地址和端口,然后调用 listen() 方法使其进入监听状态。
  • 功能
    • 等待客户端的连接请求。
    • 一旦有客户端请求连接,服务器会接受这个连接并创建一个新的通信套接字。

示例代码(C++):

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0); // 创建监听套接字
    if (server_fd == 0) {
        std::cerr << "Socket creation failed" << std::endl;
        return -1;
    }

    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY; // 监听所有可用的接口
    address.sin_port = htons(8080); // 端口号

    bind(server_fd, (struct sockaddr *)&address, sizeof(address)); // 绑定地址和端口
    listen(server_fd, 3); // 开始监听,最大连接数为3

    std::cout << "Listening on port 8080..." << std::endl;

    return 0;
}
2. 通信套接字(Communication Socket)
  • 定义:通信套接字是服务器在接受客户端连接请求后创建的套接字,用于与特定客户端进行数据传输。
  • 创建:当监听套接字接受到连接请求后,服务器会调用 accept() 方法,返回一个新的套接字(通信套接字),用于与该客户端进行通信。
  • 功能
    • 进行数据的发送和接收。
    • 每个连接的客户端都有一个独立的通信套接字。
  • 示例代码(C++):
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    
    // 省略监听套接字的创建和绑定代码...

    listen(server_fd, 3);
    std::cout << "Listening on port 8080..." << std::endl;

    int client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen); // 接受连接
    if (client_socket < 0) {
        std::cerr << "Accept failed" << std::endl;
        return -1;
    }

    const char *message = "Hello from server";
    send(client_socket, message, strlen(message), 0); // 发送数据
    std::cout << "Message sent to client" << std::endl;

    close(client_socket); // 关闭通信套接字
    close(server_fd); // 关闭监听套接字

    return 0;
}
总结
  • 监听套接字:用于等待和接受客户端的连接请求。
  • 通信套接字:用于与已连接的客户端进行数据传输。

这种设计使得服务器能够同时处理多个客户端连接,因为每个连接都有自己的通信套接字,而监听套接字则保持在监听状态,等待新的连接请求。如果你有更多问题或需要进一步的帮助,请告诉我!

模拟Button的点击事件

在JavaFX中,模拟按钮(Button)的点击可以通过直接调用按钮的fire()方法来实现,但请注意,Button类本身并没有直接提供fire()方法。不过,Button类继承自javafx.scene.control.Control,而Control类有一个受保护的fire()方法,但这个方法通常不是用来直接模拟用户点击事件的。

更好的方法是定义一个可以在代码中调用的方法,该方法包含你想要在按钮点击时执行的代码。然后,你可以在按钮的事件监听器中调用这个方法,也可以直接从代码的其他部分调用它。

import javafx.application.Application;  
import javafx.event.ActionEvent;  
import javafx.event.EventHandler;  
import javafx.scene.Scene;  
import javafx.scene.control.Button;  
import javafx.scene.layout.StackPane;  
import javafx.stage.Stage;  
  
public class ButtonClickSimulationExample extends Application {  
  
    @Override  
    public void start(Stage primaryStage) {  
        Button btn = new Button();  
        btn.setText("Click Me!");  
  
        // 定义点击时执行的方法  
        EventHandler<ActionEvent> onButtonClick = event -> {  
            System.out.println("Button clicked!");  
            // 这里可以放置更多的代码  
        };  
  
        // 将事件监听器绑定到按钮  
        btn.setOnAction(onButtonClick);  
  
        // 直接从代码中“模拟”按钮点击  
        // 注意:这不是真正的模拟点击,而是直接调用了点击时要执行的代码  
        onButtonClick.handle(new ActionEvent());  
  
        StackPane root = new StackPane();  
        root.getChildren().add(btn);  
        Scene scene = new Scene(root, 300, 250);  
  
        primaryStage.setTitle("Button Click Simulation Example");  
        primaryStage.setScene(scene);  
        primaryStage.show();  
    }  
  
    public static void main(String[] args) {  
        launch(args);  
    }  
}

可以通过设置button的disable属性来控制是否能点击按钮

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class DisableButtonExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        Button btn = new Button("点击我");
        
        // 按钮点击事件
        btn.setOnAction(event -> {
            btn.setDisable(true);
            btn.setText("按钮已禁用");
            
            // 假设这里有一个异步操作,完成后重新启用按钮
            new Thread(() -> {
                try {
                    Thread.sleep(2000); // 模拟异步操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                // 重新启用按钮
                btn.setDisable(false);
                btn.setText("点击我");
            }).start();
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("JavaFX Disable Button Example");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

最终代码

TCPServer.java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class TCPServer {
    private final int port; // 服务器监听端口号
    private final ServerSocket serverSocket; //定义服务器套接字

    public TCPServer() throws IOException {
        Scanner scanner = new Scanner(System.in); // 创建一个Scanner对象来读取标准输入
        System.out.println("请输入服务器监听的端口号:");
        if (scanner.hasNextInt()) { // 检查是否有下一个输入项并且是一个整数
            port = scanner.nextInt(); // 读取整数并赋值给port
        } else {
            System.out.println("输入错误,请输入一个有效的整数端口号。");
            // 这里可以根据需要处理错误情况,比如使用默认值或者退出程序
            port = 8080; // 例如,使用8080作为默认端口号
        }
        scanner.close(); // 关闭scanner对象

        serverSocket = new ServerSocket(port);
        System.out.println("服务器启动监听在 " + port + " 端口");
    }

    private PrintWriter getWriter(Socket socket) throws IOException {
        //获得输出流缓冲区的地址
        OutputStream socketOut = socket.getOutputStream();

        //网络流写出需要使用flush,这里在PrintWriter构造方法中直接设置为自动flush
        return new PrintWriter(
                new OutputStreamWriter(socketOut, StandardCharsets.UTF_8), true);

    }

    private BufferedReader getReader(Socket socket) throws IOException {
        //获得输入流缓冲区的地址
        InputStream socketIn = socket.getInputStream();
        return new BufferedReader(
                new InputStreamReader(socketIn, StandardCharsets.UTF_8));
    }

    //单客户版本,即每一次只能与一个客户建立通信连接
    public void Service() {
        while (true) {
            Socket socket = null;
            try {
                //此处程序阻塞等待,监听并等待客户发起连接,有连接请求就生成一个套接字。
                socket = serverSocket.accept();

                //本地服务器控制台显示客户端连接的用户信息
                System.out.println("New connection accepted: " + socket.getInetAddress().getHostAddress());
                BufferedReader br = getReader(socket);//定义字符串输入流
                PrintWriter pw = getWriter(socket);//定义字符串输出流
                //客户端正常连接成功,则发送服务器的欢迎信息,然后等待客户发送信息
                pw.println("From 服务器:欢迎使用本服务!");

                String msg = null;
                //此处程序阻塞,每次从输入流中读入一行字符串
                while ((msg = br.readLine()) != null) {
                    //如果客户发送的消息为"bye",就结束通信
                    if (msg.equals("bye")) {
                        //向输出流中输出一行字符串,远程客户端可以读取该字符串
                        pw.println("From服务器:服务器断开连接,结束服务!");
                        System.out.println("客户端离开");
                        break; //结束循环
                    }
                    //向输出流中输出一行字符串,远程客户端可以读取该字符串
                    pw.println("From服务器:" + msg);

                }
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
            finally {
                try {
                    if(socket != null)
                        socket.close(); //关闭socket连接及相关的输入输出流
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public static void main(String[] args) throws IOException {
        TCPServer server = new TCPServer();
        System.out.println("服务器将监听端口号: " + server.port);
        server.Service();
    }
}
TCPClient.java
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class TCPClient {
    private final Socket socket; // 定义套接字
    private final PrintWriter pw; // 定义字符输出流
    private final BufferedReader br; // 定义字符输入流

    public TCPClient(String ip, String port) throws IOException {
        // 主动向服务器发起连接,实现TCP的三次握手过程
        // 如果不成功,则抛出错误信息,其错误信息交由调用者处理
        socket = new Socket(ip, Integer.parseInt(port));

        // 得到网络输出字节流地址,并封装成网络输出字符流
        // 设置最后一个参数为true,表示自动flush数据
        OutputStream socketOut = socket.getOutputStream();
        pw = new PrintWriter(new OutputStreamWriter(socketOut, StandardCharsets.UTF_8), true);

        // 得到网络输入字节流地址,并封装成网络输入字符流
        InputStream socketIn = socket.getInputStream();
        br = new BufferedReader(new InputStreamReader(socketIn, StandardCharsets.UTF_8));
    }

    public void send(String msg) {
        // 输出字符流,由Socket调用系统底层函数,经网卡发送字节流
        pw.println(msg);
    }

    public String receive() {
        String msg = null;
        try {
            // 从网络输入字符流中读信息,每次只能接收一行信息
            // 如果不够一行(无行结束符),则该语句阻塞等待
            msg = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return msg;
    }

    // 实现close方法以关闭socket连接及相关的输入输出流
    public void close() {
        try {
            if (pw != null) {
                pw.close(); // 关闭PrintWriter会先flush再关闭底层流
            }
            if (br != null) {
                br.close(); // 关闭BufferedReader
            }
            if (socket != null) {
                socket.close(); // 关闭Socket连接
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
SimpleFx(窗口)
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.io.IOException;


public class SimpleFx extends Application {

    private TCPClient tcpClient;

    private final Button btnCon = new Button("连接");
    private final Button btnExit = new Button("退出");
    private final Button btnSend = new Button("发送");

    private final TextField IpAdd_input = new TextField();
    private final TextField Port_input = new TextField();
    private final TextArea OutputArea = new TextArea();

    private final TextField InputField = new TextField();


    public void start(Stage primaryStage) {
        BorderPane mainPane = new BorderPane();
        VBox mainVBox = new VBox();

        HBox hBox = new HBox();
        hBox.setSpacing(10);//各控件之间的间隔
        //HBox面板中的内容距离四周的留空区域
        hBox.setPadding(new Insets(20, 20, 10, 20));
        hBox.getChildren().addAll(new Label("IP地址: "), IpAdd_input, new Label("端口: "), Port_input, btnCon);

        hBox.setAlignment(Pos.TOP_CENTER);
        //内容显示区域
        VBox vBox = new VBox();
        vBox.setSpacing(10);//各控件之间的间隔
        //VBox面板中的内容距离四周的留空区域
        vBox.setPadding(new Insets(10, 20, 10, 20));
        vBox.getChildren().addAll(new Label("信息显示区:"), OutputArea, new Label("信息输入区"), InputField);
        //设置显示信息区的文本区域可以纵向自动扩充范围
        VBox.setVgrow(OutputArea, Priority.ALWAYS);
        // 设置文本只读和自动换行
        OutputArea.setEditable(false);
        OutputArea.setStyle("-fx-wrap-text: true; /* 实际上是默认的 */ -fx-font-size: 14px;");


        InputField.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.ENTER) {
                btnSend.fire();
            }
        });

        //底部按钮区域
        HBox hBox2 = new HBox();
        hBox2.setSpacing(10);
        hBox2.setPadding(new Insets(10, 20, 10, 20));

        // 设置按钮的交互效果
        btnCon.setOnAction(event -> {
            String ip = IpAdd_input.getText().trim();
            String port = Port_input.getText().trim();
            try {
                //tcpClient不是局部变量,是本程序定义的一个TCPClient类型的成员变量
                tcpClient = new TCPClient(ip, port);
                //成功连接服务器,接收服务器发来的第一条欢迎信息
                String firstMsg = tcpClient.receive();
                OutputArea.appendText(firstMsg + "\n");
            } catch (Exception e) {
                OutputArea.appendText("服务器连接失败!" + e.getMessage() + "\n");
            }
        });
        btnExit.setOnAction(event -> {
         if (tcpClient != null){
             //向服务器发送关闭连接的约定信息
             tcpClient.send("bye");
             tcpClient.close();
         }
            System.exit(0);
        });
        btnSend.setOnAction(event -> {
            String sendMsg = InputField.getText();
            tcpClient.send(sendMsg);//向服务器发送一串字符
            InputField.clear();
            OutputArea.appendText("客户端发送:" + sendMsg + "\n");
            String receiveMsg = tcpClient.receive();//从服务器接收一行字符
            OutputArea.appendText(receiveMsg + "\n");
        });

        hBox2.setAlignment(Pos.CENTER_RIGHT);
        hBox2.getChildren().addAll(btnSend, btnExit);

        mainVBox.getChildren().addAll(hBox, vBox, hBox2);

        mainPane.setCenter(mainVBox);
        VBox.setVgrow(vBox, Priority.ALWAYS);
        Scene scene = new Scene(mainPane, 700, 400);

        primaryStage.setScene(scene);
        primaryStage.show();


    }

    public static void main(String[] args) {
        launch(args);
    }

}

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

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

相关文章

docker配置加速器

阿里云 控制台》容器镜像服务》镜像工具》镜像加速器 复制地址&#xff1a;https://ywtoq7bz.mirror.aliyuncs.com 到&#xff1a;etc/docker下&#xff1a;vi daemon.json 格式&#xff1a; { "registry-mirrors": ["加速器地址"] } 注&#xff1…

Visual Studio 2022安OpenCV可视化工具image watch

1. 打开 VS2022 &#xff0c;扩展 -管理扩展 2. 搜索 Image Watch 关闭VS2022 后 安装 打开视图、调出 Image Watch 窗口 测试代码&#xff1a; #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.…

Transformer(Vit+注意力机制)

文献基本信息&#xff1a; Encoder-Decoder&#xff1a; Transformer的结构&#xff1a; 输入编码器解码器输出 Transformer的工作流程&#xff1a; 获取输入句子的每一个单词的表示向量X&#xff0c;X由单词的embedding&#xff08;embedding是一种将高维特征映射到低维的技…

opencv出错以及解决技巧

opencv配置 一开始&#xff0c;include的路径是<opencv4/opencv2/…> 这样在using namespace cv的时候导致了报错&#xff0c; 所以在cmakelist中需要对cmake的版本进行升级。 set(CMAKE_CXX_FLAGS “-stdc14 -O0 -Wall”)-O0 表示在编译过程中不进行任何优化 对应的pac…

Linux操作系统如何制作U盘启动盘

在麒麟系统中有一款U盘启动器软件&#xff0c;它是用于制作系统启动U盘的工具&#xff0c;方便无光驱的电脑安装操作系统&#xff0c;也可以反复使用一个U盘&#xff0c;避免光盘的浪费。下面对该U盘启动器使用方法做详细讲解。 1.准备需要安装的系统镜像文件。 图 1 2.准备1…

Node-RED开源项目的modbus通信(TCP)

一、Modbus 通信协议 Modbus是一种串行通信协议&#xff0c;是Modicon公司&#xff08;现在的施耐德电气 Schneider Electric&#xff09;于1979年为使用可编程逻辑控制器&#xff08;PLC&#xff09;通信而发表。Modbus已经成为工业领域通信协议的业界标准&#xff08;De fact…

Redis高阶篇之Redis单线程与多线程

文章目录 0 前言1. 为什么Redis是单线程&#xff1f;1.1 Redis单线程1.2 为什么Redis3时代单线程快的原因1.3 使用单线程原因 2.为什么逐渐加入多线程呢&#xff1f;2.1 如何解决 3.redis6/7的多线程特性和IO多路复用入门3.1主线程和IO线程怎么协作完成请求处理的3.2 Unix网络编…

政府采购合同公告明细数据(1996-2024年)

透明度成为了公众对政府活动的基本要求之一。特别是在政府采购领域&#xff0c;透明度不仅关系到公共资源的合理分配&#xff0c;更是维护市场公平竞争的重要保障。政府采购合同公告制度正是为了满足这一需求而设立的。 1996-2024年政府采购合同公告明细数据&#xff08;dta文…

Perl打印9x9乘法口诀

本章教程主要介绍如何用Perl打印9x9乘法口诀。 一、程序代码 1、写法① use strict; # 启用严格模式&#xff0c;帮助捕捉变量声明等错误 use warnings; # 启用警告&#xff0c;帮助发现潜在问题# 遍历 1 到 9 的数字 for my $i (1..9) {# 对于每个 $i&#xff0c;遍历 1…

Javascript 脚本查找B站限时免费番剧

目录 前言 脚本编写 脚本 前言 B站的一些番剧时不时会“限时免费”&#xff0c;白嫖党最爱&#xff0c;主打一个又占到便宜的快乐。但是在番剧索引里却没有搜索选项可以直接检索“限时免费”的番剧&#xff0c;只能自己一页一页的翻去查看&#xff0c;非常麻烦。 自己找限…

Git极速入门

git初始化 git -v git config --global user.name "" git config --global user.email "" git config --global credential.helper store git config --global --list省略(Local) 本地配置&#xff0c;只对本地仓库有效–global 全局配置&#xff0c;所有…

spring boot yml文件中引用*.properties文件中的属性

1、首先在*.properties文件中加入一个属性&#xff0c;如&#xff1a; 2、然后再application.yml文件中通过${jdbc.driver}来引用&#xff0c;如&#xff1a; 3、然后再创建一个资源配置类&#xff0c;通过PropertySource来引入这个*.properties文件&#xff0c;如&#xff1…

JDK中socket源码解析

目录 1、Java.net包 1. Socket通信相关类 2. URL和URI处理类 3. 网络地址和主机名解析类 4. 代理和认证相关类 5. 网络缓存和Cookie管理类 6. 其他网络相关工具类 2、什么是socket&#xff1f; 3、JDK中socket核心Api 4、核心源码 1、核心方法 2、本地方法 3、lin…

基于stm32的esp8266的WIFI控制风扇实验

实验案例&#xff37;&#xff29;&#xff26;&#xff29;控制风扇 项目需求 电脑通过esp8266模块远程遥控风扇。 项目框图 ​ 风扇模块封装 #include "sys.h" #include "fan.h"void fan_init(void) {GPIO_InitTypeDef gpio_initstruct;//打开时钟…

4K Mini-LED显示器平民价,一千多的联合创新27M3U到底有多香

哈喽小伙伴们好&#xff0c;我是Stark-C~ 要说前几年买显示器还是普通IPS的天下&#xff0c;那个时候虽说也有MiniLED或者OLED显示器&#xff0c;但是价格那也是真贵啊&#xff0c;毕竟那个时候MiniLED和OLED还没普及&#xff0c;只有一些高档电视或者显示器才会用到此技术。不…

OpenCV高级图形用户界面(18)手动设置轨迹条(Trackbar)的位置函数setTrackbarPos()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数设置指定窗口中指定轨迹条的位置。 注意 [仅 Qt 后端] 如果轨迹条附加到控制面板&#xff0c;则 winname 可以为空。 函数原型 void cv…

三周精通FastAPI:4 使用请求从客户端(例如浏览器)向 API 发送数据

FastAPI官网手册&#xff1a;https://fastapi.tiangolo.com/zh/tutorial/query-params/ 上节内容&#xff1a;三周精通FastAPI&#xff1a;3 查询参数 请求 FastAPI 使用请求从客户端&#xff08;例如浏览器&#xff09;向 API 发送数据。 请求是客户端发送给 API 的数据。响…

国家信息安全水平考试(NISP一级)最新题库-第十六章

目录 另外免费为大家准备了刷题小程序和docx文档&#xff0c;有需要的可以私信获取 1 防火墙是一种较早使用、实用性很强的网络安全防御技术&#xff0c;以下关于防火墙说法错误的是&#xff08;&#xff09; A.防火墙阻挡对网络的非法访问和不安全数据的传递&#xff1b;B.防…

Leecode刷题之路第27天之移除元素

题目出处 27-移除元素-题目描述 题目描述 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#x…

C++ | Leetcode C++题解之第491题非递减子序列

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> temp; vector<vector<int>> ans;void dfs(int cur, int last, vector<int>& nums) {if (cur nums.size()) {if (temp.size() > 2) {ans.push_back(temp);}return;}if…