之前的是服务端,客户端又是如何的呢?
大体流程仍然是
Main
>>Initializer
>>Handler
。不过具体流程上面还是有一定差异,接下来对比一下。
Main
Client
public class ClientMain {
public static void main(String[] args) throws Exception{
NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ClientInitializer());
ChannelFuture channelFuture = bootstrap.connect("localhost", 8989).sync();
channelFuture.channel().closeFuture().sync();
}finally {
eventLoopGroup.shutdownGracefully();
}
}
}
Server
public class ServerMain {
public static void main(String[] args) throws Exception{
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ServerInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(8989).sync();
channelFuture.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
group
可以看到,服务端有两个group
,bossGroup
负责接收请求,workerGroup
负责处理请求。
但是客户端只有一个group
。
之前也说,服务端可以只有一个group
,两部分功能共用一个。
结果而言,服务端可以一个两个,客户端只能一个。
bootStrap
服务端:ServerBootStrap
客户端:BootStrap
channel
服务端:ServerSockerChannel
。
客户端:SockerChannel
handler
服务端:handler
,childHandler
客户端:handler
前面说了服务端有两个group
,拥有的两个handler
也是针对两个group
进行的设置。
其中的workerGroup
也可以叫做childGroup
,所以childHandler
也就不难理解了。
connect
服务端:bind
,绑定监听,等待别人连接
客户端:connect
,主动发起连接
Initializer
Client
public class ClientInitializer 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());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new ClientHandler());
}
}
Server
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch2) throws Exception {
ChannelPipeline pipeline = ch2.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new ServerHandler());
}
}
不用看了,没多大差异,就最后一行代码不一样。
客户端:ClientHandler
服务端:ServerHandler
因为都只是设置handler
,两者的设置方式都已一样的,只是handler
的类型问题。
关于编解码问题,后续会进行补充。
最后一个handler
则是业务逻辑不同所以handler
不同而已。
编解码简单了解
之前说
流水线
,假如我们是画师,到我们手里的就应该是成形的模具。
netty
的底层传输,实际上就是二进制的码流信息。经过层层的加工,从泥土到模具,然后才到我们手里。
二进制码流我们并不关心,我们只关心业务逻辑,只关心模具上色。
好比中美合作,语言不重要,重要的是谈话内容。
从业务角度,转为码流叫做编码,码流转为对象就是解码。
前面几个
handler
就是转换所用,并不涉及业务逻辑操作。关于码流,后续的粘包和编解码会继续说明,目前关注流程即可。
Handler
Client
public class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("from Server :" + msg);
}
}
Server
public class ServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("from Client : " + msg);
}
}
没啥可对比的,除了方法内部逻辑不一样,其他都一样。
毕竟一个请求
一个处理
。
客户端和服务端的对吗对比,看起来只有创建初始有些许的差异。
在Initializer
发生微小的区别,然后的handler
最终迥异。
不过后续是业务逻辑的差异,就结构而言,仅初始存在差异,后续结构都是相同的。
此时的链接确定是无误的,通过
lsof -i:8989
能够证明,但是却没有效果。明确知道是因为两边都很
矜持
,没有谁主动一点去触发,所以没有结果输出。一般在
handler
的channelActive
去进行触发,也有业务中直接从队列中去数据,通过while
循环执行。关于这点,后续继续,现在主题是进行客户端和服务端代码的对比。
详细代码在此;