背景
工作场合肯定是需要自定义协议的,下面是个用Springboot写的独立的服务端、客户端程序。
上代码
-
添加Netty依赖,这里博主同时也消除Tomcat依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.31.Final</version> </dependency>
-
写一个自定义协议的类:
//这里博主写了个仅有长度和内容的协议 public class PersonProtocal { private int length; private byte[] content; public int getLength() { return length; } public void setLength(int length) { this.length = length; } public byte[] getContent() { return content; } public void setContent(byte[] content) { this.content = content; } }
-
写编码MyPersonEncoder类:
public class MyPersonEncoder extends MessageToByteEncoder<PersonProtocal> { @Override protected void encode(ChannelHandlerContext ctx, PersonProtocal msg, ByteBuf out) throws Exception { System.out.println("MyPersonEncoder encode invoked"); out.writeInt(msg.getLength()); out.writeBytes(msg.getContent()); } }
-
写译码MyPersonDecoder类:
public class MyPersonDecoder extends ReplayingDecoder<Void> { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { System.out.println("MyPersonDecoder decode invoked "); int length = in.readInt(); byte[] content = new byte[length]; in.readBytes(content); PersonProtocal personProtocal = new PersonProtocal(); personProtocal.setLength(length); personProtocal.setContent(content); out.add(personProtocal); } }
注:ByteToMessageDecoder 可以在阻塞的i/o模式下实现非阻塞的解码。
ReplayingDecoder 和ByteToMessageDecoder 最大的不同就是ReplayingDecoder ,不需要判断可用的字节。 -
服务端启动类:
public class EchoServer { private final int port; private Logger log = LoggerFactory.getLogger(this.getClass()); public EchoServer(int port) { this.port=port; } public void start() throws Exception{ final EchoServerHandler serverHandler = new EchoServerHandler(); EventLoopGroup group = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap=new ServerBootstrap(); serverBootstrap.group(group) .localAddress(new InetSocketAddress(port)) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel channel) throws Exception { channel.pipeline().addLast(new MyPersonDecoder()); channel.pipeline().addLast(new MyPersonEncoder()); channel.pipeline().addLast(serverHandler); } }); ChannelFuture f=serverBootstrap.bind().sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); } } }
-
服务端Handler类:
@ChannelHandler.Sharable //没写这个,当服务端关闭再连接时会报错Failed to initialize a channel. Closing public class EchoServerHandler extends SimpleChannelInboundHandler<PersonProtocal> { private int count; @Override protected void channelRead0(ChannelHandlerContext ctx, PersonProtocal msg) throws Exception { System.out.println("服务端接受到的数据:"); System.out.println("数据长度:"+msg.getLength()); System.out.println("数据内容:"+ new String(msg.getContent(), Charset.forName("utf-8")) ); System.out.println("服务端接收到的消息数量:"+(++count)); String responseMessage = UUID.randomUUID().toString(); int responseLength = responseMessage.getBytes(Charset.forName("utf-8")).length; byte[] responseContent = responseMessage.getBytes(Charset.forName("utf-8")); PersonProtocal personProtocal = new PersonProtocal(); personProtocal.setLength(responseLength); personProtocal.setContent(responseContent); ctx.writeAndFlush(personProtocal); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
-
-
Application,博主没有用tomcat做容器
@SpringBootApplication public class Nettyserver3Application implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(Nettyserver3Application.class, args); } @Override public void run(String... strings) throws Exception { EchoServer echoServer = new EchoServer(7000); echoServer.start(); } }
———————————————————————我是分割线,下面是客户端部分———————————————————
-
编码,解码,协议包都是不变的
-
客户端启动类:
public class EchoClient { @Value("${netty.host}") private String host; @Value("${netty.port}") private int port; //@PostConstruct public void start() throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port)) .handler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new MyPersonDecoder()); ch.pipeline().addLast(new MyPersonEncoder()); ch.pipeline().addLast(new EchoClientHandler()); } }); ChannelFuture f = b.connect().sync(); f.channel().writeAndFlush("hello"); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); } } }
-
客户端Handler类:
@ChannelHandler.Sharable public class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{ ByteBuf in = (ByteBuf) msg; System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8)); ctx.write(in); } @Override public void channelReadComplete(ChannelHandlerContext ctx){ ctx.writeAndFlush(ChannelFutureListener.CLOSE); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){ cause.printStackTrace(); ctx.close(); } }
-
Application:
@SpringBootApplication public class ServerApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(ServerApplication.class, args); } @Override public void run(String... strings) throws Exception { EchoServer echoServer = new EchoServer(7000); echoServer.start(); } }
—————————————————— 参考文章,感谢该博主———————————————————————————
https://blog.csdn.net/u012150590/article/details/81255567
代码完整版: