最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于SpringBoot實(shí)現(xiàn)一個(gè)JAVA代理HTTP/ WS的方法

 更新時(shí)間:2025年06月30日 09:35:30   作者:豬悟道  
本文主要介紹了基于SpringBoot實(shí)現(xiàn)一個(gè)JAVA代理HTTP/WS的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

1.環(huán)境信息

組件版本
JDK21
Springboot3.5.3
netty-all4.1.108.Final
smiley-http-proxy-servlet2.0

2.項(xiàng)目結(jié)構(gòu)和代碼

2.1 項(xiàng)目結(jié)構(gòu)

在這里插入圖片描述

2.2 依賴包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.demo</groupId>
    <artifactId>ProxyAPP</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-boot.version>3.5.3</spring-boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.108.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mitre.dsmiley.httpproxy</groupId>
            <artifactId>smiley-http-proxy-servlet</artifactId>
            <version>2.0</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>my-proxy</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2.3 代理配置類

啟動(dòng)類

package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.socket.config.annotation.EnableWebSocket;

/**
 * @author zhx && moon
 */
@EnableWebSocket
@SpringBootApplication
public class ProxyApp {

    public static void main(String[] args) {
        SpringApplication.run(ProxyApp.class, args);
    }
}

2.3.1 HTTP 代理

用于 HTTP 代理轉(zhuǎn)發(fā)

package com.demo.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author zhx && moon
 * @Since 1.8
 * @Date 2024-12-12 PM 2:27
 */
@Configuration
public class ProxyConfig {

    @Value("${proxy.target.url}")
    private String targetUri;

    @Bean
    public ServletRegistrationBean proxyServlet() {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new URITemplateProxyServletSUB());
        servlet.addUrlMappings("/*");
        servlet.addInitParameter("targetUri", targetUri);
        return servlet;
    }

}

轉(zhuǎn)發(fā)參數(shù)處理類,如設(shè)置頭、添加權(quán)限控制、轉(zhuǎn)發(fā)記錄等操作

package com.demo.config;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.config.SocketConfig;
import org.mitre.dsmiley.httpproxy.URITemplateProxyServlet;

import java.io.IOException;

/**
 * @Author zhx && moon
 * @Since 1.8
 * @Date 2025-02-26 PM 5:44
 */
public class URITemplateProxyServletSUB extends URITemplateProxyServlet {

    @Override
    protected SocketConfig buildSocketConfig() {
        return SocketConfig.custom()
                .setSoTimeout(3600 * 1000)
                .setSoKeepAlive(true)
                .build();
    }

    @Override
    protected HttpResponse doExecute(HttpServletRequest servletRequest, HttpServletResponse servletResponse, HttpRequest proxyRequest) throws IOException {
        //重置請(qǐng)求頭
        proxyRequest.setHeader("Connection", "keep-alive");
        //調(diào)用父方法
        return super.doExecute(servletRequest, servletResponse, proxyRequest);
    }

}

2.3.2 WS 代理

基于 NETTY 實(shí)現(xiàn) WS 協(xié)議,完成轉(zhuǎn)發(fā)

NETTY 服務(wù)端定義

package com.demo.ws;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Author zhx && moon
 * @Since 1.8
 * @Date 2025-02-28 PM 3:28
 */
@Component
public class WebSocketServer {

    Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    @Value("${server.websocket.is-ws:true}")
    private boolean isWS;

    @Value("${server.websocket.port}")
    private int websocketPort;

    @Value("${server.websocket.path}")
    private String websocketPath;

    @Value("${server.websocket.remote-uri}")
    private String remoteUri;

    /**
     * 初始化本地 WS 服務(wù)
     * @throws Exception
     */
    @PostConstruct
    private void init() throws Exception {
        if (isWS){
            // 創(chuàng)建線程
            Thread thread = new Thread(() -> {
                try {
                    run();
                } catch (Exception e) {
                    logger.error("WebSocketServer init error: {}", e.getMessage());
                }
            });
            // 啟動(dòng)線程
            thread.setDaemon(true);
            thread.setName("WebSocketServer-Proxy");
            thread.start();
        }
    }

