前言
编程学习的方法,我认为是以小见大,在理解一个东西之前一定要先会用,并用熟它,这样理解才会快。
就跟理解自行车一样,不会骑,然后先开始研究,最终可能会研究明白,但是毕竟还是事倍功半。
所以先构建一个可以使自己理解的项目,再一点一点学习原理是一种比较好的方式。
Server服务端
构建netty
的话,就是一个流程三件套,最基础的三个框架组件摆出来,然后在上面写代码,分别是:
- Server 启动类
- Initializer 实始化组件类
- Handle 请求处理类
劳记这一个流程三件套,基本netty的开发,你已经入门了,就是这么回事。
启动类
套路第一步,写一个启动类,这个是入口,netty服务的话,一般都是先启动服务端,再启动客户端。
这个好理解,如果服务端都不提供服务,客户端还有必要连接吗。当然如果是要启动着玩,就另说。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.liukai.netty.test02.server;
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel;
public class ServerNettyServer {
public static void main(String[] args) { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .childHandler(new ServerNettyChannelInitializer02());
ChannelFuture channelFuture = serverBootstrap.bind(8889).sync(); System.out.println("Netty 服务端启动完毕"); channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { bossGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); e.printStackTrace(); } } }
|
初始化
实始化必要组件,这也在其它的编常中也是非常常见的一种模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.liukai.netty.test02.server;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil;
public class ServerNettyChannelInitializer02 extends ChannelInitializer<SocketChannel> {
@Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)); pipeline.addLast(new LengthFieldPrepender(4)); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new ServerNettyServerHandle()); } }
|
处理器
处理具体业务,其实看下来,就是这个模式,是不是很好理解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.liukai.netty.test02.server;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler;
import java.util.UUID;
public class ServerNettyServerHandle extends SimpleChannelInboundHandler<String> {
@Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("server: [remote ip]-> " + ctx.channel().remoteAddress()+", [msg]-> "+msg); ctx.channel().writeAndFlush("server: "+ UUID.randomUUID()); }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); } }
|
客户端
启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public class ClientServer {
public static void main(String[] args) { EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ClientNettyChannelInitializer()); ChannelFuture channelFuture = bootstrap.connect("localhost",8889).sync(); channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { eventLoopGroup.shutdownGracefully(); e.printStackTrace(); } } }
|
初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package com.liukai.netty.test02.client;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil;
public class ClientNettyChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)); pipeline.addLast(new LengthFieldPrepender(4)); pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); pipeline.addLast(new ClientNettyHandle()); } }
|
处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package com.liukai.netty.test02.client;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler;
import java.time.LocalDateTime;
public class ClientNettyHandle extends SimpleChannelInboundHandler<String> {
@Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("client: [remote ip]->" + ctx.channel().remoteAddress() + ", [msg]->" + msg); Thread.sleep(1000); ctx.writeAndFlush("client:"+ LocalDateTime.now()); }
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush("客户端:发送数据"); } }
|
效果
可以启动看一下这个代码的效果:
1.启动服务端Server
Netty 服务端启动完毕
server: [remote ip]-> /127.0.0.1:65320, [msg]-> 客户端:发送数据
server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:42.653
server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:43.657
server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:44.661
server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:45.664
server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:46.668
server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:47.674
server: [remote ip]-> /127.0.0.1:65320, [msg]-> client:2022-05-21T21:49:48.678
2.启动客户端
client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: bd34689c-237a-4499-be9f-be7b55d1f7e2
client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: afe29b02-65f8-44c5-8b1b-c5286acd0a72
client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: 806f346f-9ae8-4dce-ad20-7a308d8b0c46
client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: 670fb4af-15cd-4471-9990-bc86ff07932c
client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: b103ed1a-127b-42df-9c86-58f9e5dd2d4a
client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: 3948c322-1cb7-4ba0-bade-7f7161fc2712
client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: e3daf473-6dd6-4e6c-8bca-27f87e43854f
client: [remote ip]->localhost/127.0.0.1:8889, [msg]->server: 64bf61e2-4126-4238-b53c-4bf90e1c21d8
总结
netyy 的刚开始学习时,只需要了解到它的这个套路,后面的开发其实大同小异,无非是对协议和序列化相关的东西进行处理。实现自己的业务需求。