3. Java Socket实现

1. SocketImpl的类树如下:

java.net.SocketImpl(抽象类)
    --> java.net.AbstractPlainSocketImpl(抽象实现类)
        --> java.net.TwoStacksPlainSocketImpl(真正实现类,Vista以下版本)
        --> java.net.DualStackPlainSocketImpl(真正实现类,Vista及以上版本)
        --> java.net.PlainSocketImpl(上述实现类的代理类)
            --> java.net.SocksSocketImpl(SOCKS TCP套接字实现类)

2. 类字段

  • SocketImpl
// 指向服务器或客户端实例,只能有一个非空
Socket socket = null;
ServerSocket serverSocket = null;

// 套接字文件描述符
FileDescriptor fd;
// 远端IP地址
InetAddress address;
// 远端端口
int port;
// 本地端口
int localport;
  • PlainSocketImpl
// 代理真正的套接字实例
// TwoStacksPlainSocketImpl或DualStackPlainSocketImpl
AbstractPlainSocketImpl impl;
  • ServerSocket
// 指向套接字代理类实例(SocksSocketImpl)
SocketImpl impl;

// 状态
// 是否创建套接字
boolean created = false;
// 是否绑定本地地址
boolean bound = false;
// 是否关闭套接字
boolean closed = false;
  • Socket
// 指向套接字代理类实例(SocksSocketImpl)
SocketImpl impl;

// 状态
// 是否创建套接字
boolean created = false;
// 是否绑定本地地址
boolean bound = false;
// 是否建立连接
boolean connected = false;
// 是否关闭读
boolean shutIn = false;
// 是否关闭写
boolean shutOut = false;
// 是否关闭套接字
boolean closed = false;

3. 创建套接字

  • ServerSocket

创建真正套接字实例:

public ServerSocket() throws IOException {
    setImpl();
}

// ServerSocket
private void setImpl() {
    // SocketImpl#impl
    impl = new SocksSocketImpl();
    impl.setServerSocket(this);
}

SocksSocketImpl() {
    // Nothing needed
}

// SocksSocketImpl的父类
PlainSocketImpl() {
    if (useDualStackImpl) {
        // AbstractPlainSocketImpl#impl
        impl = new DualStackPlainSocketImpl(exclusiveBind);
    } else {
        impl = new TwoStacksPlainSocketImpl(exclusiveBind);
    }
}

public DualStackPlainSocketImpl(boolean exclBind) {
    // 套接字是否互斥绑定,Windows为true
    exclusiveBind = exclBind;
}

初始化套接字:

// ServerSocket
SocketImpl getImpl() throws SocketException {
    if (!created)
        createImpl();
    return impl;
}

// ServerSocket
void createImpl() throws SocketException {
    if (impl == null)
        setImpl();
    try {
        impl.create(true);
        created = true;
    } catch (IOException e) {
        throw new SocketException(e.getMessage());
    }
}

// AbstractPlainSocketImpl
void create(boolean stream) throws IOException {
    this.stream = stream;
    if (!stream) {
        // SocketImpl#fd
        fd = new FileDescriptor();
        try {
            // 创建数据报套接字UDP
            socketCreate(false);
        } catch (IOException ioe) {
            fd = null;
            throw ioe;
        }
    } else {
        fd = new FileDescriptor();
        // 创建流式套接字TCP
        socketCreate(true);
    }

    // Socket或ServerSocket置为已创建状态
    if (socket != null)
        socket.setCreated();
    if (serverSocket != null)
        serverSocket.setCreated();
}

// DualStackPlainSocketImpl
void socketCreate(boolean stream) throws IOException {
    // 调用native方法创建套接字
    int newfd = socket0(stream, false /*v6 Only*/);
    // SocketImpl#fd
    fdAccess.set(fd, newfd);
}

// ServerSocket:已创建
void setCreated() {
    created = true;
}
  • Socket
public Socket() {
    setImpl();
}

void setImpl() {
    impl = new SocksSocketImpl();
    impl.setSocket(this);
}

SocketImpl getImpl() {
    if (!created)
        createImpl(true);
    return impl;
}

// 后续同ServerSocket

4. 服务器绑定本地地址

// ServerSocket
void bind(SocketAddress endpoint, int backlog) {
    // getImpl()触发套接字初始化
    // 绑定本地地址
    getImpl().bind(epoint.getAddress(), epoint.getPort());
    getImpl().listen(backlog);
}

//AbstractPlainSocketImpl
void bind(InetAddress address, int lport) {
    socketBind(address, lport);

    // Socket或ServerSocket置为已绑定状态
    if (socket != null)
        socket.setBound();
    if (serverSocket != null)
        serverSocket.setBound();
}

