您当前的位置:首页 > 电脑百科 > 站长技术 > 服务器

Java,Socket,实现Socket4代理服务器,客户端用Socket4代理请求

时间:2022-09-02 16:18:38  来源:  作者:古怪今人

前言

Socket代理协议

Socket代理协议有Socket4和Socket5两个版本,Socket4协议只支持TCP/IP,Socket代理协议只是单纯传递数据包,不关心具体协议和用法,所以速度快很快。

Socket4代理已经过时了,支持Socket4代理,需要自己编写协议,这里研究是为了抓一些数据包供分析。

协议:
http://www.openssh.com/txt/socks4.protocol

协议说明

客户端请求,服务器端绑定。

请求:|VN-1|CD-1|DSTPORT-2|DSTIP-4|NULL...|

响应:|VN-1|CD-1|DSTPORT-2|DSTIP-4|

Socket4代理服务器

一个类可以搞定~

代码实现

import JAVA.io.*;
import java.NET.*;
import java.util.Arrays;
import java.util.Base64;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Socks4Server {

    // constants
    private static final int MAX_REQUEST_BYTES_NUMBER = 50;
    private static final int MINIMUM_REQUEST_LENGTH = 8;
    private static final int REQUEST_OK = 90;
    private static final int REQUEST_REJECTED_OR_FAILED = 91;
    private static final int MAX_PARALLEL_CONNECTIONS = 20;
    private static final int PROTOCOL_VERSION = 4;
    private static final int CONNECT_REQUEST = 1;
    private static final int TIMEOUT_TIME = 2000;
    private static final int BUF_SIZE = 4096;

    // error message definitions
    private static final String SUCCESSFUL_CONNECTION = "Successful connection from ";
    private static final String PASSword_FOUND = "Password found! ";
    private static final String CLOSING_CONNECTION = "Closing connection from: ";
    private static final String CONNECTER_ERROR = "Unknown Connection Error";
    private static final String UNSUPPOERTED_SOCKS_PROT = "Unsupported SOCKS protocol version ";
    private static final String UNKNOWN_SOCKS_COMMAND_ERROR = "Unknown socks command! currently supports only CONNECT request";
    private static final String ILLEGAL_IP_LENGTH = "IP address is of illegal length";
    private static final String ILLEGAL_REQUEST_LENGTH_ERROR = "The request has an illegal length - under 8 characters";
    private static final String ILLEGAL_PORT_NUMBER_ERROR = "Illegal port number. given port number is 0";
    private static final String CONNECTION_TO_DESTINATION_IP_FAILED = "Connection error: while connecting to destination: connect timed out";
    private static final String TIMOUT_ERROR = "Timeout limit reached closing all connection";
    private static final String CLIENTHOSTIOERROR = "Error during client host communication";
    private static final String NOCONNECT = "No connect message received";

    /**
     * @param args
     */
    public static void main(String[] args) {
        // 服务端口
        int port = 1080;
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            // 线程池
            ThreadPoolExecutor connections = new ThreadPoolExecutor(0, MAX_PARALLEL_CONNECTIONS,
                    1, TimeUnit.HOURS, new ArrayBlockingQueue(1), new RejectedConnection());
            while (true) {
                try {
                    connections.submit(new Handler(serverSocket.accept()));
                } catch (Exception e) {
                    System.err.println("与客户端的连接过程中出错");
                }
            }
        } catch (Exception e) {
            System.err.println("代理服务器崩溃");
        }
    }

    /**
     * SOCKS4服务器拒绝
     */
    private static class RejectedConnection implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            executor.remove(r);
            System.err.println("服务器容量已满,连接请求已被拒绝.");
        }

    }

    /**
     * 处理实际连接
     */
    private static class Handler extends Thread {

        private Socket clientSocket;
        private int destinationPort;
        private InetAddress destinationIP;

        Handler(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }

        @Override
        public void run() {
            try (DataInputStream clientInput = new DataInputStream(clientSocket.getInputStream());
                 DataOutputStream clientOutput = new DataOutputStream(clientSocket.getOutputStream())) {
                // 设置客户端超时时间
                clientSocket.setSoTimeout(TIMEOUT_TIME);
                // 读取来自客户端的请求
                byte[] byteArray = new byte[MAX_REQUEST_BYTES_NUMBER];
                int successFlag = clientInput.read(byteArray);
                if (successFlag == -1) {
                    System.err.println("已到达流的末端");
                    throw new NullPointerException();
                }
                // 处理连接请求并生成响应
                byte[] response = processRequest(byteArray);
                // 错误的请求导致连接被拒绝
                if (response[1] == REQUEST_REJECTED_OR_FAILED) {
                    clientOutput.write(response);
                    clientOutput.flush();
                    closingClientConnectionMessage(clientSocket);
                } else if (response[1] == REQUEST_OK) {
                    // 正常连接
                    try (Socket hostSocket = new Socket(destinationIP, destinationPort);
                         BufferedWriter HostOutput = new BufferedWriter(new OutputStreamWriter(hostSocket.getOutputStream()))) {
                        hostSocket.setSoTimeout(TIMEOUT_TIME);
                        clientOutput.write(response);
                        clientOutput.flush();
                        // 连接成功
                        successfulConnectionMessage(clientSocket);
                        try (BufferedReader clientBufReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) {
                            ExecutorService IOService = Executors.newFixedThreadPool(2);
                            if (destinationPort == 80) {
                                processClientDataHTTP(clientBufReader, HostOutput);
                            }
                            // 数据交换
                            while (true) {
                                ioService.submit(new ProcessData(clientSocket.getOutputStream(), hostSocket.getInputStream()));
                                ioService.submit(new ProcessData(hostSocket.getOutputStream(), clientSocket.getInputStream()));
                                if (!ioService.awaitTermination(TIMEOUT_TIME, TimeUnit.MILLISECONDS)) {
                                    throw new SocketTimeoutException();
                                }
                            }
                        } catch (SocketTimeoutException e) {
                            closingConnectionMessage(clientSocket);
                        } catch (IOException e) {
                            System.err.println(CLIENTHOSTIOERROR);
                        }
                    } catch (Exception e) {
                        response[1] = REQUEST_REJECTED_OR_FAILED;
                        clientOutput.write(response);
                        clientOutput.flush();
                        System.err.println(CONNECTION_TO_DESTINATION_IP_FAILED);
                        closingClientConnectionMessage(clientSocket);
                    }
                }
            } catch (NullPointerException e) {
                System.err.println(NOCONNECT);
            } catch (SocketTimeoutException e) {
                System.err.println(TIMOUT_ERROR);
            } catch (IOException e) {
                System.err.println(CONNECTER_ERROR);
            } finally {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    System.err.println("关闭过程中出错");
                }
            }
        }

        /**
         * 主机通信
         */
        private class ProcessData implements Runnable {

            OutputStream out;
            InputStream in;

            private ProcessData(OutputStream out, InputStream in) {
                this.in = in;
                this.out = out;
            }

            @Override
            public void run() {
                try {
                    byte[] buf = new byte[BUF_SIZE];
                    int numOfbytes = 0;
                    int numOfLine = 0;
                    while ((numOfbytes = in.read(buf)) > 0 && numOfbytes < BUF_SIZE) {
                        System.out.print(String.format("%02x", numOfbytes) + " ");
                        if (numOfLine % 16 == 0) {
                            System.out.println();
                        }
                        numOfLine++;
                        out.write(buf, 0, numOfbytes);
                    }
                } catch (Exception e) {
                    return;
                }
            }
        }

        /**
         * @param clientBufReader
         * @param HostOutput
         * @throws Exception
         */
        private void processClientDataHTTP(BufferedReader clientBufReader, BufferedWriter HostOutput) throws Exception {
            String line = "";
            StringBuilder clientData = new StringBuilder();
            line = clientBufReader.readLine();
            // regex definition
            Pattern authorizationPattern = Pattern.compile("^[Aa]uthorization: [Bb]asic (.*)");
            String credentials = "";
            Pattern hostPattern = Pattern.compile("Host: (.*)");
            String hostName = "";
            Base64.Decoder decoder = Base64.getDecoder();
            // 读取客户端数据
            while (clientBufReader.ready()) {
                // password/username regex
                Matcher authorizationMatcher = authorizationPattern.matcher(line);
                if (authorizationMatcher.matches()) {
                    credentials = authorizationMatcher.group(1);
                    credentials = new String(decoder.decode(credentials.getBytes("UTF-8")));
                }
                // hostname regex
                Matcher hostMatcher = hostPattern.matcher(line);
                if (hostMatcher.matches()) {
                    hostName = hostMatcher.group(1);
                }
                // parsing request
                clientData.Append(line);
                clientData.append("rn");
                line = clientBufReader.readLine();
            }
            System.out.println(PASSWORD_FOUND + "http//:" + credentials + "@" + hostName);
            clientData.append("rnrn");
            HostOutput.write(clientData.toString());
            HostOutput.flush();
        }

        private void successfulConnectionMessage(Socket clientSocket) {
            System.out.println(SUCCESSFUL_CONNECTION + clientSocket.getInetAddress().getHostAddress() + ":" + clientSocket.getPort() + " to " + destinationIP.getHostAddress() + ":" + destinationPort);
        }

        private void closingConnectionMessage(Socket clientSocket) {
            System.out.println(CLOSING_CONNECTION + clientSocket.getInetAddress().getHostAddress() + ":" + clientSocket.getPort() + " to " + destinationIP.getHostAddress() + ":" + destinationPort);
        }

        private void closingClientConnectionMessage(Socket clientSocket) {
            System.out.println(CLOSING_CONNECTION + clientSocket.getInetAddress().getHostAddress() + ":" + clientSocket.getPort());
        }

        /**
         * @param request - the client request
         * @return response - the SOCKS4 server response
         */
        private byte[] processRequest(byte[] request) {
            byte[] response = new byte[8];
            byte replyStatus = REQUEST_OK;
            // 检查请求是否有效
            if (!isRequestLengthLegal(request, response) ||
                    !processProtocolVersionRequest(request[0]) ||
                    !processSOCKScommandCode(request[1]) ||
                    !processDestinationPortNumber(request[2], request[3]) ||
                    !processDestinationIP(request)) {
                System.out.println("请求被拒绝!");
                replyStatus = REQUEST_REJECTED_OR_FAILED;
            }
            response[1] = replyStatus;
            return response;
        }

        // VN - SOCKS protocol version number
        private boolean processProtocolVersionRequest(byte version) {
            if (version != PROTOCOL_VERSION) {
                System.err.println(UNSUPPOERTED_SOCKS_PROT + "(got " + version + ")");
                return false;
            }
            return true;
        }

        // DSPORT
        private boolean processDestinationPortNumber(byte firstByte, byte secondByte) {
            this.destinationPort = (firstByte & 0xff) << 8 | (secondByte & 0xff);
            if (this.destinationPort <= 0) {
                System.err.println(ILLEGAL_PORT_NUMBER_ERROR);
                return false;
            }
            return true;
        }

        // CD - SOCKS command code and should be 1 for CONNECT request
        private boolean processSOCKScommandCode(byte code) {
            if (code != CONNECT_REQUEST) {
                System.err.println(UNKNOWN_SOCKS_COMMAND_ERROR);
                return false;
            }
            return true;
        }

        // DSTIP
        private boolean processDestinationIP(byte[] requestArray) {
            try {
                // SOCKS 4A
                if (requestArray[4] == 0 && requestArray[5] == 0 && requestArray[6] == 0 && requestArray[7] != 0) {
                    int start = 0;
                    int end = 0;
                    int i = 8;
                    while (requestArray[i] != 0) {
                        i++;
                    }
                    start = i + 1;
                    i++;
                    while (requestArray[i] != 0) {
                        i++;
                    }
                    end = i;
                    destinationIP = InetAddress.getByName(new String(Arrays.copyOfRange(requestArray, start, end)));
                }
                // regular SOCKS
                else {
                    destinationIP = InetAddress.getByAddress(Arrays.copyOfRange(requestArray, 4, 8));
                }
            } catch (UnknownHostException e) {
                System.err.println(ILLEGAL_IP_LENGTH);
                return false;
            }
            return true;
        }

        // request length must be minimum of length 8
        private boolean isRequestLengthLegal(byte[] request, byte[] response) {
            if (request.length < MINIMUM_REQUEST_LENGTH) {
                System.err.println(ILLEGAL_REQUEST_LENGTH_ERROR);
                return false;
            }
            // dest port
            response[2] = request[2];
            response[3] = request[3];
            // ip
            response[4] = request[4];
            response[5] = request[5];
            response[6] = request[6];
            response[7] = request[7];
            return true;
        }
    }

}

