也是在网上查的资料,整理一下相互学习下
第一步:生成SSL证书:
因为是测试,直接使用jdk自带的keytool工具生成自签名证书(注:自签名证书是不被浏览器认可的,只能用于测试),
--打开cmd
--输入命令(复制啊):keytool -genkey -keysize 2048 -validity 365 -keyalg RSA -keypass netty123 -storepass netty123 -keystore wss.jks
第二步:在ChannelPipeline添加SslHandler:
首先写个工具类:SslUtil配置SSLContext
public static SSLContext createSSLContext(String type ,String path ,String password) throws Exception {
KeyStore ks = KeyStore.getInstance(type); /// "JKS"
InputStream ksInputStream = new FileInputStream(path); /// 证书存放地址
ks.load(ksInputStream, password.toCharArray());
//KeyManagerFactory充当基于密钥内容源的密钥管理器的工厂。
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());//getDefaultAlgorithm:获取默认的 KeyManagerFactory 算法名称。
kmf.init(ks, password.toCharArray());
//SSLContext的实例表示安全套接字协议的实现,它充当用于安全套接字工厂或 SSLEngine 的工厂。
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, null);
return sslContext;
}
添加SslHandler(放在第一个)
@Override
public void initChannel(SocketChannel ch) throws Exception {
SSLContext sslContext = SslUtil.createSSLContext("JKS","D://wss.jks","netty123");
//SSLEngine 此类允许使用ssl安全套接层协议进行安全通信
SSLEngine engine = sslContext.createSSLEngine();
engine.setUseClientMode(false);
ch.pipeline().addLast(new SslHandler(engine));
ch.pipeline().addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
ch.pipeline().addLast("http-codec", new HttpServerCodec());
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
ch.pipeline().addLast(new AcceptorIdleStateTrigger());
ch.pipeline().addLast("handler", new WebSocketHandler());
}
JS文件的URL:
var url = "wss://localhost:8000/ws";
第三步:运行
运行服务端,在浏览器地址栏输入https://localhost:8000/ 浏览器会提示这是不安全的连接(浏览器不信任自签名证书,如果有域名可以自己申请一个证书,网上有免费测试版的证书),添加例外信任,再在html页面上右键获得它的本地路径 在浏览器中运行 连接成功
补充:(因为发现一些小伙伴不清楚部分类的来源,特此补充)--2021.09.08
目录:
AcceptorIdleStateTrigger .java
package com.netty.wss;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
@ChannelHandler.Sharable
public class AcceptorIdleStateTrigger extends ChannelInboundHandlerAdapter {
private int unReceivedCounts=0;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
//有消息时归零
unReceivedCounts=0;
super.channelRead(ctx, msg);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
if (evt instanceof IdleStateEvent) {
//在这里并处理业务逻辑
IdleState state = ((IdleStateEvent) evt).state();
if (state == IdleState.READER_IDLE) {
unReceivedCounts++;
System.out.println(unReceivedCounts);
if (unReceivedCounts>10) {
ctx.close();
unReceivedCounts = 0;
}
}
} else {
super.userEventTriggered(ctx, evt);
}
}
}
SslUtil.java
package com.netty.wss;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
public class SslUtil {
public static SSLContext createSSLContext(String type ,String path ,String password) throws Exception {
KeyStore ks = KeyStore.getInstance(type); /// "JKS"
InputStream ksInputStream = new FileInputStream(path); /// 证书存放地址
ks.load(ksInputStream, password.toCharArray());
//KeyManagerFactory充当基于密钥内容源的密钥管理器的工厂。
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());//getDefaultAlgorithm:获取默认的 KeyManagerFactory 算法名称。
kmf.init(ks, password.toCharArray());
//SSLContext的实例表示安全套接字协议的实现,它充当用于安全套接字工厂或 SSLEngine 的工厂。
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, null);
return sslContext;
}
}
WebSocketHandler.java
package com.netty.wss;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
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 io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;
/**
* websocket 具体业务处理方法
*
* */
public class WebSocketHandler extends ChannelInboundHandlerAdapter{
private WebSocketServerHandshaker handshaker;
/**
* 当客户端连接成功,返回个成功信息
* */
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
/**
* 当客户端断开连接
* */
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
// 传统的HTTP接入
if(msg instanceof FullHttpRequest){
handleHttpRequest(ctx,(FullHttpRequest)msg);
}else if(msg instanceof WebSocketFrame){
handlerWebSocketFrame(ctx,(WebSocketFrame)msg);
}
super.channelRead(ctx, msg);
}
public void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception{
//关闭请求
if(frame instanceof CloseWebSocketFrame){
handshaker.close(ctx.channel(), (CloseWebSocketFrame)frame.retain());
return;
}
//只支持文本格式,不支持二进制消息
if(!(frame instanceof TextWebSocketFrame)){
throw new Exception("仅支持文本格式");
}
}
//第一次请求是http请求,请求头包括ws的信息
public void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req){
if(!req.decoderResult().isSuccess()){
sendHttpResponse(ctx,req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
return;
}
// 构造握手响应返回
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws:/"+ctx.channel()+ "/ws",null,false);
handshaker = wsFactory.newHandshaker(req);
// 请求头不合法, 导致handshaker没创建成功
if(handshaker == null){
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
}else{
handshaker.handshake(ctx.channel(), req);
}
}
public static void sendHttpResponse(ChannelHandlerContext ctx,FullHttpRequest req,DefaultFullHttpResponse res){
// 返回应答给客户端
if (res.status().code() != 200){
ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
}
// 如果是非Keep-Alive,关闭连接
ChannelFuture f = ctx.channel().writeAndFlush(res);
if (!isKeepAlive(req) || res.status().code() != 200){
f.addListener(ChannelFutureListener.CLOSE);
}
}
private static boolean isKeepAlive(FullHttpRequest req){
return false;
}
//异常处理,netty默认是关闭channel
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
//输出日志
cause.printStackTrace();
ctx.close();
}
}
WebsocketServer.java (启动类)
package com.netty.wss;
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.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
public class WebsocketServer {
public static WebsocketServer instance;
public static final WebsocketServer getInstance(){
return instance;
}
private int port;
public WebsocketServer(int port) {
if(instance!=null){
return;
}
this.port = port;
try {
this.start();
instance=this;
} catch (Exception e) {
e.printStackTrace();
}
}
private void start() throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup);
b.channel(NioServerSocketChannel.class);
b.childHandler(new myChannelInitializer());
b.option(ChannelOption.SO_BACKLOG, 128);
b.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port);
System.out.println("Netty Websocket is start .........");
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
private class myChannelInitializer extends ChannelInitializer<SocketChannel>{
@Override
public void initChannel(SocketChannel ch) throws Exception {
SSLContext sslContext = SslUtil.createSSLContext("JKS","D://wss.jks","netty123");
//SSLEngine 此类允许使用ssl安全套接层协议进行安全通信
SSLEngine engine = sslContext.createSSLEngine();
engine.setUseClientMode(false);
ch.pipeline().addLast(new SslHandler(engine));
ch.pipeline().addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
ch.pipeline().addLast("http-codec", new HttpServerCodec());
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
ch.pipeline().addLast(new AcceptorIdleStateTrigger());
ch.pipeline().addLast("handler", new WebSocketHandler());
}
}
public static void main(String[] args) throws Exception {
try {
new WebsocketServer(8000);
} catch (Exception e) {
// TODO: handle exception
}
}
}
源码地址:点击打开链接
参考:http://www.360doc.com/content/17/1228/16/51390869_717156716.shtml;