// DualStackPlainSocketImpl
void socketBind(InetAddress address, int port) {
    //  套接字文件描述符
    int nativefd = checkAndReturnNativeFD();
    // 调用native方法绑定本地地址
    bind0(nativefd, address, port, exclusiveBind);
    // 本地端口
    if (port == 0) {
        localport = localPort0(nativefd);
    } else {
        localport = port;
    }
}

5. 服务器监听连接请求

// ServerSocket
void bind(SocketAddress endpoint, int backlog) {
    // getImpl()触发套接字初始化
    getImpl().bind(epoint.getAddress(), epoint.getPort());
    // 监听连接请求
    getImpl().listen(backlog);
}

// AbstractPlainSocketImpl
void listen(int count) {
    socketListen(count);
}

// DualStackPlainSocketImpl
void socketListen(int backlog) {
    // 套接字文件描述符
    int nativefd = checkAndReturnNativeFD();
    // 调用native方法监听请求
    listen0(nativefd, backlog);
}

6. 客户端请求连接

// Socket
void connect(SocketAddress endpoint, int timeout) {
    // 远程地址和端口
    InetSocketAddress epoint = (InetSocketAddress) endpoint;
    InetAddress addr = epoint.getAddress ();
    int port = epoint.getPort();

    // 连接请求
    impl.connect(addr.getHostName(), port);

    // 已连接
    connected = true;
    // 如果套接字在连接之前没有绑定,内核将选择临时端口和本地地址
    bound = true;
}

// AbstractPlainSocketImpl
void connect(String host, int port){
    InetAddress address = InetAddress.getByName(host);
    if (address.isAnyLocalAddress()) {
        doConnect(InetAddress.getLocalHost(), port, timeout);
    } else {
        doConnect(address, port, timeout);
    }
}

// AbstractPlainSocketImpl
void doConnect(InetAddress address, int port, int timeout) {
    socketConnect(address, port, timeout);
    if (socket != null) {
        // Socket设置为已绑定、已连接
        socket.setBound();
        socket.setConnected();
    }
}

// DualStackPlainSocketImpl
void socketConnect(InetAddress address, int port, int timeout) {
    //  套接字文件描述符
    int nativefd = checkAndReturnNativeFD();

    int connectResult;
    if (timeout <= 0) {
        // 阻塞直到连接成功
        connectResult = connect0(nativefd, address, port);
    } else {
        // 阻塞,直到连接成功或超时
        configureBlocking(nativefd, false);
        try {
            connectResult = connect0(nativefd, address, port);
            if (connectResult == WOULDBLOCK) {
                // 调用native方法超时等待连接
                waitForConnect(nativefd, timeout);
            }
        } finally {
            configureBlocking(nativefd, true);
        }
    }
}

7. 服务器接受连接

// ServerSocket
public Socket accept() {
    Socket s = new Socket((SocketImpl) null);
    implAccept(s);
    return s;
}

// ServerSocket
void implAccept(Socket s) {
    SocketImpl si = null;

    // 创建SocketImpl实例
    s.setImpl();
    
    si = s.impl;
    s.impl = null;
    si.address = new InetAddress();
    si.fd = new FileDescriptor();

    getImpl().accept(si);
    
    s.impl = si;
    s.connected = true;
    s.created = true;
    s.bound = true;
}

// AbstractPlainSocketImpl
void accept(SocketImpl s) {
    socketAccept(s);
}

// DualStackPlainSocketImpl
void socketAccept(SocketImpl s) {
    // ServerSocket监听的文件描述符
    int nativefd = checkAndReturnNativeFD();

    // 与客户端连接的文件描述符
    // 如果复用监听fd,则只能与一个客户端连接通信
    // 为连接新建fd,使服务器能够连接多个客户端
    int newfd = -1;
    // 保存客户端的地址和端口
    InetSocketAddress[] isaa = new InetSocketAddress[1];
    
    if (timeout <= 0) {
        // 调用本地方法与客户端建立连接
        newfd = accept0(nativefd, isaa);
    } else {
        // 超时连接
        configureBlocking(nativefd, false);
        try {
            waitForNewConnection(nativefd, timeout);
            newfd = accept0(nativefd, isaa);
            if (newfd != -1) {
                configureBlocking(newfd, true);
            }
        } finally {
            configureBlocking(nativefd, true);
        }
    }
    
    // 将新SocketImpl的fd设置为连接的文件描述符
    // 使用该fd与客户端进行通信
    fdAccess.set(s.fd, newfd);
    
    // 远程,即客户端的地址和端口
    InetSocketAddress isa = isaa[0];
    s.port = isa.getPort();
    s.address = isa.getAddress();
    // 新SocketImpl的本地端口即ServerSocket监听的端口
    s.localport = localport;
}