客户端使用Socket4代理

客户端协议实现类

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Sock4Socket extends Socket {

    // 版本
    private static byte VERSION4 = 0X04;
    // 请求
    private static byte CONNECT_REQUEST = 0X01;
    private static byte BIND_REQUEST = 0X02;
    // 结束
    private static byte END = 0X00;

    private String targetHost;

    private int targetPort;

    public Sock4Socket(String proxyHost, int proxyPort, String targetHost, int targetPort) throws UnknownHostException, IOException {
        super(proxyHost, proxyPort);
        this.targetHost = targetHost;
        this.targetPort = targetPort;
        this.connect();
    }

    /**
     * @param host
     * @param port
     * @return
     * @throws UnknownHostException
     */
    private byte[] connByte(String host, int port) throws UnknownHostException {
        String[] subIP = host.split("\.");
        String portStr = Integer.toHexString(port);
        byte subP1B = 0;
        byte subP2B = 0;
        if (portStr.length() <= 2) {
            subP2B = (byte) Byte.valueOf(portStr, 16);
        } else if (portStr.length() == 3) {
            String p1 = portStr.charAt(0) + "";
            subP1B = (byte) Byte.valueOf(p1, 16);
            String p2 = portStr.charAt(1) + "" + portStr.charAt(2);
            subP2B = (byte) Byte.valueOf(p2, 16);
        } else if (portStr.length() == 4) {
            String p1 = portStr.charAt(0) + "" + portStr.charAt(1);
            subP1B = (byte) Byte.valueOf(p1, 16);
            String p2 = portStr.charAt(2) + "" + portStr.charAt(3);
            subP2B = (byte) Byte.valueOf(p2, 16);
        }
        byte[] bt = new byte[9];
        bt[0] = VERSION4;
        bt[1] = CONNECT_REQUEST;
        bt[2] = subP1B;
        bt[3] = subP2B;
        bt[4] = (byte) Integer.parseInt(subIP[0]);
        bt[5] = (byte) Integer.parseInt(subIP[1]);
        bt[6] = (byte) Integer.parseInt(subIP[2]);
        bt[7] = (byte) Integer.parseInt(subIP[3]);
        bt[8] = END;
        return bt;
    }

    /**
     * 连接
     */
    private void connect() throws IOException {
        byte[] data = connByte(this.targetHost, this.targetPort);
        OutputStream os = this.getOutputStream();
        // 握手字节序列
        os.write(data);
        os.flush();
        // 服务端返回的字节 version,command,ip,port,message
        byte[] receive = new byte[8];
        InputStream is = this.getInputStream();
        is.read(receive);
        byte b = receive[1];
        if (b == 0) {
            throw new IOException("");
        } else if (b == 92) {
            throw new IOException("server time out");
        } else if (b == 90) {
            // success
        }
    }

}

