Netty服务端支持wss请求并处理-通过netty书写wss客户端进行链接

1:本文包含的内容

1:java版本:通过netty实现支持ssl,wss方式去链接的服务端
2:java版本:书写支持通过wss方式去链接服务端的客户端
3:js版本:通过wss和ws去链接服务端的客户端

2:使用的jar包

 <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.36.Final</version>
</dependency>

3:java版本服务端,支持wss链接

  1. MyChannelHandlerPool
package com.example.fallrainboot.netty;

import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * @description: 通道组池,管理所有websocket连接
 * @author: fallrain
 * @time: 2022/7/7/007
 */
public class MyChannelHandlerPool {
    public MyChannelHandlerPool(){}

    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}
  1. MyWebSocketHandler
package com.example.fallrainboot.netty;

import com.alibaba.fastjson.JSON;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @description:
 * @author: fallrain
 * @time: 2022/7/7/007
 */
public class MyWebSocketHandler  extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private final String USER = "user";

    private final AttributeKey<String> key = AttributeKey.valueOf(USER);


    /**
     * 有客户端连接服务器会触发此函数
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("与客户端建立连接,通道开启!");
        System.out.println(ctx);

        //添加到channelGroup通道组
        MyChannelHandlerPool.channelGroup.add(ctx.channel());
    }

    /**
     * 有客户端终止连接服务器会触发此函数
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("与客户端断开连接,通道关闭!");
        //添加到channelGroup 通道组
        MyChannelHandlerPool.channelGroup.remove(ctx.channel());
    }

    /**
     * 有客户端发消息会触发此函数
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //首次连接是FullHttpRequest,处理参数 by zhengkai.blog.csdn.net
        if (null != msg && msg instanceof FullHttpRequest) {
            FullHttpRequest request = (FullHttpRequest) msg;
            String uri = request.uri();

            Map<String, String> paramMap=getUrlParams(uri);
            System.out.println("接收到的参数是:"+ JSON.toJSONString(paramMap));

            online(paramMap.get("uid"), ctx.channel());
            //如果url包含参数,需要处理
            if(uri.contains("?")){
                String newUri=uri.substring(0,uri.indexOf("?"));
                System.out.println(newUri);
                request.setUri(newUri);
            }

        }else if(msg instanceof TextWebSocketFrame){
            //正常的TEXT消息类型
            TextWebSocketFrame frame=(TextWebSocketFrame)msg;
            //以下是每次收到消息之后回给client的代码,如果不需要回复则可以注释了。
            String uid = ctx.channel().attr(key).get();
            System.out.println("客户端收到"+ uid +"服务器数据:" +frame.text());
            System.out.println("read0: " + frame.text());
            List<Channel> channelList = getChannelByName(uid);
            if (channelList.size() <= 0) {
                System.out.println("用户" + uid + "不在线!");
            }
            channelList.forEach(channel -> channel.writeAndFlush(new TextWebSocketFrame(frame.text())));
            //批量发送
            //sendAllMessage(frame.text());
        }
        super.channelRead(ctx, msg);
    }

    private void sendOneMessage(String uid, String message){
        //收到信息后,单发给channel
        List<Channel> channelList = getChannelByName(uid);
        channelList.forEach(channel -> channel.writeAndFlush(new TextWebSocketFrame(message)));
    }

    /**
     * 批量发送
     * @param message
     */
    private void sendAllMessage(String message){
        //收到信息后,群发给所有channel
        MyChannelHandlerPool.channelGroup.writeAndFlush( new TextWebSocketFrame(message));
    }

    /**
     * 根据用户id查找channel
     *
     * @param name
     * @return
     */
    public List<Channel> getChannelByName(String name) {
        return MyChannelHandlerPool.channelGroup.stream().filter(channel -> channel.attr(key).get().equals(name))
                .collect(Collectors.toList());
    }

    /**
     * 上线一个用户
     *
     * @param channel
     * @param userId
     */
    private void online(String userId, Channel channel) {
        // 保存channel通道的附带信息,以用户的uid为标识
//        channel.attr(key).set(userId);
        channel.attr(key).setIfAbsent(userId);
        MyChannelHandlerPool.channelGroup.add(channel);
    }



    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {

    }

    private static Map getUrlParams(String url){
        Map<String,String> map = new HashMap<>();
        url = url.replace("?",";");
        if (!url.contains(";")){
            return map;
        }
        if (url.split(";").length > 0){
            String[] arr = url.split(";")[1].split("&");
            for (String s : arr){
                String key = s.split("=")[0];
                String value = s.split("=")[1];
                map.put(key,value);
            }
            return  map;

        }else{
            return map;
        }
    }


}
  1. NettyServer