8. 通信的InputStream和OutputStream

// Socket
InputStream getInputStream() {
    return impl.getInputStream();
}
OutputStream getOutputStream() {
    return impl.getOutputStream();
}

// AbstractPlainSocketImpl
InputStream getInputStream() {
    socketInputStream = new SocketInputStream(this);
    return socketInputStream;
}
OutputStream getOutputStream() {
    socketOutputStream = new SocketOutputStream(this);
    return socketOutputStream;
}
  • SocketInputStream extends FileInputStream
// 指向连接的文件描述符
FileDescriptor fd;
// 指向连接的真正套接字
AbstractPlainSocketImpl impl;
// 指向连接的Socket实例
Socket socket = null;

SocketInputStream(AbstractPlainSocketImpl impl) {
    fd = impl.getFileDescriptor();
    this.impl = impl;
    socket = impl.getSocket();
}

int read(byte b[], int off, int length, int timeout) {
    int n = socketRead(fd, b, off, length, timeout);
}

private int socketRead(FileDescriptor fd, byte b[], int off, int len, int timeout) {
    // 使用套接字接受原语读取
    return socketRead0(fd, b, off, len, timeout);
}

// 流的关闭会导致Socket关闭
public void close() {
    if (socket != null) {
        if (!socket.isClosed()) {
            socket.close();
        }
    } else {
        impl.close();
    }
}
  • SocketOutputStream extends FileOutputStream
// 指向连接的文件描述符
FileDescriptor fd;
// 指向连接的真正套接字
AbstractPlainSocketImpl impl;
// 指向连接的Socket实例
Socket socket = null;

SocketOutputStream(AbstractPlainSocketImpl impl) {
    fd = impl.getFileDescriptor();
    this.impl = impl;
    socket = impl.getSocket();
}

void write(int b) {
    temp[0] = (byte)b;
    socketWrite(temp, 0, 1);
}

void socketWrite(byte b[], int off, int len) {
    // 写到套接字
    socketWrite0(fd, b, off, len);
}

// 流的关闭会导致Socket关闭
public void close() {
    if (socket != null) {
        if (!socket.isClosed()) {
            socket.close();
        }
    } else {
        impl.close();
    }
}
  • shutdownInput()和shutdownOutput()
// Socket
void shutdownInput() {
    getImpl().shutdownInput();
    shutIn = true;
}
void shutdownOutput() {
    getImpl().shutdownOutput();
    shutOut = true;
}

// AbstractPlainSocketImpl
protected void shutdownInput() throws IOException {
    if (fd != null) {
        // SHUT_RD = 0
        socketShutdown(SHUT_RD);
        if (socketInputStream != null) {
            socketInputStream.setEOF(true);
        }
      shut_rd = true;
    }
}
protected void shutdownOutput() {
    if (fd != null) {
        // SHUT_WR = 1
        socketShutdown(SHUT_WR);
        shut_wr = true;
    }
}

// DualStackPlainSocketImpl
// howto:0,input;1,output;
void socketShutdown(int howto) throws IOException {
    int nativefd = checkAndReturnNativeFD();
    shutdown0(nativefd, howto);
}

9. 关闭

// ServerSocket/Socket
public void close() {
    synchronized(closeLock) {
        if (isClosed())
            return;
        if (created)
            impl.close();
        closed = true;
    }
}

// AbstractPlainSocketImpl
protected void close() throws IOException {
    synchronized(fdLock) {
        if (fd != null) {
            if (fdUseCount == 0) {
                if (closePending) {
                    return;
                }
                closePending = true;
                // 我们分两步关闭文件描述符——首先是“预关闭”,它关闭套接字,但不释放底层文件描述符。
                // 由于未传输数据和长时间的逗留间隔,此操作可能很长。
                // 一旦预关闭完成,我们做实际的套接字来释放fd。
                try {
                    socketPreClose();
                } finally {
                    socketClose();
                }
                fd = null;
                return;
                
            } else {
                // 还需要递减fdUseCount来通知释放fd的最后一个线程关闭它。
                if (!closePending) {
                    closePending = true;
                    fdUseCount--;
                    socketPreClose();
                }
            }
        }
    }
}
void socketPreClose() {
    socketClose0(true);
}
void socketClose() {
    socketClose0(false);
}

// DualStackPlainSocketImpl
void socketClose0(boolean useDeferredClose/*unused*/) throws IOException {
    final int nativefd = fdAccess.get(fd);
    fdAccess.set(fd, -1);
    close0(nativefd);
}

你可能感兴趣的:(3. Java Socket实现)