引入Netty的依賴包
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
WebsocketServer.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class WebsocketServer {
//通過主函數運行此方法即可開啓Netty服務
public static void run(int port,String websocketPath) throws Exception {
//boos對應,IOServer.java中的接受新連接線程,主要負責創建新連接
EventLoopGroup bossGroup = new NioEventLoopGroup();
//worker對應 IOClient.java中的負責讀取數據的線程,主要用於讀取數據以及業務邏輯處理
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//獨立創建服務
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new WebsocketServerInitializer(websocketPath))
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
//綁定端口,等待連接
ChannelFuture future = bootstrap.bind(port).sync();
//通道關閉監聽並同步阻塞
future.channel().closeFuture().sync();
} finally {
//優雅的關閉羣組
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
WebsocketServerInitializer.java
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
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.stream.ChunkedWriteHandler;
public class WebsocketServerInitializer extends ChannelInitializer<SocketChannel> {
private String websocketPath;
public WebsocketServerInitializer(String websocketPath){
super();
this.websocketPath = websocketPath;
}
//通道初始化
@Override
public void initChannel(SocketChannel ch){
//通信管道
ChannelPipeline pipeline = ch.pipeline();
//http服務編解碼器
pipeline.addLast(new HttpServerCodec());
//設置最大消息長度
pipeline.addLast(new HttpObjectAggregator(64*1024));
//分塊寫入處理器
pipeline.addLast(new ChunkedWriteHandler());
//自定義的http服務處理器
pipeline.addLast(new HttpRequestHandler(websocketPath));
//websocket的服務協議處理器
pipeline.addLast(new WebSocketServerProtocolHandler(websocketPath));
//自定義的websocket的文本框架處理器
pipeline.addLast(new TextWebSocketFrameHandler());
}
}
HttpRequestHandler.java
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { //1
private String websocketPath;
public HttpRequestHandler(String websocketPath){
this.websocketPath = websocketPath;
}
//迴應客戶端請求並保持通道
@Override
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
//如果websocket路徑和請求路徑相同,則保持通道
if(websocketPath.equalsIgnoreCase(request.getUri())){
ctx.fireChannelRead(request.retain());
}else{//否則關閉通道
ctx.close();
}
}
//當請求發生錯誤時,關閉通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
cause.printStackTrace();
ctx.close();
}
}
TextWebSocketFrameHandler.java
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
//默認通道羣組
public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
//當某個通道有消息傳入時
@Override
protected void channelRead0(ChannelHandlerContext ctx,TextWebSocketFrame msg) {
//(自定義操作)對其它客戶端進行消息廣播
for (Channel channel : channels) {
if (channel != ctx.channel()){
channel.writeAndFlush(new TextWebSocketFrame(msg.text()));
}
}
}
//當某個通道被激活時
@Override
public void channelActive(ChannelHandlerContext ctx) {
channels.add(ctx.channel());
System.out.println("當前連接的客戶端數量:"+channels.size());
}
//當某個通道被銷燬時
@Override
public void channelInactive(ChannelHandlerContext ctx) {
channels.remove(ctx.channel());
System.out.println("當前連接的客戶端數量:"+channels.size());
}
//當某個通道發生錯誤時
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
cause.printStackTrace();
ctx.close();
}
}
最後通過主函數開啓Netty服務
public static void main(String[] args) {
try {
WebsocketServer.run(9999,"/ws");
} catch (Exception e){
e.printStackTrace();
}
}
H5前端如何連接服務?
let ws = new WebSocket("ws://localhost:9999/ws");