    /**
     * 構(gòu)建本地 WS 服務(wù)
     * @throws Exception
     */
    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 添加 HTTP 編解碼器
                            pipeline.addLast(new HttpServerCodec());
                            // 添加 HTTP 內(nèi)容聚合器
                            pipeline.addLast(new HttpObjectAggregator(65536));
                            // 添加 WebSocket 處理器
                            pipeline.addLast(new WebSocketServerProtocolHandler(websocketPath));
                            // 添加自定義處理器
                            pipeline.addLast(new WebSocketProxyHandler(remoteUri));
                        }
                    });

            // 綁定端口并啟動(dòng)服務(wù)器
            Channel channel = bootstrap.bind(websocketPort).sync().channel();
            // 輸出啟動(dòng)信息
            logger.info("webSocket server started on port {}", websocketPort);
            // 等待服務(wù)器 socket 關(guān)閉
            channel.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

WS 處理器

package com.demo.ws;

import com.demo.ws.remote.WebSocketClientConnector;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Author zhx && moon
 * @Since 1.8
 * @Date 2025-02-28 PM 3:29
 */
public class WebSocketProxyHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    Logger logger = LoggerFactory.getLogger(WebSocketProxyHandler.class);

    /**
     * 遠(yuǎn)端 WS 服務(wù)連接器
     */
    private final WebSocketClientConnector connector;

    /**
     * 遠(yuǎn)端 WS 服務(wù)會(huì)話
     */
    private WebSocketSession session;

    private AtomicReference<ChannelHandlerContext> ctx = new AtomicReference<>();

    /**
     * 構(gòu)造 WS 消息處理器
     * @param uri
     */
    public WebSocketProxyHandler(String uri) {
        //添加目標(biāo)服務(wù)
        connector = new WebSocketClientConnector(uri, ctx);
        //建立連接
        try {
            connector.connect();
        } catch (Exception e) {
            logger.info("connect to remote server failed", e);
        }
    }

    /**
     * 處理接收到的消息,并轉(zhuǎn)發(fā)到遠(yuǎn)端服務(wù)
     * @param ctx
     * @param frame
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        if (frame instanceof TextWebSocketFrame) {
            // 處理文本幀
            String text = ((TextWebSocketFrame) frame).text();
            logger.info("proxy received text message: {}", text);
            // 獲取 SESSION
            if (Objects.isNull(this.ctx.get())){
                this.ctx.set(ctx);
                this.session = connector.getRemoteSession();
            }
            // 轉(zhuǎn)發(fā)到遠(yuǎn)端服務(wù)
            if (Objects.nonNull(this.session)){
                session.sendMessage(new TextMessage(text));
            } else {
                this.ctx.get().writeAndFlush(new TextWebSocketFrame("remote server connection failed!"));
            }
        } else if (frame instanceof BinaryWebSocketFrame) {
            // 處理二進(jìn)制幀
            logger.info("received binary message");
        } else if (frame instanceof CloseWebSocketFrame) {
            // 處理關(guān)閉幀
            ctx.close();
        }
    }

    /**
     * 鏈接關(guān)閉處理
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 關(guān)閉遠(yuǎn)端連接
        connector.close();
        // 記錄日志
        logger.info("client disconnected: {}", ctx.channel().remoteAddress());
    }

    /**
     * 異常處理
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.error("proxy exception caught", cause);
        ctx.close();
    }

}

NETTY 客戶端,用于連接第三方 WS

package com.demo.ws.remote;

import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.WebSocketConnectionManager;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Author zhx && moon
 * @Since 1.8
 * @Date 2025-02-28 PM 6:27
 */
public class WebSocketClientConnector {

    Logger logger = LoggerFactory.getLogger(WebSocketClientConnector.class);

    private final String uri;
    private final AtomicReference<ChannelHandlerContext> ctx;
    private final WebSocketClient webSocketClient = new StandardWebSocketClient();
    private final WebSocketClientHandler webSocketClientHandler;
    private WebSocketConnectionManager connectionManager;

