2017-09-12 20:54:11 +0000   |     java web socket tcp ip   |   Viewed times   |    

前言

我们已经知道Socket是向程序员提供TCP/IP服务的一个抽象。经典的Berkeley Socket API提供的函数如下,

具体参考这篇文章 -> http://www.ciaoshen.com/algorithm/leetcode/2017/09/12/berkeley-socket-api.html

Java有java.net.Socketjava.net.ServerSocket两个类库,向程序员提供TCP/IP网络传输服务。

另外一篇文章用一个Demo演示了怎么用ServerSocket搭建一个简易的HTTP服务器。 -> http://www.ciaoshen.com/algorithm/leetcode/2017-09-10-how-tomcat-works-chapter-two-httpserver1.html

这篇文章主要分析java.net.ServerSockt的源码,看它内部具体是怎么工作的。以ServerSocket#accept()函数为例,它最终调用的是PlainSocketImpl#socketAccept()本地方法(Native Method)。

所以,理解Java的套接字接口,主要有两个层面,

首先,程序员日常面对的是java.net.Socketjava.net.ServerSocket两个类库提供的接口。 再进一步,这两个类库本身面向的是伯克利套接字Socket API定义的一组行为。可以把这组接口理解为操作系统向其他应用或者编程语言提供的服务。

ServerSocket#accept()函数

ServerSocket#accept()函数调动ServerSocket#implAccept(Socket)函数。需要传递一个空的Socket实例进去,最后返回的 已连接套接字(connected socket) 就是这个实例的引用。

public class ServerSocket implements java.io.Closeable {

    // ... ... 省略其他代码

    public Socket accept() throws IOException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        if (!isBound())
            throw new SocketException("Socket is not bound yet");
        Socket s = new Socket((SocketImpl) null); // 构造一个空的套接字实例
        implAccept(s); // 把空的套接字实例传递给implAccept(Socket s)函数继续处理
        return s;
    }
}

ServerSocket#implAccept(Socket)函数

ServerSocket#implAccept(Socket s)函数通过ServerSocket#getImpl()函数找到ServerSocket#impl字段的引用,调用它的accept(SocketImpl si)方法。

public class ServerSocket implements java.io.Closeable {

    // ... ... 省略其他代码

    /**
     * The implementation of this Socket.
     */
    private SocketImpl impl; // impl暴露的是SocketImpl接口

    protected final void implAccept(Socket s) throws IOException {
        SocketImpl si = null;
        try {
            if (s.impl == null)
              s.setImpl();
            else {
                s.impl.reset();
            }
            si = s.impl;
            s.impl = null;
            si.address = new InetAddress();
            si.fd = new FileDescriptor();
            getImpl().accept(si); // 关键调用在这里!

            SecurityManager security = System.getSecurityManager();
            if (security != null) {
                security.checkAccept(si.getInetAddress().getHostAddress(),
                                     si.getPort());
            }
        } catch (IOException e) {
            if (si != null)
                si.reset();
            s.impl = si;
            throw e;
        } catch (SecurityException e) {
            if (si != null)
                si.reset();
            s.impl = si;
            throw e;
        }
        s.impl = si;
        s.postAccept();
    }
}

ServerSocket#impl字段对外暴露的是SocketImpl接口

ServerSocket#impl字段是一个SocketImpl实例,所以调用的是SocketImpl#accept(SocketImpl)函数。

public class ServerSocket implements java.io.Closeable {

    // ... ... 省略其他代码

    /**
     * The implementation of this Socket.
     */
    private SocketImpl impl;

    SocketImpl getImpl() throws SocketException {
        if (!created)
            createImpl();
        return impl;    // 返回的是impl字段
    }
}

这个ServerSocket#impl字段在ServerSocket()构造函数里就初始化了。但实际上SocketImpl是一个抽象类(相当于一个接口)。

public abstract class SocketImpl implements SocketOptions {
    // ... ... 省略代码
}

继续看ServerSocket()的构造函数,调用setImpl()函数初始化impl字段。

public class ServerSocket implements java.io.Closeable {

    // ... ... 省略其他代码

    public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
        setImpl(); // 初始化impl字段
        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#impl字段实际是SocksSocketImpl类的实例

ServerSocket#setImpl()函数里实际构造的是SocksSocketImpl()类实例。这里的factory默认是null,用户可以通过ServerSocket#setSocketFactory(java.net.SocketImplFactory)方法设置工厂,但默认的是不使用工厂。

public class ServerSocket implements java.io.Closeable {

    // ... ... 省略其他代码

    /**
     * The factory for all server sockets.
     */
    private static SocketImplFactory factory = null;

    private void setImpl() {
        if (factory != null) { // 工厂默认为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(); // impl实际是SocksSocketImpl类的实例
        }
        if (impl != null)
            impl.setServerSocket(this);
    }
}

SocksSocketImpl#accept(SocketImpl)函数是怎么实现的?

SocksSocketImpl类继承自PlainSocketImpl

class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {

    // ... ... 省略代码

}

PlainSocketImpl类继承自AbstractPlainSocketImpl抽象类

class PlainSocketImpl extends AbstractPlainSocketImpl {

    // ... ... 省略代码

}

SocksSocketImpl#accept(Socket)AbstractPlainSocketImpl#accept(Socket)中定义

AbstractPlainSocketImpl#accept(Socket)函数调用AbstractPlainSocketImpl#socketAccept(Socket)函数。

class PlainSocketImpl extends AbstractPlainSocketImpl {

    // ... ... 省略代码

    protected void accept(SocketImpl s) throws IOException {
        acquireFD();
        try {
            socketAccept(s);
        } finally {
            releaseFD();
        }
    }
}

AbstractPlainSocketImpl#socketAccept(SocketImpl)函数在PlainSocketImpl类里实现

AbstractPlainSocketImpl类只是一个骨架实现,

abstract class AbstractPlainSocketImpl extends SocketImpl {

    // ... ... 省略代码

    abstract void socketAccept(SocketImpl s) throws IOException; // 骨架实现类没有实现该方法
}

AbstractPlainSocketImpl#socketAccept(SocketImpl)具体在PlainSocketImpl类里实现。实际调用的是一个叫socketAccept(SocketImpl)的本地方法。不是用Java写的代码。

class PlainSocketImpl extends AbstractPlainSocketImpl {

    // ... ... 省略代码

    native void socketAccept(SocketImpl s) throws IOException; // 调用本地方法,不是用Java代码实现的,追踪结束

}

总结

可以理解为,

ServerSocket#accept()函数中最终实际工作的代码在一个系统调用中。不是Java代码。

所以,

程序员面向的是SocketServerSocket两个类库提供的服务,而这两个类库面向的是基于伯克利套接字Berkeley Socket API的一组系统调用提供的服务。 `