废话两句
这次更新拖了很长时间,第一是自己生病了,第二是因为最开始这篇想写的很大,然后构思了很久,发现不太合适把很多东西写在一起,所以做了点拆分,准备国庆前完成这篇博客。
目的&&期望
开门见山,先说下这篇文章能学到什么
- netty中channel的实现类的实现方式和作用
- 自己动手写netty框架的channel部分和bind方法,完成服务端的端口绑定
Bootstrap主要成员
上一篇我们看了Bootstrap类的大致的组成,也了解了它的一些配置方法,其实都是在设置它的成员变量,有些是ServerBootstrap的也有是它的父类AbstractBootstrap的,在这里,我觉得主要有下面几个。
- EventLoopGroup
- Channel
- Handler
当然,这些只是它的一些接口,具体有多种实现,除了Nio,它也有Bio等其他版本的,继承链也很复杂,这里我们先看下channel的实现。
NioServerSocketChannel
继承链
说到channel,它是Java Nio中的一个重要的成员,不过在netty中,channel是自己封装的一个更抽象的东西,在Nio版本的实现中,它也维护了Java Nio的channel,不过这些都是后话了,我们先看一下它的继承链,这里我自己画了一个简单的。
这里可以看到,它的继承有两个分支,准确的说一条线是继承抽象类,一条线是实现接口,这么做,可以说是netty设计上的考虑,一方面,它是nio的channel,但是同时它又是server端的channel,所以netty选择了这样做来实现。
与Nio的摩擦
看到这里你可能会疑问,这个channel是netty自己封装的,那他是怎么和Nio的channel发生碰撞的呢?
首先,它在构造方法里利用Java Nio的selectProvider 打开了一个socketChannel,然后把这个Nio的Channel和它的准备状态传给父类构造器,然后父类构造器里存储到成员变量中。下面无耻的贴点源码。
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
//这个DEFAULT_SELECTOR_PROVIDER其实就是select的provide
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
//然后是newSocket打开了一个Channel
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException(
"Failed to open a server socket.", e);
}
}
//最后是调用父类的构造
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
看到这些,我想大家对Channel有所了解了吧,现在我们来开始实现自己的版本
自己动手
热身
在这之前先说下bootstrap的bind方法,这边netty实现的比较复杂,它是通过各种线程异步,还有它的一个内部类unsafe,最后调用到NioServerSocketChannel的doBind方法,doBind在调用Java Nio的bind方法。这里我们先实现简单版本,它的关于线程的一些设计,我们说到eventloop之后再说。
先来实现Channel和Channel工厂类,方便我们动态new出不同的Channel。
public interface Channel {
void bind(InetSocketAddress inetSocketAddress);
}
public interface ChannelFactory<T extends Channel>{
T newChannel();
}
public class ReflectChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectChannelFactory(Class clazz) {
this.clazz = clazz;
}
public T newChannel() {
try {
return clazz.newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("can not init");
}
}
}
启动类
我们也学netty,把启动类抽象成两层,方便以后写客户端。
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B,C>,C extends Channel> {
private ChannelFactory<C> channelFactory;
public B channel(Class<? extends C> clazz){
this.channelFactory = new ReflectChannelFactory<C>(clazz);
return (B)this;
}
public B bind(Integer port){
validate();
doBind(port);
return (B)this;
}
private void doBind(Integer port){
Channel channel = channelFactory.newChannel();
channel.bind(new InetSocketAddress(port));
}
public void validate(){
if(channelFactory == null){
throw new IllegalArgumentException("channelFactory should be initialized");
}
}
}
//服务端暂时没有用到额外的方法,先直接继承就OK了
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap,Channel>{
}
Channel实现类
最后我们实现我们的NioChannel,相比netty,我们目前只做了两层抽象。
public abstract class AbstractNioChannel implements Channel{
private final SelectableChannel ch;
public AbstractNioChannel(SelectableChannel ch) {
this.ch = ch;
}
protected SelectableChannel ch() {
return ch;
}
protected abstract void doBind(SocketAddress localAddress) throws Exception;
public void bind(InetSocketAddress inetSocketAddress) {
try {
doBind(inetSocketAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class NioServerSocketChannel extends AbstractNioChannel {
public NioServerSocketChannel() {
super(newSocket());
}
private static ServerSocketChannel newSocket(){
try {
return SelectorProvider.provider().openServerSocketChannel();
} catch (IOException e) {
throw new IllegalArgumentException("can not open socket");
}
}
@Override
protected ServerSocketChannel ch(){
return (ServerSocketChannel) super.ch();
}
@Override
protected void doBind(SocketAddress localAddress) throws Exception{
ch().socket().bind(localAddress);
}
}
测试
最后写个测试类,来测试下
public class Test {
public static void main(String[] args) {
ServerBootstrap sb = new ServerBootstrap();
sb.channel(NioServerSocketChannel.class).bind(9999);
}
}
最后
当然,这个连残缺版netty都算不上,在绑定完端口后就自动结束了。别着急,我们慢慢来,下一篇我们会了解EventLoopGroup以及他的成员EventLoop,然后,完善我们的程序,增加其接收数据的能力。
文章的源码我会同步更新到我的gayHub上,欢迎大家star,哈哈。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。