    /**
     * 構(gòu)建訪問(wèn)遠(yuǎn)端 WS 服務(wù)的本地客戶端
     * @param uri
     * @param ctx
     */
    public WebSocketClientConnector(String uri, AtomicReference<ChannelHandlerContext> ctx) {
        this.uri = uri;
        this.ctx = ctx;
        this.webSocketClientHandler = new WebSocketClientHandler(ctx);
    }

    /**
     * 連接遠(yuǎn)端服務(wù)
     */
    public void connect() {
        // 創(chuàng)建 WebSocket 連接管理器
        this.connectionManager = new WebSocketConnectionManager(
                webSocketClient,
                webSocketClientHandler,
                uri
        );
        // 啟動(dòng)連接
        this.connectionManager.start();
        // 記錄日志
        logger.info("web socket client started and connecting to: {}", uri);
    }

    /**
     * 關(guān)閉連接
     */
    public void close(){
        this.connectionManager.stop();
    }

    /**
     * 獲取與遠(yuǎn)端的會(huì)話 SESSION
     * @return
     */
    public WebSocketSession getRemoteSession(){
        if (Objects.nonNull(webSocketClientHandler.getSession())) {
            // 發(fā)送一條消息到服務(wù)器
            return webSocketClientHandler.getSession();
        }
        return null;
    }
}

WS 客戶端處理器

package com.demo.ws.remote;

import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Author zhx && moon
 * @Since 1.8
 * @Date 2025-02-28 PM 6:06
 */
public class WebSocketClientHandler extends TextWebSocketHandler {

    Logger logger = LoggerFactory.getLogger(WebSocketClientHandler.class);

    /**
     * WebSocket 會(huì)話
     */
    private WebSocketSession session;

    /**
     * 本地 WS 服務(wù)的 NIO 通道上下文
     */
    private final AtomicReference<ChannelHandlerContext> ctx;

    /**
     * 構(gòu)造 WS 客戶端消息處理器, 獲取本第 WS 服務(wù)的 NIO 通道上下文
     * @param ctx
     */
    public WebSocketClientHandler(AtomicReference<ChannelHandlerContext> ctx) {
        this.ctx = ctx;
    }

    /**
     * 建立連接后操作
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 連接建立成功
        logger.info("connected to web socket server: {}", session.getUri());
        // Save the session
        this.session = session;
    }

    /**
     * 消息處理,接收遠(yuǎn)端服務(wù)
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 處理從服務(wù)器收到的消息
        logger.info("received message: {}", message.getPayload());
        // 轉(zhuǎn)發(fā)到本地 WS 服務(wù),并回寫(xiě)給其他連接
        if (Objects.nonNull(this.ctx.get())){
            this.ctx.get().writeAndFlush(new TextWebSocketFrame(message.getPayload()));
        }
    }

    /**
     * 連接關(guān)閉后處理
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        logger.info("disconnected from web socket server: {}", status.getReason());
    }

    /**
     * 報(bào)錯(cuò)處理
     * @param session
     * @param exception
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        logger.error("web socket transport error: ", exception);
    }

    /**
     * 獲取到遠(yuǎn)端服務(wù)的 WebSocket 會(huì)話
     * @return
     */
    public WebSocketSession getSession(){
        return this.session;
    }
}

2.4 YML 配置文件

server:
  port: 8081
  websocket:
    is-ws: true
    port: 8082
    path: /ws/conn
    remote-uri: ws://127.0.0.1:8080/ws/conn

proxy.target.url: http://127.0.0.1:8080

3.測(cè)試用第三方服務(wù)示例

3.1 項(xiàng)目結(jié)構(gòu)

在這里插入圖片描述

依賴包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>WebTest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-boot.version>3.5.3</spring-boot.version>
    </properties>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>my-proxy</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

3.2 代碼

啟動(dòng)類

package com.demo;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.socket.config.annotation.EnableWebSocket;

/**
 * @author zhx && moon
 */
@EnableWebSocket
@SpringBootApplication
public class ProxyApp {

    public static void main(String[] args) {
        SpringApplication.run(ProxyApp.class, args);
    }
}