package com.example.fallrainboot.netty;

import com.example.fallrainboot.util.SSLContextUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.util.concurrent.TimeUnit;
/**
 * @description: NettyServer Netty服务器配置
 * @author: fallrain
 * @time: 2022/7/7/007
 */
public class NettyServer {
    private final int port;

    public NettyServer(int port) {
        this.port = port;
    }

    public void start() throws Exception {
        //配置服务端的NIO线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();

        EventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap sb = new ServerBootstrap();
            sb.option(ChannelOption.SO_BACKLOG, 1024); //服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝
            sb.group(group, bossGroup) // 绑定线程池
                    .channel(NioServerSocketChannel.class) // 指定使用的channel
                    .localAddress(this.port)// 绑定监听端口
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 绑定客户端连接时候触发操作
                        //服务端初始化,客户端与服务器端连接一旦创建,这个类中方法就会被回调,设置出站编码器和入站解码器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {

//                            // SSL处理器
//                            SSLContext sslContext = SSLContextUtil.createSSLContext("JKS", "D:\\WYProject\\fallrainboot\\src\\main\\resources\\tomcat\\8092720_www.fallrain.vip.pfx","fNHszHKD");
                            SSLContext sslContext = SSLContextUtil.createSSLContext("JKS", "D:\\WYProject\\fallrainboot\\src\\main\\resources\\jks\\8092720_www.fallrain.vip.jks","VBQp6Hxn");
//                            SSLContext sslContext = SSLContextUtil.createSSLContext("JKS", "D:\\WYProject\\fallrainboot\\demo.liukun.com.keystore","123456");
//                            SSLContext sslContext = SSLContextUtil.createSSLContext("JKS", "D:\\WYProject\\JgServer\\src\\main\\resources\\wss.jks","netty123");
                            SSLEngine sslEngine = sslContext.createSSLEngine();
                            sslEngine.setNeedClientAuth(false);
                            sslEngine.setUseClientMode(false);
                            ch.pipeline().addFirst("ssl", new SslHandler(sslEngine));


//                            System.out.println("收到新连接");
//                          //websocket协议本身是基于http协议的,所以这边也要使用http解编码器
                            ch.pipeline().addLast(new HttpServerCodec());
                            //以块的方式来写的处理器
                            ch.pipeline().addLast(new ChunkedWriteHandler());
                            ch.pipeline().addLast(new HttpObjectAggregator(8192));
                            //参数处理
                            ch.pipeline().addLast(new MyWebSocketHandler());
                            ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws", null, true, 65536 * 10));
                        }
                    });
//                    .childOption(ChannelOption.SO_KEEPALIVE, true);//保持长连接,2小时无数据激活心跳机制
            // 绑定端口,开始接收进来的连接
//            ChannelFuture future = sb.bind(this.port).sync();
            ChannelFuture cf = sb.bind().sync(); // 服务器异步创建绑定
            System.out.println(NettyServer.class + " 启动正在监听: " + cf.channel().localAddress());
            //关闭channel和块,直到它被关闭
            cf.channel().closeFuture().sync(); // 关闭服务器通道
        } finally {
            group.shutdownGracefully().sync(); // 释放线程池资源
            bossGroup.shutdownGracefully().sync();
        }
    }

}
  1. SSLContextUtil
package com.example.fallrainboot.util;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
/**
 * @description: jks证书处理工具类
 * @author: fallrain
 * @time: 2022/7/8/008
 */
public class SSLContextUtil {

    private static volatile SSLContext sslContext = null;

