# Netty Websocket SSL Java 服务端 + Javascript 客户端
📆 2022-1-5 14:12
# SSL 证书
申请完阿里云免费证书 (opens new window)后,下载 Nginx 版证书。
Nginx 证书的 .pem .key 使用 X.509 协议。
.pem 无需转换,但是 .key 需要转换成 pkcs8 协议。
使用 openssl 转换证书命令如下:
openssl pkcs8 -topk8 -inform PEM -in 142857_netty.tun6.com.key -outform PEM -nocrypt -out 142857_netty.tun6.com.key.pkcs8
# Java 实现
# Java 目录结构
- src/main/java
- com.tun6
- MyWebsocketService.java
- MyMainHandler.java
- src/main/resources
- 142857_netty.tun6.com.key
- 142857_netty.tun6.com.key.pkcs8
- 142857_netty.tun6.com.pem
# MyWebsocketService
public class MyWebsocketService {
private static MyMainHandler mainHandler=new MyMainHandler();
public static void main(String[] args) {
try {
SslContext sslCtx=SslContextBuilder.forServer(
this.getClass().getResourceAsStream("/142857_netty.tun6.com.pem"),
this.getClass().getResourceAsStream("/142857_netty.tun6.com.key.pkcs8")
).build();
EventLoopGroup bossGroup=new NioEventLoopGroup(1);
EventLoopGroup workerGroup=new NioEventLoopGroup(4);
ServerBootstrap bootstrap=new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(Epoll.isAvailable()?EpollServerSocketChannel.class:NioServerSocketChannel.class)// 尽量使用 Epool
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pp=ch.pipeline();
pp.addLast(sslCtx.newHandler(ch.alloc()));// 处理 ssl 协议
pp.addLast(new HttpServerCodec());// 分解为 http 协议体
pp.addLast(new HttpObjectAggregator(65536));// http 协议长度限制器
pp.addLast(new WebSocketServerCompressionHandler());// 处理 websocket 握手
pp.addLast(new WebSocketServerProtocolHandler("/websocket",null,true));// 处理 websocket 协议
pp.addLast(new BinaryWebSocketFrameDecoder2());// BinaryWebSocketFrame ==> ByteBuf
pp.addLast(new ProtobufFrameDecoder2());// ByteBuf ==> ProtobufFrame
pp.addLast(new BinaryWebSocketFrameEncoder2());// ByteBuf ==> BinaryWebSocketFrame
pp.addLast(new ProtobufFrameEncoder2());// ProtobufFrame ==> ByteBuf
pp.addLast(mainHandler);
}
});
Channel ch=bootstrap.bind(888).sync().channel();
ch.closeFuture().sync();
} catch (Exception e) {
log.error("socket service start fail",e);
}
}
}
# MyMainHandler
public class MyMainHandler extends SimpleChannelInboundHandler<ProtobufFrame> {
private ChannelGroup channelGroup=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
protected void channelRead0(ChannelHandlerContext ctx, ProtobufFrame msg) throws Exception {
log.info("channel read {}, {}",ctx,msg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("channel active {}",ctx);
channelGroup.add(ctx.channel());
log.info("channelGroupSize: {}",channelGroup.size());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info("channel inactive {}",ctx);
channelGroup.remove(ctx.channel());
log.info("channelGroupSize: {}",channelGroup.size());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.error("channel {} error",ctx,cause);
}
}
# JavaScript 实现
const ws=new WebSocket('wss://netty.tun6.com:888/websocket')
ws.onopen=(e)=>{
console.log('open');
ws.send(Uint8Array.from([1,4,2,8,5,7]))
}
ws.onclose=(e)=>{
console.log('close');
}
ws.onmessage=(e)=>{
console.log('message');
}
ws.onerror=(e)=>{
console.log('error',e);
}