HTTP 接口類

package org.example.controller;

import org.springframework.web.bind.annotation.*;

/**
 * @author zhuwd && moon
 * @Description
 * @create 2025-06-29 12:41
 */
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/get")
    public String get(@RequestParam("params") String params){
        return "Hello " + params;
    }

    @PostMapping("/post")
    public String post(@RequestBody String params){
        return "Hello " + params;
    }

}

WS 配置

package org.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * @author zhuwd && moon
 * @Description
 * @create 2025-06-29 12:58
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler(), "/ws/conn")
                .setAllowedOrigins("*"); // 允許所有來(lái)源
    }

    public ServerWebSocketHandler webSocketHandler() {
        return new ServerWebSocketHandler();
    }
}

WS 處理器,將消息轉(zhuǎn)大寫(xiě)返回

package org.example.config;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhuwd && moon
 * @Description
 * @create 2025-06-29 14:31
 */
public class ServerWebSocketHandler extends TextWebSocketHandler {

    // 存儲(chǔ)所有活動(dòng)會(huì)話
    private static final ConcurrentHashMap<String, WebSocketSession> sessions = new ConcurrentHashMap<>();

    // 消息計(jì)數(shù)
    private static int messageCount = 0;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.put(session.getId(), session);
        System.out.println("連接建立: " + session.getId());

        // 向新連接的客戶端發(fā)送歡迎消息
        session.sendMessage(new TextMessage(
                "{\"type\": \"system\", \"message\": \"連接服務(wù)器成功!發(fā)送 'broadcast' 可以廣播消息\"}"
        ));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        System.out.println("收到消息 [" + session.getId() + "]: " + payload);
        messageCount++;

        // 處理特殊指令
        if ("broadcast".equalsIgnoreCase(payload)) {
            broadcastMessage("來(lái)自服務(wù)器的廣播消息 (" + messageCount + ")");
        } else {
            // 默認(rèn)回顯消息
            String response = "{\"type\": \"echo\", \"original\": \"" +
                    escapeJson(payload) +
                    "\", \"modified\": \"" +
                    payload.toUpperCase() +
                    "\"}";
            session.sendMessage(new TextMessage(response));
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessions.remove(session.getId());
        System.out.println("連接關(guān)閉: " + session.getId() + ", 狀態(tài): " + status);
    }

    // 廣播消息給所有客戶端
    private void broadcastMessage(String message) throws IOException {
        TextMessage msg = new TextMessage("{\"type\": \"broadcast\", \"message\": \"" + escapeJson(message) + "\"}");
        for (WebSocketSession session : sessions.values()) {
            if (session.isOpen()) {
                session.sendMessage(msg);
            }
        }
    }

    // 處理JSON轉(zhuǎn)義
    private String escapeJson(String str) {
        return str.replace("\\", "\\\\")
                .replace("\"", "\\\"")
                .replace("\b", "\\b")
                .replace("\f", "\\f")
                .replace("\n", "\\n")
                .replace("\r", "\\r")
                .replace("\t", "\\t");
    }
}

4.測(cè)試

