最近在學習netty,看了幾天的博客以後,打算本身寫一個練手的程序。java
這個程序很簡單:客戶端發送一個ping
,服務端會相應地回覆一個pong
,當監測到服務端失去鏈接後,即斷開。bootstrap
整個代碼分爲client與server兩部分,結構以下:服務器
引入netty包:socket
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.15.Final</version> </dependency>
client端:ide
PingClient.javaoop
package org.attempt.netty4.demo001.client; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; 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.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; public class PingClient { /** 服務器IP地址 */ private String host = "127.0.0.1"; /** 服務器端口 */ private int port = 8000; private EventLoopGroup group = null; private Bootstrap b = null; private Channel channel = null; public PingClient() throws Exception { group = new NioEventLoopGroup(); b = new Bootstrap(); b.group(group) .option(ChannelOption.SO_KEEPALIVE, true) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline() //字符串解碼和編碼 .addLast(new StringDecoder()) .addLast(new StringEncoder()) //客戶端的邏輯 .addLast(new PingClientHandler()); } }); } public Channel getChannel() throws Exception { if(null == channel || !channel.isActive()) { channel = b.connect(host, port).sync().channel(); } return channel; } public static void main(String[] args) throws Exception { PingClient client = null; try { client = new PingClient(); Channel channel = client.getChannel(); while(true) { //輸出channel的狀態,對應於close() System.out.println(channel.isOpen()); //判斷鏈接狀態 if(channel.isActive()) { channel.writeAndFlush("ping"); } else { System.out.println("失去鏈接,關閉客戶端"); channel.close(); break; } Thread.sleep(5000); } } finally { if(null != client) { client.stop(); } } } public void stop() { if(null != group) { //優雅退出,釋放線程池資源 group.shutdownGracefully(); } } }
PingClientHandler.java學習
package org.attempt.netty4.demo001.client; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class PingClientHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("receive from server: " + msg.toString()); } }
server端: PongServer.java編碼
package org.attempt.netty4.demo001.server; 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.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; public class PongServer { public static void main(String[] args) throws Exception { int port = 8000; if (args != null && args.length > 0) { try { port = Integer.valueOf(args[0]); } catch (NumberFormatException e) { //採用默認值 } } new PongServer().bind(port); } public void bind(int port) throws Exception { //配置服務端的NIO線程組 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline() //字符串解碼和編碼 .addLast(new StringDecoder()) .addLast(new StringEncoder()) //服務器的邏輯 .addLast(new PongServerHandler()); } }); //綁定端口,同步等待成功 ChannelFuture f = b.bind(port).sync(); //等待服務器監聽端口關閉 f.channel().closeFuture().sync(); } finally { //優雅退出,釋放線程池資源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
PongServerHandler.java.net
package org.attempt.netty4.demo001.server; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; /** * @author admin * @date 2018-09-06 22:48 */ public class PongServerHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("receive from client: " + msg.toString()); //返回客戶端消息 if(msg.toString().equals("ping")) { ctx.writeAndFlush("pong"); } else { ctx.writeAndFlush("UNKONWN"); } } }
運行,先啓動server端,再啓動client端,結果以下:線程
receive from client: ping receive from client: ping receive from client: ping receive from client: ping receive from client: ping Process finished with exit code -1
true receive from server: pong true receive from server: pong true receive from server: pong true receive from server: pong true receive from server: pong false 失去鏈接,關閉客戶端
代碼中的一些類及用法,後面再說。