    /**
     * type是PKCS12、path是pfx文件路径、password是pfx对应的密码
     * @param type
     * @param path
     * @param password
     * @return
     * @throws Exception
     */
    public static SSLContext createSSLContext(String type ,String path ,String password) throws Exception {
        if(null == sslContext){
            synchronized (SSLContextUtil.class) {
                if(null == sslContext){
                    // 支持JKS、PKCS12(我们项目中用的是阿里云免费申请的证书,下载tomcat解压后的pfx文件,对应PKCS12)
                    KeyStore ks = KeyStore.getInstance(type);
                    // 证书存放地址
                    InputStream ksInputStream = new FileInputStream(path);
                    ks.load(ksInputStream, password.toCharArray());
                    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
                    kmf.init(ks, password.toCharArray());
                    sslContext = SSLContext.getInstance("TLS");
                    sslContext.init(kmf.getKeyManagers(), null, null);
                }
            }
        }
        return sslContext;
    }




}

4:java版本客户端,支持wss去链接服务端

  1. ClientHandler
package com.example.fallrainboot.nettyClient;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;


//客户端业务处理类
@Slf4j
public class ClientHandler   extends SimpleChannelInboundHandler<Object> {

    private ChannelHandlerContext channel;
    private  WebSocketClientHandshaker handshaker;
    private  ChannelPromise handshakeFuture;

    /**
     * 当客户端主动链接服务端的链接后,调用此方法
     *
     * @param channelHandlerContext ChannelHandlerContext
     */
    @Override
    public void channelActive(ChannelHandlerContext channelHandlerContext) {
        System.out.println("客户端Active .....");
        handlerAdded(channelHandlerContext);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        System.out.println("\n\t⌜⎓⎓⎓⎓⎓⎓exception⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" +
                cause.getMessage());
        ctx.close();
    }

    public void setHandshaker(WebSocketClientHandshaker handshaker) {
        this.handshaker = handshaker;
    }
    public void handlerAdded(ChannelHandlerContext ctx) {
        this.handshakeFuture = ctx.newPromise();
    }
    public ChannelFuture handshakeFuture() {
        return this.handshakeFuture;
    }

//    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
//        System.out.println("channelRead0");
//        Channel ch = ctx.channel();
//        if (!handshaker.isHandshakeComplete()) {
//            try {
//                handshaker.finishHandshake(ch, (FullHttpResponse) msg);
//                System.out.println("WebSocket Client connected!");
//                handshakeFuture.setSuccess();
//            } catch (WebSocketHandshakeException e) {
//                System.out.println("WebSocket Client failed to connect");
//                handshakeFuture.setFailure(e);
//
//            }
//            return;
//        }
//        if (msg instanceof FullHttpResponse) {
//            FullHttpResponse response = (FullHttpResponse) msg;
//            throw new IllegalStateException("Unexpected FullHttpResponse (getStatus=" + response.getStatus() + ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')');
//        }
//
//        WebSocketFrame frame = (WebSocketFrame) msg;
//        if (frame instanceof TextWebSocketFrame) {
//            TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;            // resposnse(ctx, frame);
//            channel.writeAndFlush(textFrame.text());
//            System.out.println("WebSocket Client received message: " + textFrame.text());
//        } else if (frame instanceof PongWebSocketFrame) {
//            System.out.println("WebSocket Client received pong");
//        } else if (frame instanceof CloseWebSocketFrame) {
//            System.out.println("WebSocket Client received closing");
//            ch.close();
//        }
//
//    }

//    protected void channelRead0(ChannelHandlerContext ctx, Object o) throws Exception {
//        System.out.println("channelRead0");
//
//        // 握手协议返回,设置结束握手
//        if (!this.handshaker.isHandshakeComplete()){
//            FullHttpResponse response = (FullHttpResponse)o;
//            this.handshaker.finishHandshake(ctx.channel(), response);
//            this.handshakeFuture.setSuccess();
//            System.out.println("WebSocketClientHandler::channelRead0 HandshakeComplete...");
//            return;
//        }
//        else  if (o instanceof TextWebSocketFrame)
//        {
//            TextWebSocketFrame textFrame = (TextWebSocketFrame)o;
//            System.out.println("WebSocketClientHandler::channelRead0 textFrame: " + textFrame.text());
//        } else   if (o instanceof CloseWebSocketFrame){
//            System.out.println("WebSocketClientHandler::channelRead0 CloseWebSocketFrame");
//        }
//
//    }

