自学内容网 自学内容网

服务器中使用wss协议连接websocket(基于netty)

前置条件:可用的SSL证书

需要两个文件,key格式的还有pem格式的

如果是阿里云证书,下面Nginx类型的就可以

快捷转移——数字证书管理服务管理控制台 (aliyun.com)

在这里插入图片描述

自建证书

以管理员形式打开cmd窗口,如果提示keytool不存在,将目录定位到java安装目录的bin下方。

1、生成证书

keytool -genkey -alias myalias -keyalg RSA -keysize 2048 -keystore mykeystore.jks -validity 365

2、jks证书转化为pem格式

keytool -export -alias myalias -keystore mykeystore.jks -rfc -file certificate.pem

3、导出私钥

keytool -importkeystore -srckeystore mykeystore.jks -destkeystore temp.p12 -srcalias myalias -deststoretype PKCS12 -srcstorepass <keystore_password> -deststorepass <p12_password>

4、提取私钥

openssl pkcs12 -in temp.p12 -nocerts -nodes -out private_key.pem

这种方式得到两个接下来要用到的文件

① certificate.pem

② private_key.pem

阿里云证书的方式直接下载得到的是key和pem,需要将key转化为私钥

执行openssl rsa -in old_server_key.pem -out private_key.pem

这样也得到了上述两个文件

接下来使用netty搭建简易websocket服务器

@Configuration
@Slf4j
public class NettyWebSocketServer {

    private final static int WEB_SOCKET_PORT = 9090;

    private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    private final EventLoopGroup workerGroup = new NioEventLoopGroup(NettyRuntime.availableProcessors());

    @PostConstruct
    public void start() throws Exception {
        run();
    }

    @PreDestroy
    public void destroy() {
        Future<?> future = bossGroup.shutdownGracefully();
        Future<?> future1 = workerGroup.shutdownGracefully();
        future.syncUninterruptibly();
        future1.syncUninterruptibly();
        log.info("关闭 ws server 成功");
    }

    public void run() throws Exception {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        SslContext sslContext = SslContextBuilder.forServer(getCertPem(), getCertKey()).build();

        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 128)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .handler(new LoggingHandler(LogLevel.INFO)) // 为 bossGroup 添加 日志处理器
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
// 注意将SSL必须是第一个处理器
                        pipeline.addLast(sslContext.newHandler(socketChannel.alloc()));

                        //30秒客户端没有向服务器发送心跳则关闭连接
                        pipeline.addLast(new IdleStateHandler(30, 0, 0));
                        // 因为使用http协议,所以需要使用http的编码器,解码器
                        pipeline.addLast(new HttpServerCodec());
                        // 以块方式写,添加 chunkedWriter 处理器
                        pipeline.addLast(new ChunkedWriteHandler());
                        /**
                         * 说明:
                         *  1. http数据在传输过程中是分段的,HttpObjectAggregator可以把多个段聚合起来;
                         *  2. 这就是为什么当浏览器发送大量数据时,就会发出多次 http请求的原因
                         */
                        pipeline.addLast(new HttpObjectAggregator(8192));
                        //保存用户ip
                        pipeline.addLast(new HttpHeadersHandler());
                        /**
                         * 
                         */
                        pipeline.addLast(new WebSocketServerProtocolHandler("/"));
                    }
                });
        // 启动服务器,监听端口,阻塞直到启动成功
        serverBootstrap.bind(WEB_SOCKET_PORT).sync();
        log.info("netty在{}启动成功", WEB_SOCKET_PORT);
    }

    private InputStream getCertPem() {
        ClassPathResource classPathResource = new ClassPathResource("certificate.pem");
        return classPathResource.getStream();
    }

    private InputStream getCertKey() {
        ClassPathResource classPathResource = new ClassPathResource("private_key.pem");
        return classPathResource.getStream();
    }

}

然后加成jar包放到服务器上启动。

配置nginx反向代理

server{
    listen 443 ssl;
    server_name your-server-name; # 填写①
    ssl_certificate certificate.pem; # 填写路径②
    ssl_certificate_key private_key.pem; # 填好路径③
    ssl_session_timeout 5m;
    root /www/wwwroot; # 自行配置
    charset utf-8;

    location /netty{
        proxy_pass https://your-address-url:9090/; # 填写④ 不能是9090 必须是9090/
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        rewrite "^/netty/(.*)$" /$1 break;
    }
}

测试 wss://your-server-name/netty 可以连接成功

常见错误

1、io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record

错误说明没有使用SSL/TLS

导致的原因可能是:

① 使用了ws协议连接

② nginx转发的时候使用了http,需要使用https

2、wss Received fatal alert: certificate_unknown

错误说明证书未知

导致的原因可能是:

① 证书错误、不被信任


原文地址:https://blog.csdn.net/a1308003218/article/details/142916068

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!