好如上篇所说我们今天就来详细分析一下上篇文章中的服务端客户端端的每一个步骤都在干嘛,这里呢我只以服务端为例,因为在Netty编程中客户端和服务端是及其相似的基本差不多少。那么我们开始吧。
首先是服务端启动部分
package netty.demo_01;
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 Server {
public static void main(String[] args) throws Exception{
/*
这里是创建了两个线程组,服务端通常会有两个线程组,一个用来处理并记录存储客户端的连接,
另一个负责来处理集体的业务也就是客户端的请求,数据的返回等。
*/
EventLoopGroup boosgrop=new NioEventLoopGroup();
EventLoopGroup workgrop=new NioEventLoopGroup();
try {
/*
创建以一个 ServerBootstrap对象 ServerBootstrap其实就是一个关键字而已 类似与我们java原生socket一样
那么他是干什么的呢?
他其实就是来实现绑定服务器 以及端口啊线程组等等,总之他呢就一个必须的必。客户端也有与之对应的Bootstrap与服务端用法基本相同。
*/
ServerBootstrap serverBootstrap=new ServerBootstrap();
//绑定线程组
serverBootstrap.group(boosgrop,workgrop)
//设置处理类
.channel(NioServerSocketChannel.class)
//设置将被添加的子处理类
.childHandler(new ServerInitializer());
//绑定服务器 监听端口
ChannelFuture channelFuture=serverBootstrap.bind(8899).sync();
//获取 Channel 的CloseFuture,等待关闭
channelFuture.channel().closeFuture().sync();
}finally {
//关闭 两个线程组
boosgrop.shutdownGracefully();
workgrop.shutdownGracefully();
}
}
}
服务端初始化器
package netty.demo_01;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
/*
这里其实没什么好说的,可以说是挂载处理器的地方,
也可以理解为是初始化器,
把其当作是挂载处理器的话不是很好理解,
建议大家还是将其理解为初始化器
*/
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//使用socketchannel 用其拿到管道 并为管到挂载处理器
ChannelPipeline pipeline=socketChannel.pipeline();
//解码器
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
//编码器
pipeline.addLast(newStringEncoder(CharsetUtil.UTF_8));
//自己实现的 具体业务处理器
pipeline.addLast(new ServerHandler());
}
}
服务端业务处理器
package netty.demo_01;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class ServerHandler extends SimpleChannelInboundHandler<String> {
/*
这里我们首先是复写了父类的channelActive方法,
这个方法没啥特殊的,就是当连接建立好以后就会运行这个方法,通常会用来发送一条指令 看你的具体业务需求咯。
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush("Hello hello big family good my name is Server");
}
/*
channelRead0方法也是一个被复写的方法,这个方法会在 客户端发来数据时被调用,注意tcp有粘包拆包问题,
这里我们暂时不考虑
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
System.out.println(s);
}
//这里我想大家已经猜到了把,没错这个方法会在发生异常时被调用哦!
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
好了哈哈上篇的代码以已经分析完了,是不是有些畏惧了,有些苦恼了,有些激动了。没事的Netty了解其运行机制后真的就没那么困难了。当然想比原生的socket代码是多了一些,没事的都很简单的,慢慢来。还有啊我们的Netty竟然可以自定义编解码器是不是很激动啊。让我们慢慢来,慢慢领略Netty的魅力。
关于昨天的代码大家是否跟着敲下来的呢?运行是否成功了呢?遇到了问题自己是否接解决掉了呢?哈哈那都不要紧,没写完了 今天看着注解接这着写,没成功的有问题没解决的,看看注解想想理解一下,是否就豁然开朗了呢?加油哦!
今天到这里就结束咯,下次见面我会给大家分享一下 Netty纯手工编写一个http服务器哦。那下次见咯!
哦,差点忘记了在最后补上昨天忘记发的Netty的版本: 4.1.30.Final
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.30.Final</version>
</dependency>