Netty快速入門實例-TCP服務
1、實例要求:使用IDEA創建Netty項目;
2、Netty伺服器在6668埠監聽,客戶端能發送消息給伺服器「hello,伺服器」;
3、伺服器可以回復消息給客戶端「hello,客戶端」;
4、目的:對Netty線程模型有一個初步認識,便於理解Netty模型理論;
伺服器端
public class NettyServer { public static void main(String[] args) throws Exception { /** * 說明 * 1、創建兩個線程組bossGroup 和 workerGroup * 2、bossGroup只是處理連接請求,真正和客戶端業務處理的會交給workerGroup完成 * 3、兩個都是無限循環 */ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { //創建伺服器端的啟動對象,配置參數 Serverbootstrap bootstrap = new ServerBootstrap(); //使用鏈式編程來進行設置 //設置兩個線程組 ServerBootstrap bootstrap1 = bootstrap.group(bossGroup, workerGroup) //使用NioServerSocketChannel作為伺服器的通道實現 .channel(NioServerSocketChannel.class) //設置線程隊列得到的連接個數 .option(ChannelOption.SO_BACKLOG, 128) //設置保持獲得的連接狀態 .childOption(ChannelOption.SO_KEEPALIVE, true) //創建一個通道測試對象(匿名對象) //給我們的workerGroup的EventLoop對應的管道設置處理器 .childHandler(new ChannelInitializer<SocketChannel>() { //給pipeline設置處理器 @Override protected void initChannel(SocketChannel sc) throws Exception { sc.pipeline().addLast(new NettyServerHandler()); } }); System.out.println("......伺服器 is ready..."); //綁定一個埠並且同步,生成了一個ChannelFuture對象 //啟動伺服器(並綁定埠) ChannelFuture cf = bootstrap.bind(6668).sync(); //對關閉通道進行監聽 cf.channel().closeFuture().sync(); } finally { //優雅關閉 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }}
伺服器端業務處理器
/** * 1、我們自定義一個Handler,需要繼承netty規定好的某個handlerAdapter(規範) * 2、這時我們自定義一個Handler,才能稱為一個Handler */public class NettyServerHandler extends ChannelInboundHandlerAdapter { /** * 讀取數據(這裡我們可以讀取客戶端發送的消息) * @param ctx 上下文對象,含有管道pipeline,通道channel,地址 * @param msg 就是客戶端發送的數據,默認是Object */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("server ctx = "+ctx); //將msg轉成一個ByteBuf //ByteBuf 是Netty提供的,不是NIO的ByteBuffer ByteBuf buf = (ByteBuf)msg; System.out.println("客戶端發送的消息是:"+buf.toString(CharsetUtil.UTF_8)); System.out.println("客戶端地址:"+ctx.channel().remoteAddress()); } /** * 數據讀取完畢 * @param ctx */ @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { /** * writeAndFlush 是write+flush * 將數據寫入到緩衝區,並刷新,一般講,我們對這個發送的數據進行編碼 */ ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客戶端",CharsetUtil.UTF_8)); } /** * 處理異常,一般需要關閉通道 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); }}
客戶端
public class NettyClient { public static void main(String[] args) { //客戶端需要一個事件循環組 NioEventLoopGroup group = new NioEventLoopGroup(); try { //創建客戶端啟動對象,注意客戶端使用不是ServerBootstarp而是Bootstrap Bootstrap bootstrap = new Bootstrap(); //設置相關參數 //設置線程組 bootstrap.group(group) //設置客戶端通道的實現類(反射) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel sc) throws Exception { //加入自己的處理器 sc.pipeline().addLast(new NettyClientHandler()); } }); System.out.println("客戶端 ok..."); //啟動客戶端去連接伺服器端 //關於ChannelFuture要分析,涉及到netty的異步模型 ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync(); //給關閉通道進行監聽 channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } }}
客戶端業務處理器
public class NettyClientHandler extends ChannelInboundHandlerAdapter { /** * 當通道就緒就會觸發該方法 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("client "+ctx); ctx.writeAndFlush(Unpooled.copiedBuffer("hello,服務端 ", CharsetUtil.UTF_8)); } /** *當通道有讀取事件時會觸發 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; System.out.println("伺服器回復的消息:"+buf.toString(CharsetUtil.UTF_8)); System.out.println("伺服器的地址:"+ctx.channel().remoteAddress()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}
Netty快速入門實例-HTTP服務
1、Netty伺服器在6668埠監聽,瀏覽器發出請求「http://localhost:6668」;
2、伺服器可以回復消息給客戶端「Hello 我是伺服器5」,並對特定請求資源進行過濾;
3、目的:Netty可以做Http服務開發,並且理解Handler實例和客戶端及其請求的關係;
服務端代碼
/** * 服務端 */public class TestServer { public static void main(String[] args) { NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class). childHandler(new TestServerInitializer()); ChannelFuture channelFuture = serverBootstrap.bind(6668).sync(); channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }}
TestServerInitializer代碼
public class TestServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { //向管道加入處理器 //得到管道 ChannelPipeline pipeline = ch.pipeline(); //加入一個netty提供的httpServerCodec codec=>[coder - decoder] //HttpserverCodec是netty提供的處理http的 編-解碼器 pipeline.addLast("MyhttpServerCodec",new HttpServerCodec()); //增加一個自定義的handler pipeline.addLast("MyTestHttpServerHandler",new TestHttpServerHandler()); }}
業務處理模塊TestHttpServerHandler
/** * 1、SimpleChannelInboundHandler 是 SimpleChannelInboundHandlerAdapter * 2、HttpObject 客戶端和服務端互相通訊的數據被封裝成 HttpObject */public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> { //讀取客戶端數據 @Override protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { //判斷msg 是不是HttpRequest if(msg instanceof HttpRequest){ //每開一個瀏覽器都回產生一個新的TestHttpServerHandler,因為http協議用完就關掉 System.out.println("pipeline hashcode "+ctx.pipeline().hashCode()+",TestHttpServerHandler hashcode="+this.hashCode()); System.out.println("msg 類型="+msg.getClass()); System.out.println("客戶端地址"+ctx.channel().remoteAddress()); //獲取到 HttpRequest httpRequest = (HttpRequest) msg; //獲取uri,過濾指定資源 URI uri = new URI(httpRequest.uri()); if("/favicon.ico".equals(uri.getPath())){ System.out.println("請求了faviconicon,不做響應"); return; } //回覆信息給瀏覽器 ByteBuf content = Unpooled.copiedBuffer("hello,我是伺服器", CharsetUtil.UTF_8); //構造一個http的響應,即httpResponse FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content); response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain;charset=utf-8"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes()); //將構建好的response返回 ctx.writeAndFlush(response); } }}
測試結果
HTTP請求用例