java-NIO-ServerSocketChannel

ServerSocketChannel

一. Channel接口

public interface Channel extends Closeable {
    public boolean isOpen();
    public void close() throws IOException;

}

二. ServerSocketChannel

总体代码如下:

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ServerSocket socket = serverSocketChannel.socket();
        socket.bind(new InetSocketAddress(9999));

        while(true){
            SocketChannel socketChannel =
                    serverSocketChannel.accept();
            //do something with socketChannel...

ServerSocketChannel的open方法如下

   public static ServerSocketChannel open() throws IOException {
        return SelectorProvider.provider().openServerSocketChannel();
    }

三. 创建SelectorProvider,构造ServerSocketChannelImpl

    public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

doPrivileged是本地方法,把PrivilegedAction这个接口函数传进去

public interface PrivilegedAction<T> {
    T run();
}

run方法,返回SelectorProvider
然后通过如下三种方式尝试获取provider

  • java.nio.channels.spi.SelectorProvider
  • loadProviderAsService() 加载
  • sun.nio.ch.DefaultSelectorProvider.create()

但是最后成功的只有第三种方法
DefaultSelectorProvider根据平台区分,linux平台如下;

public class DefaultSelectorProvider {

    private DefaultSelectorProvider() { }

	//创建EPollSelectorProvider
    @SuppressWarnings("unchecked")
    private static SelectorProvider createProvider(String cn) {
        Class<SelectorProvider> c;
        try {
            c = (Class<SelectorProvider>)Class.forName(cn);
        } catch (ClassNotFoundException x) {
            throw new AssertionError(x);
        }
        try {
            return c.newInstance();
        } catch (IllegalAccessException | InstantiationException x) {
            throw new AssertionError(x);
        }
    }

    public static SelectorProvider create() {
        String osname = AccessController.doPrivileged(new GetPropertyAction("os.name"));
        if (osname.equals("SunOS"))
            return createProvider("sun.nio.ch.DevPollSelectorProvider");
        if (osname.equals("Linux"))
            return createProvider("sun.nio.ch.EPollSelectorProvider");
        return new sun.nio.ch.PollSelectorProvider();
    }

linux平台下利用反射创建EPollSelectorProvider
经过这个过程,把SelectorProvider这个类的provider修改为EPollSelectorProvider,并返回
继承关系如下:
EPollSelectorProvider->SelectorProviderImpl->SelectorProvider

openServerSocketChannel
openServerSocketChannel方法,把自身EPollSelectorProvider作为参数传入,并且构造一个ServerSocketChannelImpl

   public ServerSocketChannel openServerSocketChannel() throws IOException {
        return new ServerSocketChannelImpl(this);
    }

四. ServerSocketChannel

可以看出ServerSocketChannel是ServerSocketChannelImpl,其socket方法如下
把ServerSocketChannelImpl作为参数,传入ServerSocketAdaptor,然后构造了一个ServerSocketAdaptor返回

    public ServerSocket socket() {
        synchronized (stateLock) {
            if (socket == null)
                socket = ServerSocketAdaptor.create(this);
            return socket;
        }
    }

ServerSocketAdaptor是一个ServerSocket
内部操持了一个重要的参数

private final ServerSocketChannelImpl ssc;

ServerSocketAdaptor
继承关系如下:
ServerSocketAdaptor->ServerSocket
够造时把ServerSocketChannelImpl传入,最后调用默认的构造方法,并设置内部的ssc为ServerSocketChannelImpl

    public static ServerSocket create(ServerSocketChannelImpl ssc) {
        try {
            return new ServerSocketAdaptor(ssc);
        } catch (IOException x) {
            throw new Error(x);
        }
    }

bind(new InetSocketAddress(9999))调用的是ServerSocketAdaptor的bind方法
InetSocketAddress实际是SocketAddress的子类,构造过:

	public InetSocketAddress(int port) {
        this(InetAddress.anyLocalAddress(), port);
    }

InetAddress管理ip地址的工具类

    static InetAddress anyLocalAddress() {
        return impl.anyLocalAddress();
    }

impl的构造
InetAddress的静态初始化代码构造,最后构造出java.net.Inet4AddressImpl

impl = InetAddressImplFactory.create();

InetAddressImplFactory类在InetAddress内部,isIPv6Supported获取系统参数,判断是否ipv6模式,然后提供一个比如Inet4AddressImpl,传入loadImpl方法

class InetAddressImplFactory {
    static InetAddressImpl create() {
        return InetAddress.loadImpl(isIPv6Supported() ?
                                    "Inet6AddressImpl" : "Inet4AddressImpl");
    }
    static native boolean isIPv6Supported();
}

利用反射构造InetAddressImpl

    static InetAddressImpl loadImpl(String implName) {
        Object impl = null;
		//从系统获取impl.prefix的value,如果没有就为空,这里为空
        String prefix = AccessController.doPrivileged(
                      new GetPropertyAction("impl.prefix", ""));
        try {
            impl = Class.forName("java.net." + prefix + implName).newInstance();
        } catch (ClassNotFoundException e) {
            System.err.println("Class not found: java.net." + prefix +
                               implName + ":\ncheck impl.prefix property " +
                               "in your properties file.");
        } catch (InstantiationException e) {
            System.err.println("Could not instantiate: java.net." + prefix +
                               implName + ":\ncheck impl.prefix property " +
                               "in your properties file.");
        } catch (IllegalAccessException e) {
            System.err.println("Cannot access class: java.net." + prefix +
                               implName + ":\ncheck impl.prefix property " +
                               "in your properties file.");
        }

        if (impl == null) {
            try {
                impl = Class.forName(implName).newInstance();
            } catch (Exception e) {
                throw new Error("System property impl.prefix incorrect");
            }
        }
        return (InetAddressImpl) impl;
    }

anyLocalAddress
我们知道构造的是java.net.Inet4AddressImpl

    public synchronized InetAddress anyLocalAddress() {
        if (anyLocalAddress == null) {
            anyLocalAddress = new Inet4Address(); // {0x00,0x00,0x00,0x00}
            anyLocalAddress.holder().hostName = "0.0.0.0";
        }
        return anyLocalAddress;
    }

anyLocalAddresss是一个Inet4Address构造过程
Inet4Address->InetAddress

  	Inet4Address() {
        super();
        holder().hostName = null;
        holder().address = 0;
        holder().family = IPv4;
    }

父类中构造的这个holder,设置为0.0.0.0

    InetAddress() {
        holder = new InetAddressHolder();
    }

最后返回的是一个Inet4Address,并且hostname为0

回到InetSocketAddress创建

 	public InetSocketAddress(int port) {
        this(InetAddress.anyLocalAddress(), port);
    }

继续调用
InetSocketAddress内部也创建一个InetSocketAddressHolder
传入的addr就是构建的Inet4Address

 	public InetSocketAddress(InetAddress addr, int port) {
        holder = new InetSocketAddressHolder(
                        null,
                        addr == null ? InetAddress.anyLocalAddress() : addr,
                        checkPort(port));
    }

主要构建并返回一个InetSocketAddressHolder,默认的参数如下

hostname = null;
addr = Inet4Address;
port = port;

这样就构建了一个InetSocketAddress,其实都是0

五. bind方法

回顾一下

    ServerSocket socket = serverSocketChannel.socket();
    socket.bind(new InetSocketAddress(9999));

ServerSocketChannelImpl调用socket方法,其实是ServerSocketAdaptor调用了create方法,并把ServerSocketChannelImpl作为参数传进去
最后构建了一个ServerSocketAdaptor,然后返回,后来调用bind方法就是ServerSocketAdaptor的bind方法

  socket.bind(new InetSocketAddress(9999));

把Inet4Address传入

 	public void bind(SocketAddress local) throws IOException {
        bind(local, 50);
    }

继续bind调用的ServerSocketChannelImpl的bind方法

 	public void bind(SocketAddress local, int backlog) throws IOException {
        if (local == null)
            local = new InetSocketAddress(0);
        try {
            ssc.bind(local, backlog);
        } catch (Exception x) {
            Net.translateException(x);
        }
    }

ServerSocketChannelImpl的bind方法如下
调用c方法,做了两个操作

  • 把文件描述符,InetSocketAddress和端口号绑定到一起
  • 监听这个端口号
 	public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
        synchronized (lock) {
            if (!isOpen())
                throw new ClosedChannelException();
            if (isBound())
                throw new AlreadyBoundException();
            InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
                Net.checkAddress(local);
            SecurityManager sm = System.getSecurityManager();
            if (sm != null)
                sm.checkListen(isa.getPort());
			//啥也没做
            NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
			//调用bind和listen
            Net.bind(fd, isa.getAddress(), isa.getPort());
            Net.listen(fd, backlog < 1 ? 50 : backlog);
            synchronized (stateLock) {
                localAddress = Net.localAddress(fd);
            }
        }
        return this;
    }

五. accept方法

可以看出,这里比较有趣的一个地方,用ServerSocketAdaptor去bind,用ServerSocketChannelImpl去accept

accept0(this.fd, newfd, isaa)这个操作是非阻塞的,通过返回值n控制是否阻塞SocketChannelImpl返回
1 成功
IOStatus.UNAVAILABLE 失败,非阻塞,并且没有pending
IOStatus.INTERRUPTED 失败,阻塞
如果这个连接创建好了,设置阻塞模式,默认为true,然后创建一个

    public SocketChannel accept() throws IOException {
        synchronized (lock) {
            if (!isOpen())
                throw new ClosedChannelException();
            if (!isBound())
                throw new NotYetBoundException();
            SocketChannel sc = null;

            int n = 0;
            FileDescriptor newfd = new FileDescriptor();
            InetSocketAddress[] isaa = new InetSocketAddress[1];

            try {
                begin();
                if (!isOpen())
                    return null;
                thread = NativeThread.current();
                for (;;) {
					//调用本地的accept方法
                    n = accept0(this.fd, newfd, isaa);
                    if ((n == IOStatus.INTERRUPTED) && isOpen())
                        continue;
                    break;
                }
            } finally {
                thread = 0;
                end(n > 0);
                assert IOStatus.check(n);
            }

            if (n < 1)
                return null;
			//配置文件描述符是否阻塞
            IOUtil.configureBlocking(newfd, true);
			//远端连接的地址
            InetSocketAddress isa = isaa[0];
			//传入连接,连理一个新的SocketChannelImpl
            sc = new SocketChannelImpl(provider(), newfd, isa);
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                try {
                    sm.checkAccept(isa.getAddress().getHostAddress(),
                                   isa.getPort());
                } catch (SecurityException x) {
                    sc.close();
                    throw x;
                }
            }
            return sc;
        }
    }
posted @ 2016-10-05 21:59  zhangshihai1232  阅读(422)  评论(0)    收藏  举报