HTML 實(shí)現(xiàn)一個(gè) WS 客戶端

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket測(cè)試工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #1a2a6c, #2b5876, #4e4376);
            color: #fff;
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        header {
            text-align: center;
            padding: 20px 0;
            margin-bottom: 30px;
        }
        
        h1 {
            font-size: 2.5rem;
            background: linear-gradient(to right, #00d2ff, #3a7bd5);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            text-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        
        .subtitle {
            color: #bbd4ff;
            margin-top: 10px;
            font-weight: 300;
        }
        
        .panel {
            background: rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(10px);
            border-radius: 15px;
            padding: 25px;
            margin-bottom: 30px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
            border: 1px solid rgba(255, 255, 255, 0.1);
        }
        
        .connection-panel {
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
        }
        
        .connection-status {
            display: flex;
            align-items: center;
        }
        
        .status-indicator {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            margin-right: 10px;
            background-color: #6c757d;
        }
        
        .status-connected {
            background-color: #28a745;
            box-shadow: 0 0 10px #28a745;
        }
        
        .status-disconnected {
            background-color: #dc3545;
        }
        
        .connection-controls {
            display: flex;
            gap: 15px;
        }
        
        input {
            padding: 12px 15px;
            border: none;
            border-radius: 8px;
            background: rgba(255, 255, 255, 0.1);
            color: #fff;
            font-size: 1rem;
            width: 100%;
            outline: none;
        }
        
        input:focus {
            background: rgba(255, 255, 255, 0.15);
            box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.25);
        }
        
        button {
            padding: 12px 25px;
            border: none;
            border-radius: 8px;
            background: linear-gradient(to right, #2193b0, #6dd5ed);
            color: white;
            font-size: 1rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            outline: none;
        }
        
        button:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
        }
        
        button:active {
            transform: translateY(0);
        }
        
        button:disabled {
            background: linear-gradient(to right, #5a6268, #6c757d);
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }
        
        .disconnect-btn {
            background: linear-gradient(to right, #f85032, #e73827);
        }
        
        .form-group {
            margin-bottom: 20px;
        }
        
        label {
            display: block;
            margin-bottom: 8px;
            color: #b3d7ff;
        }
        
        .log-container {
            height: 400px;
            overflow-y: auto;
            background: rgba(0, 0, 0, 0.2);
            border-radius: 10px;
            padding: 15px;
            font-family: monospace;
            font-size: 0.9rem;
            margin-bottom: 15px;
        }
        
        .log-entry {
            margin-bottom: 8px;
            padding: 8px;
            border-radius: 5px;
            background: rgba(0, 0, 0, 0.1);
        }
        
        .incoming {
            border-left: 4px solid #4db8ff;
        }
        
        .outgoing {
            border-left: 4px solid #28a745;
        }
        
        .system {
            border-left: 4px solid #ffc107;
        }
        
        .error {
            border-left: 4px solid #dc3545;
            color: #ffabab;
        }
        
        .timestamp {
            color: #999;
            font-size: 0.8rem;
            margin-right: 10px;
        }
        
        .flex-container {
            display: flex;
            gap: 20px;
        }
        
        .flex-container > div {
            flex: 1;
        }
        
        .info-container {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 15px;
            margin-top: 20px;
        }
        
        .info-box {
            background: rgba(0, 0, 0, 0.15);
            padding: 15px;
            border-radius: 10px;
        }
        
        .info-box h3 {
            margin-bottom: 10px;
            color: #80bdff;
            font-weight: 500;
        }
        
        .code-example {
            background: rgba(0, 0, 0, 0.2);
            padding: 15px;
            border-radius: 10px;
            margin-top: 30px;
            font-family: monospace;
            font-size: 0.9rem;
            color: #e9ecef;
            overflow-x: auto;
        }
        
        @media (max-width: 768px) {
            .flex-container {
                flex-direction: column;
            }
            
            .connection-controls {
                width: 100%;
                margin-top: 15px;
            }
            
            .connection-panel {
                flex-direction: column;
                align-items: flex-start;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>WebSocket 測(cè)試工具</h1>
            <p class="subtitle">測(cè)試、調(diào)試和監(jiān)控您的WebSocket連接</p>
        </header>
        
        <div class="panel connection-panel">
            <div class="connection-status">
                <div class="status-indicator" id="statusIndicator"></div>
                <span id="statusText">未連接</span>
            </div>
            <div class="connection-controls">
                <input type="text" id="serverUrl" placeholder="ws://127.0.0.1:8082/ws/conn" value="ws://127.0.0.1:8082/ws/conn">
                <button id="connectBtn" class="connect-btn">連接</button>
                <button id="disconnectBtn" class="disconnect-btn" disabled>斷開(kāi)</button>
            </div>
        </div>
        
        <div class="flex-container">
            <div>
                <div class="panel">
                    <h2>發(fā)送消息</h2>
                    <div class="form-group">
                        <label for="messageInput">輸入要發(fā)送的消息:</label>
                        <input type="text" id="messageInput" placeholder="輸入消息內(nèi)容..." disabled>
                    </div>
                    <button id="sendBtn" disabled>發(fā)送消息</button>
                </div>
                
                <div class="panel">
                    <h2>連接信息</h2>
                    <div class="info-container">
                        <div class="info-box">
                            <h3>當(dāng)前狀態(tài)</h3>
                            <p id="currentState">未連接</p>
                        </div>
                        <div class="info-box">
                            <h3>傳輸協(xié)議</h3>
                            <p id="protocol">-</p>
                        </div>
                        <div class="info-box">
                            <h3>消息計(jì)數(shù)</h3>
                            <p id="messageCount">已發(fā)送: 0 | 已接收: 0</p>
                        </div>
                    </div>
                </div>
            </div>
            
            <div>
                <div class="panel">
                    <h2>通信日志</h2>
                    <div class="log-container" id="logContainer"></div>
                    <button id="clearLogBtn">清除日志</button>
                </div>
            </div>
        </div>
        
        <div class="panel code-example">
            <h3>示例服務(wù)器URL:</h3>
            <p>本地測(cè)試服務(wù)器: ws://localhost:8082/ws/conn</p>
            <p>SSL測(cè)試服務(wù)器: wss://websocket-echo.com</p>
        </div>
    </div>
    
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 頁(yè)面元素引用
            const statusIndicator = document.getElementById('statusIndicator');
            const statusText = document.getElementById('statusText');
            const serverUrl = document.getElementById('serverUrl');
            const connectBtn = document.getElementById('connectBtn');
            const disconnectBtn = document.getElementById('disconnectBtn');
            const messageInput = document.getElementById('messageInput');
            const sendBtn = document.getElementById('sendBtn');
            const logContainer = document.getElementById('logContainer');
            const clearLogBtn = document.getElementById('clearLogBtn');
            const currentState = document.getElementById('currentState');
            const protocol = document.getElementById('protocol');
            const messageCount = document.getElementById('messageCount');
            
            // 狀態(tài)變量
            let socket = null;
            let messageCounter = { sent: 0, received: 0 };
            
            // 連接狀態(tài)更新函數(shù)
            function updateConnectionStatus(connected) {
                if (connected) {
                    statusIndicator.className = 'status-indicator status-connected';
                    statusText.textContent = `已連接到: ${socket.url}`;
                    currentState.textContent = "已連接";
                    
                    // 啟用發(fā)送控件
                    messageInput.disabled = false;
                    sendBtn.disabled = false;
                    connectBtn.disabled = true;
                    disconnectBtn.disabled = false;
                } else {
                    statusIndicator.className = 'status-indicator status-disconnected';
                    statusText.textContent = '未連接';
                    currentState.textContent = "未連接";
                    
                    // 禁用發(fā)送控件
                    messageInput.disabled = true;
                    sendBtn.disabled = true;
                    connectBtn.disabled = false;
                    disconnectBtn.disabled = true;
                }
            }
            
            // 日志函數(shù)
            function addLog(type, content) {
                const logEntry = document.createElement('div');
                logEntry.className = `log-entry ${type}`;
                
                const now = new Date();
                const timestamp = `[${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}]`;
                
                logEntry.innerHTML = `<span class="timestamp">${timestamp}</span> ${content}`;
                
                logContainer.prepend(logEntry);
            }
            
            // 連接WebSocket
            function connect() {
                const url = serverUrl.value.trim();
                
                if (!url) {
                    alert('請(qǐng)輸入有效的WebSocket服務(wù)器URL');
                    return;
                }
                
                try {
                    socket = new WebSocket(url);
                    
                    // 連接打開(kāi)
                    socket.addEventListener('open', (event) => {
                        updateConnectionStatus(true);
                        protocol.textContent = "WebSocket";
                        addLog('system', `連接已建立于: ${url}`);
                    });
                    
                    // 接收消息
                    socket.addEventListener('message', (event) => {
                        messageCounter.received++;
                        updateMessageCount();
                        addLog('incoming', `接收: ${event.data}`);
                    });
                    
                    // 錯(cuò)誤處理
                    socket.addEventListener('error', (error) => {
                        addLog('error', `錯(cuò)誤: ${error.message || '未知錯(cuò)誤'}`);
                    });
                    
                    // 連接關(guān)閉
                    socket.addEventListener('close', (event) => {
                        updateConnectionStatus(false);
                        addLog('system', `連接關(guān)閉 (代碼: ${event.code}, 原因: ${event.reason || '無(wú)'})`);
                        socket = null;
                    });
                    
                } catch (error) {
                    updateConnectionStatus(false);
                    addLog('error', `連接失敗: ${error.message}`);
                }
            }
            
            // 斷開(kāi)WebSocket連接
            function disconnect() {
                if (socket) {
                    socket.close();
                    socket = null;
                }
            }
            
            // 發(fā)送消息
            function sendMessage() {
                if (!socket || socket.readyState !== WebSocket.OPEN) {
                    addLog('error', '錯(cuò)誤: 連接未就緒,無(wú)法發(fā)送消息');
                    return;
                }
                
                const message = messageInput.value.trim();
                if (!message) {
                    alert('請(qǐng)輸入要發(fā)送的消息');
                    return;
                }
                
                try {
                    socket.send(message);
                    messageCounter.sent++;
                    updateMessageCount();
                    addLog('outgoing', `發(fā)送: ${message}`);
                    messageInput.value = '';
                } catch (error) {
                    addLog('error', `發(fā)送失敗: ${error.message}`);
                }
            }
            
            // 更新消息計(jì)數(shù)顯示
            function updateMessageCount() {
                messageCount.textContent = `已發(fā)送: ${messageCounter.sent} | 已接收: ${messageCounter.received}`;
            }
            
            // 清除日志
            function clearLog() {
                logContainer.innerHTML = '';
            }
            
            // 設(shè)置事件監(jiān)聽(tīng)器
            connectBtn.addEventListener('click', connect);
            disconnectBtn.addEventListener('click', disconnect);
            sendBtn.addEventListener('click', sendMessage);
            clearLogBtn.addEventListener('click', clearLog);
            
            // 支持按Enter鍵發(fā)送消息
            messageInput.addEventListener('keypress', (event) => {
                if (event.key === 'Enter' && !sendBtn.disabled) {
                    sendMessage();
                }
            });
            
            // 初始化日志
            addLog('system', 'WebSocket測(cè)試工具已準(zhǔn)備就緒');
            addLog('system', '請(qǐng)連接到一個(gè)WebSocket服務(wù)器開(kāi)始測(cè)試');
        });
    </script>
</body>
</html>

4.1 HTTP 和 WS 測(cè)試

分別啟動(dòng)測(cè)試服務(wù)和代理服務(wù)

測(cè)試服務(wù)

在這里插入圖片描述

代理服務(wù)

在這里插入圖片描述

HTTP 測(cè)試,8081 轉(zhuǎn)發(fā)到 8080

Get

在這里插入圖片描述

Post

在這里插入圖片描述

WS 測(cè)試 8982 轉(zhuǎn)發(fā)到 8080

在這里插入圖片描述

到此這篇關(guān)于基于SpringBoot實(shí)現(xiàn)一個(gè)JAVA代理HTTP/ WS的方法的文章就介紹到這了,更多相關(guān)SpringBoot 代理HTTP/ WS內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

您可能感興趣的文章:

相關(guān)文章

最新評(píng)論

门头沟区| 临澧县| 招远市| 黔南| 周口市| 高阳县| 嘉善县| 抚松县| 包头市| 马鞍山市| 竹山县| 马鞍山市| 安泽县| 阳泉市| 错那县| 光泽县| 卓尼县| 崇阳县| 马公市| 平乐县| 本溪市| 平舆县| 古田县| 普格县| 四子王旗| 乐安县| 旬阳县| 阳城县| 老河口市| 闸北区| 姜堰市| 阳东县| 启东市| 浏阳市| 武强县| 瓦房店市| 鄂伦春自治旗| 调兵山市| 宁安市| 济南市| 普格县|