1、看!源码之ServerSocket实现

1、ServerSocket实现

本章将以源码形式介绍ServerSocket的实现,并且会将它内部它所实现的方法拉出来单独讲述。

A、
 ServerSocket(SocketImpl impl) {
        this.impl = impl;
        impl.setServerSocket(this);
    }
B、
 public ServerSocket() throws IOException {
        setImpl();
    }
C、
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
        setImpl();
        if (port < 0 || port > 0xFFFF)
            throw new IllegalArgumentException(
                       "Port value out of range: " + port);
        if (backlog < 1)
          backlog = 50;
        try {
            bind(new InetSocketAddress(bindAddr, port), backlog);
        } catch(SecurityException e) {
            close();
            throw e;
        } catch(IOException e) {
            close();
            throw e;
        }
    }

上方为ServerSocket的构造器,这里只列举了三个而他存在五个构造器另外两个都是针对C构造器的重载,所以这里笔者不介绍有兴趣的童鞋可以自己去看看。
A构造器:传入了SocketImpl从名字可以看出他是socket的实现这里暂时不讲因为后面大有文章介绍,还有一个信息就是A构造器是包作用域,代表着我们不能使用只给了同级包使用。
B构造器:比A还简单甚至都没有传实现但是调用了自己的一个setImpl方法,setImpl方法稍后介绍。
C构造器:传入了端口号,队列最大长度,需要绑定的地址。在方法内也调用了setImpl方法,但是与上方不同的是他帮你调用了bind方法,bind方法稍后介绍。
到此构造器就讲完了比较简单并没有什么难度下面介绍impl是什么和setImpl方法的实现。

private SocketImpl impl;
private static SocketImplFactory factory = null;
private void setImpl() {
    if (factory != null) {
        impl = factory.createSocketImpl();
        checkOldImpl();
    } else {
        // No need to do a checkOldImpl() here, we know it's an up to date
        // SocketImpl!
        impl = new SocksSocketImpl();
    }
    if (impl != null)
        impl.setServerSocket(this);
}
private void checkOldImpl() {
    if (impl == null)
        return;
    // SocketImpl.connect() is a protected method, therefore we need to use
    // getDeclaredMethod, therefore we need permission to access the member
    try {
        AccessController.doPrivileged(
            new PrivilegedExceptionAction() {
                public Void run() throws NoSuchMethodException {
                    impl.getClass().getDeclaredMethod("connect",
                                                        SocketAddress.class,
                                                        int.class);
                    return null;
                }
            });
    } catch (java.security.PrivilegedActionException e) {
        oldImpl = true;
    }
}
public boolean getReuseAddress() throws SocketException {
    if (isClosed())
        throw new SocketException("Socket is closed");
    return ((Boolean) (getImpl().getOption(SocketOptions.SO_REUSEADDR))).booleanValue();
}

从上方代码可以看出impl就是ScoketImpl类的一个对象,这个对象稍后详细介绍因为不管是Socket还是ServerSocket都是针对SocketImpl的操作,这里采用了外观模式。
factory 除了impl对象还有 factory对象这个对象使用创建impl他是一个接口这个接口里只有一个方法那就是createSocketImpl(),在jdk的源码中比并没有找到关于这个接口的实现类,笔者觉得应该是用来提供给开发者扩展用的,因为前面介绍的构造器中有个构造器是可以传入实现类的结果他是package的作用域导致开发者不能正常使用,所以写了个接口叫开发者去实现,不仅仅是从package的构造器中看出再有一点是factory是static修饰的有对应的setSocketFactory方法去设置这个方式是静态并且为pulic的所以从而得出factory是官方留着开发者扩展使用。
setImpl 方法这个方法很简单就寥寥几行,第一个if else 就是为了看用户是否自己扩展了SocketImpl如果使用了那么使用用户实现的factory去创建impl,否则使用SocksSocketImpl,最后设置Impl方法中的ServerSocket为当前窗口的ServerSocket。其中还有checkOldImpl这个方法笔者没有看懂因为毫无意义不知道干嘛的,说是校验访问权限但是在SocketImpl中connect方法是abstract的你继承了SocketImpl就必须实现的,所以整个校验毫无意义,但是如果说传入的是抽象的impl呢?这个不存在因为抽象不能new额,在stackoverflow中发了问题有大佬回答据说是校验当前类必须实现connect方法不允许使用父类的因为 getDeclaredMethod获取不到父类的connect,好吧既然如此暂时先不管他因为我们并不需要实现SocketImpl具体原因下方会解释。
getReuseAddress或者set.. 使用来设置当前端口的重用的,可以看出也是操作的impl属性操作设置的,这里需要注意的时候如果使用有存在接口重用的需求那么就可以设置他,一般都建议设置为true并不是说重用之类的而是为了保证进程创建了socket后没有正确释放从而立即重启的时候如果这个属性为false则会报错端口占用当然这只是其中一个场景,还有可以在一个进程里创建多个ServerSocket这样虽然绑定的试一个端口但是可以根据请求数据中的某个标识去分发处理,还有两种使用场景笔者就不一一介绍了可自行白度关键字是SO_REUSEADDR。
setReceiveBufferSize或get.. ,使用来获取和设置socket接受数据的缓冲区大小。
setSoTimeout ,这个方法有点意思并不是C、C++的设置而是java自己加的也是调用impl进行设置SO_TIMEOUT与上方端口重用设置一样后续会讲他的实现和使用。
ServerSocket总结:到这里ServerSocket差不多就结束了,这里只说了他的几个它存在设置的的方法剩下的方法都是对impl属性的获取和处理并没有什么可讲的下一章将讲述SocketImpl.

你可能感兴趣的:(1、看!源码之ServerSocket实现)