Netty 常用的场景:
1.充当HTTP 服务器,但Netty 并没有遵循servlet 的标准,反而实现了自己的一套标准进行Http 服务;
2,RPC 远程调用,在分布式系统中常用的框架
3.Socket 长连接
需要了解的名词
1.NioEventLoopGroup: 对线程的控制,线程组,事件循环组,死循环,需要定义两个线程组,一个只用于接收客户端的请求,一个负责具体的处理
2.Bootstrap :对服务启动的封装,可以很轻松的启动服务管道,进行服务的启动
3.Channel:渠道,每一个请求都会形成一个渠道
4.initChannel:连接一旦被创建,就会执行initChannel方法
5.ChannelPipline:连接的管道,里面装有很多的ChannelHandler处理器(第6)
6.ChannelHandler:定义我们自己的处理器,因为流程进行到这里我们的请求已经进来了,需要我们进行自己的逻辑处理与响应,相当于filter 一样; netty也提供了很多的处理器;
7.ByteBuf:很重要,以后详细讲解
8.ChannelGroup :用来存放一个一个的Channel 对象的Channel 组 ,可以通过 new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);进行获取
接下来是示例代码:我们目前刚接触,我们先不用管它里面具体怎么实现的,只要先对netty 有一个全局的认识,知道它的执行流程就可以,知道执行流程后再深入学习每一步的作用,这样我们就不会太迷茫
基本上,所有netty 编程中都会遵循以下三点:
1. 创建线程组,即EventLoopGroup
2.进行服务启动类,即bootstrap
3.针对自己的业务,创建自己的Handler
1.我们的启动类
public class WebSocketService { public static void main(String[] args) throws Exception { //一样,定义线程组 EventLoopGroup boosGroup =new NioEventLoopGroup(); EventLoopGroup workGroup =new NioEventLoopGroup(); //服务启动类 try { ServerBootstrap bootstrap=new ServerBootstrap(); bootstrap.group(boosGroup,workGroup) .channel(NioServerSocketChannel.class) //添加日志 .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new WebSocketInitializer()); //端口绑定 ChannelFuture channelFuture=bootstrap.bind(9077).sync(); channelFuture.channel().closeFuture().sync(); } finally { boosGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
2.初始化器,用来存放Hander 的
/** * initializer 初始化器 * @author cys * */ public class WebSocketInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pip =ch.pipeline(); //=====websocket 是基于http协议基础之上的,所以要有http的支持=======// //编解码器 pip.addLast(new HttpServerCodec()); //对大数据的支持 pip.addLast(new ChunkedWriteHandler()); // 对httpMessage进行聚合,聚合成FullHttpRequest或FullHttpResponse // 几乎在netty中的编程,都会使用到此hanler pip.addLast(new HttpObjectAggregator(1024*64)); //This handler does all the heavy lifting for you to run a websocket server. //这个handler 做了所有的繁重的工作,对于你运行的websocket服务器 //处理了frame 的 close ping pang binary text ,运行中并带到下一个关于你的实现的handler pip.addLast(new WebSocketServerProtocolHandler("/ws"));//===>ws://localhost:8066/ws //自定义自己的处理类,WebSocketServerProtocolHandler 会将数据帧带到你的handler中 pip.addLast(new WebSocketMyHandler()); } }
3.定义自己的Handler ,也就是自己的逻辑处理
/** * webSocket 自定义handler * webSocket 对于数据的传输都是基于Frame(帧)的 ,关于frame 一共有五种类型,遵循RFC标准,netty 实现了这五种标准,所有netty 对websocket 有很好的支持 * 1.BinaryWebSocketFrame * 2.TextWebSocketFrame * 3.PongWebSocketFrame * 4.PingWebSocketFrame * 5.CloseWebSocketFrame * ping、pong 的操作,对应的是 WebSocket 的两个控制帧 ==心跳 * * @author cys * */ public class WebSocketMyHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { //当前的channel 组 private static ChannelGroup channelGroup =new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { Channel ch =ctx.channel(); //msg.text()接收客户端的消息 String text=msg.text(); System.out.println("收到消息"+text); for(Channel self :channelGroup) { if(ch ==self) { ch.writeAndFlush(new TextWebSocketFrame("我:===>"+text)); } else { self.writeAndFlush(new TextWebSocketFrame(ch.remoteAddress()+"===>"+text)); } } } /** * 当有新的Channel连接 时候会触发 */ @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel ch = ctx.channel(); channelGroup.writeAndFlush(new TextWebSocketFrame(ch.remoteAddress()+"---上线")); System.out.println(ch.remoteAddress()+"已经连接"); channelGroup.add(ctx.channel()); } /** * 连接断开的时候触发 */ @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { Channel ch = ctx.channel(); System.out.println(new TextWebSocketFrame(ch.remoteAddress()+"已经断开")); channelGroup.writeAndFlush(ch.remoteAddress()+"---下线"); } /** * 出现异常时候触发,关闭当前连接 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.channel().close(); } }
4.因为websocket 是基于HTML5的,所以客户端就是我们支持websocket的浏览器,
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>web socket连接</title> </head> <body> <div>发送消息</div> <input type="text" id="msgContent"/> <input type="button" value="send" onclick="CHAT.chat()"/> <div>接收消息</div> <div id="reciveMsg" style="background-color:pink"></div> <script type="text/javascript"> window.CHAT ={ socket:null, init: function(){ if(window.WebSocket){ CHAT.socket =new WebSocket("ws://10.9.121.91:9077/ws"); CHAT.socket.onopen = CHAT.onopen, CHAT.socket.onclose =CHAT.onclose, CHAT.socket.onerror =CHAT.onerror, CHAT.socket.onmessage =CHAT.onmessage } else{ alert("不支持socket"); } }, chat: function(){ //消息发送 var content =document.getElementById("msgContent"); if (CHAT.socket != null && CHAT.socket != undefined && CHAT.socket.readyState == WebSocket.OPEN) { CHAT.socket.send(content.value); } else { // 重连websocket CHAT.init(); } } , //客户端与服务器链接触发 onopen :function(){ console.log("链接已经链接"); }, onclose :function(){ console.log("链接已经关闭"); }, onerror:function(){ console.log("出现错误"); }, onmessage:function(e){ console.log("链接消息"); var reciveMsg =document.getElementById("reciveMsg"); var html =reciveMsg.innerHTML; reciveMsg.innerHTML =html+"<br/>"+e.data; } } CHAT.init(); </script> </body> </html>