测试案例

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

public class Sock4SocketClientDemo {

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        // Socket4代理服务地址
        String proxyHost = "127.0.0.1";
        int proxyPort = 1080;
        // 访问的地址
        String targetHost = "124.239.226.238";
        int targetPort = 80;
        // 创建Socket4代理Socket
        Sock4Socket sock4Socket = new Sock4Socket(proxyHost, proxyPort, targetHost, targetPort);
        OutputStream outputStream = sock4Socket.getOutputStream();
        InputStream inputStream = sock4Socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(inputStream, "GBK");
        BufferedReader br = new BufferedReader(isr);
        // 请求内容
        StringBuilder request = new StringBuilder();
        request.append("GET / HTTP/1.1rn");
        request.append("Accept-Language: zh-cnrn");
        request.append("Host: www.toutiao.comrn");
        request.append("rn");
        outputStream.write(request.toString().getBytes());
        outputStream.flush();
        // 响应内容
        StringBuilder sb = new StringBuilder();
        String str = null;
        while ((str = br.readLine()) != null) {
            sb.append(str + "n");
        }
        System.out.println(sb.toString());
        br.close();
        isr.close();
        outputStream.close();
    }

}


Tags:代理服务器   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
异步Rust:构建实时消息代理服务器
在本文中,我们将深入研究使用Rust构建实时消息代理服务器,展示其强大的并发特性。我们将使用Warp作为web服务器,并使用Tokio来管理异步任务。此外,我们将创建一个WebSocket客户...【详细内容】
2024-02-01  Search: 代理服务器  点击:(58)  评论:(0)  加入收藏
Java网络代理:探索代理服务器与虚拟网络技术
在现代互联网环境中,网络代理扮演着重要的角色。通过使用代理服务器或VPN(Virtual Private Network,虚拟私人网络)技术,用户可以实现匿名访问、数据加密和访问限制的绕过等功能。...【详细内容】
2023-11-30  Search: 代理服务器  点击:(206)  评论:(0)  加入收藏
反向代理服务器与CDN的协同作用
在当今互联网时代,网站和应用程序的性能和可用性对于用户体验至关重要。为了提高网站的访问速度和稳定性,反向代理服务器和CDN(内容分发网络)成为了不可或缺的组成部分。它们之...【详细内容】
2023-11-13  Search: 代理服务器  点击:(208)  评论:(0)  加入收藏
如何知道你是否使用了代理服务器?
什么是代理?代理是充当客户端和另一台服务器之间的中介的服务器或软件应用。它充当客户端和互联网之间的网关,允许用户访问在线资源,同时隐藏他们的身份、位置和其他个人信息...【详细内容】
2023-03-24  Search: 代理服务器  点击:(234)  评论:(0)  加入收藏
Java使用Netty框架自建DNS代理服务器教程
前言DNS协议作为着互联网客户端-服务器通信模式得第一关,在当下每天都有成千上亿上网记录产生得当今社会,其重要性自然不可言喻。在国内比较有名得DNS服务器有电信得114.114.1...【详细内容】
2023-02-09  Search: 代理服务器  点击:(149)  评论:(0)  加入收藏
Nginx是什么意思?Nginx作为反向代理服务器有什么作用?
在本文中,小编将给大家分析下Nginx是什么意思?Nginx作为反向代理服务器有什么作用?一、什么是Nginx?Nginx是一个开源的网络服务器,它是安装在我们的主机帐户上的软件,负责处理网站...【详细内容】
2022-11-17  Search: 代理服务器  点击:(358)  评论:(0)  加入收藏
Java,Socket,实现Socket4代理服务器,客户端用Socket4代理请求
前言Socket代理协议Socket代理协议有Socket4和Socket5两个版本,Socket4协议只支持TCP/IP,Socket代理协议只是单纯传递数据包,不关心具体协议和用法,所以速度快很快。Socket4代理...【详细内容】
2022-09-02  Search: 代理服务器  点击:(432)  评论:(0)  加入收藏
教你一分钟内网设置代理服务器上钉钉微信,通过socks5代理
在我们国内,微信和钉钉已经是我们日常工作中和个人社交的沟通软件,一天甚至几十个群的信息轰炸,完全用手机操作也不是很方便,经常会用到PC版的微信和钉钉。但在工作中,经常有用户...【详细内容】
2022-08-17  Search: 代理服务器  点击:(895)  评论:(0)  加入收藏
VPN跟vps和系统自带的代理服务器区别
VPN是什么? 虚拟与显示的交叉点 虚拟专用网络(VPN)数据传输VPN可以有效的隐藏用户隐私现在各大平台相继出来的IP地址显示 微信公众号也不例外 现在显示的是省份。代理服务器ip...【详细内容】
2022-06-16  Search: 代理服务器  点击:(393)  评论:(0)  加入收藏
基于frp实现将安卓手机变成代理服务器
公司有一个业务场景,需要利用分布在全国各地的一些手机作为代理服务器拿到这任务的我是一脸懵逼的,这tm都行。。。不过这让我让我想到之前用的内网穿透 frp内网穿透大致原理可...【详细内容】
2022-03-17  Search: 代理服务器  点击:(1184)  评论:(0)  加入收藏
▌简易百科推荐
为什么Nginx被称为“反向”代理呢?
Nginx(发音为"engine-x")是一款高性能、轻量级的开源Web服务器软件,也可用作反向代理服务器、负载均衡器和HTTP缓存。Nginx之所以有被称为“反向”代理,是因为它充当客户端设备...【详细内容】
2024-02-01  coderidea  微信公众号  Tags:Nginx   点击:(60)  评论:(0)  加入收藏
哪种服务器操作系统更好呢?
在当今的IT世界中,服务器操作系统扮演着至关重要的角色。它们是确保服务器能够高效、安全地运行的关键因素。然而,对于许多人来说,服务器操作系统的种类和特点可能是一个复杂的...【详细内容】
2024-01-30    简易百科  Tags:操作系统   点击:(76)  评论:(0)  加入收藏
什么是VPS服务器
VPS服务器是一种虚拟化技术,它将一台物理服务器划分为多个虚拟的独立服务器,每个虚拟服务器都可以拥有自己的操作系统、运行环境、应用程序等。这种技术使得每个虚拟服务器可...【详细内容】
2024-01-30    简易百科  Tags:VPS服务器   点击:(71)  评论:(0)  加入收藏
VPS服务器下载速度慢?这五招帮你提速
VPS服务器下载速度慢可能会让用户感到沮丧,尤其是对于需要大量下载和上传数据的用户。幸运的是,有一些方法可以帮助您提高VPS服务器的下载速度,使您的在线体验更加顺畅。在本文...【详细内容】
2024-01-30  IDC行业观察者    Tags:VPS服务器   点击:(58)  评论:(0)  加入收藏
美国VPS和英国VPS:地理位置对服务器性能的影响
在今天的数字时代,VPS已成为在线业务和网站托管的关键组成部分。然而,选择合适的VPS主机服务时,地理位置通常被忽视,尽管它对服务器性能有着重要的影响。本文将探讨美国VPS和英...【详细内容】
2024-01-26  IDC行业观察者    Tags:服务器   点击:(55)  评论:(0)  加入收藏
如何判断服务器所需带宽:基于业务需求和流量模式的关键考量
在选择服务器时,带宽是一个重要的考虑因素。带宽的大小直接影响到网站的加载速度和用户的访问体验。那么,如何判断服务器需要多大的带宽呢?本文将为你揭示这一关键问题的答案...【详细内容】
2024-01-26  源库科技    Tags:服务器   点击:(75)  评论:(0)  加入收藏
服务器内存空间及IO操作原理解析
服务器的内存空间分为内核空间和用户空间,而我们编写的程序通常在用户空间中运行。在进行读写操作时,我们直接操作的是用户缓冲区,而用户缓冲区的内容来自于内核缓冲区。这种内...【详细内容】
2024-01-23  王建立    Tags:服务器   点击:(44)  评论:(0)  加入收藏
如何在Java环境中安装Nginx?
1. 下载Nginx:首先,前往Nginx官方网站(https://nginx.org/en/download.html)下载新版本的Nginx。选择适合您操作系统的版本,通常有Windows、Linux和Mac等不同操作系统的版本可供...【详细内容】
2024-01-22  敲代码的小动    Tags:Nginx   点击:(63)  评论:(0)  加入收藏
服务器证书和SSL证书有啥区别?
在互联网经济时代,随着越来越多的信息以及合作都是从企业官网开始的,因此绝大多数企业都会为自己的网站配置SSL证书,以提高安全性。在接触SSL证书时,也有很多人称之为服务器证书...【详细内容】
2024-01-10  安信SSL证书    Tags:服务器证书   点击:(65)  评论:(0)  加入收藏
宝塔面板怎样部署java项目?
宝塔面板怎样部署java项目?在使用宝塔面板部署Java项目之前,需要确保已经安装了Java Development Kit (JDK)。接下来,将介绍如何使用宝塔面板来部署Java项目的步骤。步骤一:安装...【详细内容】
2024-01-09  西部数码    Tags:宝塔面板   点击:(115)  评论:(0)  加入收藏
站内最新
站内热门
站内头条