    protected void channelRead0(ChannelHandlerContext ctx, Object message) throws Exception {
        System.out.println("channelRead0");

        // 判断是否正确握手
        if (!this.handshaker.isHandshakeComplete()){
            try {
                this.handshaker.finishHandshake(ctx.channel(), (FullHttpResponse) message);
                log.debug("websocket Handshake 完成!");
                this.handshakeFuture.setSuccess();
            } catch (WebSocketHandshakeException e) {
                log.debug("websocket连接失败!");
                this.handshakeFuture.setFailure(e);
            }
            return;

        }
        // 握手失败响应
        if (message instanceof FullHttpResponse) {
            FullHttpResponse response = (FullHttpResponse) message;
            log.error("握手失败!code:{},msg:{}", response.status(), response.content().toString(CharsetUtil.UTF_8));
        }
        WebSocketFrame frame = (WebSocketFrame) message;
        // 消息处理
        if (frame instanceof TextWebSocketFrame) {
            TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
            log.debug("收到消息: " + textFrame.text());
        }
        if (frame instanceof PongWebSocketFrame) {
            log.debug("pong消息");
        }
        if (frame instanceof CloseWebSocketFrame) {
            log.debug("服务器主动关闭连接");
            ctx.close();
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        log.debug("超时事件时触发");
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            // 当我们长时间没有给服务器发消息时,发送ping消息,告诉服务器我们还活跃
            if (event.state().equals(IdleState.WRITER_IDLE)) {
                log.debug("发送心跳");
                ctx.writeAndFlush(new PingWebSocketFrame());
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

}
  1. WebSocketNettyClient
package com.example.fallrainboot.nettyClient;

import com.alibaba.fastjson.JSONObject;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.URI;

@Slf4j
public class WebSocketNettyClient {

    public static void main(String[] args)  {

        EventLoopGroup group = new NioEventLoopGroup();
        final ClientHandler handler =new ClientHandler();
        try {
            URI websocketURI = new URI("wss://localhost:8109/ws?uid=369AX");
            //进行握手
            log.debug("握手开始");
            WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(websocketURI, WebSocketVersion.V13, (String)null, false,new DefaultHttpHeaders());
            System.out.println(websocketURI.getScheme());
            System.out.println(websocketURI.getHost());
            System.out.println(websocketURI.getPort());
            SslContext sslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();



            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_KEEPALIVE,true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {

                            //放到第一位 addFirst 支持wss链接服务端
                            ch.pipeline().addFirst(sslCtx.newHandler(ch.alloc(), websocketURI.getHost(),websocketURI.getPort()));

                            ChannelPipeline pipeline = ch.pipeline();
                            // 添加一个http的编解码器
                            pipeline.addLast(new HttpClientCodec());
                            // 添加一个用于支持大数据流的支持
                            pipeline.addLast(new ChunkedWriteHandler());
                            // 添加一个聚合器,这个聚合器主要是将HttpMessage聚合成FullHttpRequest/Response
                            pipeline.addLast(new HttpObjectAggregator(1024 * 64));
                            pipeline.addLast(handler);
                            ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws", null, true, 65536 * 10));

                        }
                    });



            final Channel channel=bootstrap.connect(websocketURI.getHost(),websocketURI.getPort()).sync().channel();
            handler.setHandshaker(handshaker);
            handshaker.handshake(channel);
            //发送消息
            System.out.println("发送消息");
            JSONObject hhh = new JSONObject();
            hhh.put("cmd","test");
            for (int i = 0; i < 5; i++) {
                Thread.sleep(2000);
                channel.writeAndFlush(new TextWebSocketFrame(hhh.toString()));
            }
            //阻塞等待是否握手成功
            handler.handshakeFuture().sync();
            System.out.println("握手成功");

            //发送消息
            System.out.println("发送消息");
            JSONObject clientJson = new JSONObject();
            clientJson.put("cmd","test");
            channel.writeAndFlush(new TextWebSocketFrame(clientJson.toString()));

            // 等待连接被关闭
            channel.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            group.shutdownGracefully();
        }

    }

}

5:测试步骤

  1. 启动顺序:首先启动服务端端代码—>后面启动客户端代码

  2. 成功截图1
    在这里插入图片描述

  3. 成功截图2
    在这里插入图片描述

  4. 成功截图3
    在这里插入图片描述

  5. 成功截图4
    在这里插入图片描述

  6. 成功截图5
    在这里插入图片描述

项目地址

	[项目git地址](https://gitee.com/fallrainliulei/fallrainboot/tree/netty_wss/)
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,我可以为您提供一些关于使用Netty实现WebSocket客户端支持wss安全连接)的建议。 首先,您需要确保已经包含了Netty和WebSocket依赖项。然后可以按照以下步骤实现: 1. 创建一个WebSocketClientHandshaker,用于握手和建立WebSocket连接。这里我们需要使用wss连接,因此需要使用WebSocketClientHandshakerFactory创建一个以SSL/TLS方式连接的WebSocket连接。 2. 创建一个WebSocketClientHandler,处理WebSocket连接的事件。这里我们需要重写channelActive、channelInactive和channelRead0等方法,以便在连接建立、关闭和接收到消息时执行相应的操作。 3. 创建一个Bootstrap实例,用于启动客户端。设置bootstrap的group、channel、handler等属性。 4. 连接到WebSocket服务器。通过bootstrap的connect方法连接到指定的WebSocket服务器。 下面是一个简单的示例代码: ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; 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.nio.NioSocketChannel; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequestEncoder; import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator; import io.netty.handler.codec.http.websocketx.WebSocketVersion; import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import java.net.URI; import java.util.concurrent.TimeUnit; public class NettyWebSocketClient { private final URI uri; private final EventLoopGroup group; private Channel channel; public NettyWebSocketClient(URI uri) { this.uri = uri; this.group = new NioEventLoopGroup(); } public void connect() throws Exception { try { SslContext sslContext = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker( uri, WebSocketVersion.V13, null, true, null); Bootstrap bootstrap = new Bootstrap() .group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LoggingHandler(LogLevel.DEBUG)); pipeline.addLast(new HttpClientCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new HttpRequestEncoder()); pipeline.addLast(new HttpResponseDecoder()); pipeline.addLast(new SslHandler(sslContext.newEngine(ch.alloc(), uri.getHost(), uri.getPort()))); pipeline.addLast(new WebSocketClientProtocolHandler(handshaker)); pipeline.addLast(new WebSocketFrameAggregator(65536)); pipeline.addLast(new WebSocketClientHandler()); } }); channel = bootstrap.connect(uri.getHost(), uri.getPort()).sync().channel(); handshaker.handshake(channel).sync(); } catch (Exception e) { group.shutdownGracefully(); throw e; } } public void sendMessage(WebSocketFrame frame) { channel.writeAndFlush(frame); } public void close() { channel.writeAndFlush(new CloseWebSocketFrame()) .addListener(ChannelFutureListener.CLOSE); group.shutdownGracefully(); } private class WebSocketClientHandler extends io.netty.channel.SimpleChannelInboundHandler<Object> { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpResponse) { FullHttpResponse response = (FullHttpResponse) msg; throw new IllegalStateException("Unexpected response (status=" + response.status() + ", content=" + response.content().toString() + ")"); } WebSocketFrame frame = (WebSocketFrame) msg; if (frame instanceof TextWebSocketFrame) { TextWebSocketFrame textFrame = (TextWebSocketFrame) frame; // 处理文本消息 System.out.println("Received Text Message: " + textFrame.text()); } else if (frame instanceof BinaryWebSocketFrame) { BinaryWebSocketFrame binaryFrame = (BinaryWebSocketFrame) frame; // 处理二进制消息 } else if (frame instanceof PingWebSocketFrame) { ctx.writeAndFlush(new PingWebSocketFrame(frame.content().retain())); } else if (frame instanceof CloseWebSocketFrame) { channel.close(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("WebSocket Client disconnected!"); group.schedule(() -> { try { connect(); } catch (Exception e) { e.printStackTrace(); } }, 10, TimeUnit.SECONDS); } } } ``` 在使用时,您可以按照以下步骤: ```java URI uri = new URI("wss://your.websocket.server.com"); NettyWebSocketClient client = new NettyWebSocketClient(uri); client.connect(); // 发送消息 WebSocketFrame frame = new TextWebSocketFrame("Hello, WebSocket!"); client.sendMessage(frame); // 关闭连接 client.close(); ``` 希望这些信息能够对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值