HotSpot Net

SherlockCharlie發表於2020-11-10

HotSpot對上層Java網路的支援主要通過Socket來支援。通過套接在TCP/IP傳輸控制協議層和HTTP/FTP/SMTP應用層之間搭建橋樑。傳輸控制層用作業系統層面實現。上層協議藉助Socket進一步實現,Java平臺已為我們實現了方便的應用層協議。如需自行實現自定義協議,可藉助Socket來實現,比如Hadoop中的YARN排程器就藉助Socket和NIO來實現分散式排程。

一.Socket

為了除錯Socket修改Hello類的內容如下

public class Hello{

    public static void main(String[] args){

        try {
            // 監聽指定的埠
            int port = 55533;
            ServerSocket server = new ServerSocket(port);
            // server將一直等待連線的到來
            System.out.println("server waiting ....");
            Socket socket = server.accept();
            // 建立好連線後,從socket中獲取輸入流,並建立緩衝區進行讀取
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len;
            StringBuilder sb = new StringBuilder();
            while ((len = inputStream.read(bytes)) != -1) {
                //注意指定編碼格式,傳送方和接收方一定要統一,建議使用UTF-8
                sb.append(new String(bytes, 0, len,"UTF-8"));
            }
            System.out.println("get message from client: " + sb);
            inputStream.close();
            socket.close();
            server.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

斷點跟蹤ServerSocket的建立

java\net\ServerSocket.java

public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
        setImpl();
        bind(new InetSocketAddress(bindAddr, port), backlog);
    }

IP和埠繫結監聽
java\net\ServerSocket.java

public void bind(SocketAddress endpoint, int backlog) throws IOException {
         getImpl().bind(epoint.getAddress(), epoint.getPort());
         getImpl().listen(backlog);
         bound = true;
    }

java\net\ServerSocket.java

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

java\net\ServerSocket.java

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

java\net\SocketImpl.java

public abstract class SocketImpl implements SocketOptions {
	protected abstract void create(boolean stream) throws IOException;
	protected abstract void bind(InetAddress host, int port) throws IOException;
	protected abstract void listen(int backlog) throws IOException;
}

java\net\AbstractPlainSocketImpl.java

abstract class AbstractPlainSocketImpl extends SocketImpl {
	protected synchronized void create(boolean stream) throws IOException {
        this.stream = stream;
        if (!stream) {
            ResourceManager.beforeUdpCreate();
            // only create the fd after we know we will be able to create the socket
            fd = new FileDescriptor();
            try {
                socketCreate(false);
                SocketCleanable.register(fd, false);
            } catch (IOException ioe) {
                ResourceManager.afterUdpClose();
                fd = null;
                throw ioe;
            }
        } 
    }
}

在PlainSocketImpl定義了一組native方法來對接系統層面的Socket操作
java\net\PlainSocketImpl.java

class PlainSocketImpl extends AbstractPlainSocketImpl{
	native void socketCreate(boolean isServer) throws IOException;
	native void socketBind(InetAddress address, int port)
        throws IOException;
        native void socketCreate(boolean isServer) throws IOException;

    native void socketConnect(InetAddress address, int port, int timeout)
        throws IOException;

    native void socketBind(InetAddress address, int port)
        throws IOException;

    native void socketListen(int count) throws IOException;

    native void socketAccept(SocketImpl s) throws IOException;

}

在HotSpotJNI層面實現了Socket,具體實現在jdk/src/java.base/unix/native/libnet/PlainSocketImpl.c

JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_socketCreate
  (JNIEnv *, jobject, jboolean);

JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_socketConnect
  (JNIEnv *, jobject, jobject, jint, jint);

JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_socketBind
  (JNIEnv *, jobject, jobject, jint);

JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_socketListen
  (JNIEnv *, jobject, jint);

JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_socketAccept
  (JNIEnv *, jobject, jobject);

......

從Socket中獲取SocketInputStream來讀取埠資料
java\net\AbstractPlainSocketImpl.java

abstract class AbstractPlainSocketImpl extends SocketImpl {
	 protected synchronized InputStream getInputStream() throws IOException {
        synchronized (fdLock) {
            if (isClosedOrPending())
                throw new IOException("Socket Closed");
            if (shut_rd)
                throw new IOException("Socket input is shutdown");
            if (socketInputStream == null)
                socketInputStream = new SocketInputStream(this);
        }
        return socketInputStream;
    }
}

讀取資料時通過Socket檔案描述符來讀指定的連線埠
java\net\SocketInputStream.java

class SocketInputStream extends FileInputStream {
	private native int socketRead0(FileDescriptor fd,
                                   byte b[], int off, int len,
                                   int timeout)
}

jdk/src/java.base/unix/native/libnet/SocketInputStream.c

JNIEXPORT jint JNICALL
Java_java_net_SocketInputStream_socketRead0(JNIEnv *env, jobject this,...){
  ......
}

二.網路SPI

網路SPI層主要提供上層使用者介面,Socket實現,提供一個統一的網路框架。此外還實現了DatagramSocket,用於實現資料包。URLConnection標準網路API, URLClassLoader類載入器。

java\net
在這裡插入圖片描述

三.網路協議

網路協議層實現了豐富的應用協議,http,file,ftp,https.smtp,sdp等等。

sun\net
在這裡插入圖片描述
以URLConnection為例在http協議層表現為HttpURLConnection

sun\net\www\protocol\http\HttpURLConnection.java

public void connect() throws IOException {
        synchronized (this) {
            connecting = true;
        }
        plainConnect();
    }

sun\net\www\protocol\http\HttpURLConnection.java

protected void plainConnect()  throws IOException {
	plainConnect0();
}

sun\net\www\protocol\http\HttpURLConnection.java

protected void plainConnect0()  throws IOException {
	 http = getNewHttpClient(url, p, connectTimeout);
}

sun\net\www\http\HttpClient.java

public static HttpClient New(URL url, Proxy p, int to, ...) throws IOException {
  ret = new HttpClient(url, p, to);
}

sun\net\www\http\HttpClient.java

protected HttpClient(URL url, Proxy p, int to) throws IOException {
        proxy = (p == null) ? Proxy.NO_PROXY : p;
        this.host = url.getHost();
        this.url = url;
        port = url.getPort();
        if (port == -1) {
            port = getDefaultPort();
        }
        setConnectTimeout(to);

        capture = HttpCapture.getCapture(url);
        openServer();
    }

sun\net\www\http\HttpClient.java

 @Override
 public void openServer(String server, int port) throws IOException {
        serverSocket = doConnect(server, port);
        OutputStream out = serverSocket.getOutputStream();
            if (capture != null) {
                out = new HttpCaptureOutputStream(out, capture);
            }
            serverOutput = new PrintStream(
                new BufferedOutputStream(out),  false, encoding);
        serverSocket.setTcpNoDelay(true);
 }

sun\net\NetworkClient.java
最終藉助Socket來完成到傳輸控制層的資料

 protected Socket doConnect (String server, int port)
    throws IOException, UnknownHostException {
      //建立套接字
        Socket s;
        if (proxy != null) {
            if (proxy.type() == Proxy.Type.SOCKS) {
                s = AccessController.doPrivileged(
                    new PrivilegedAction<>() {
                        public Socket run() {
                                       return new Socket(proxy);
                                   }});
            } else if (proxy.type() == Proxy.Type.DIRECT) {
                s = createSocket();
            } else {
                // Still connecting through a proxy
                // server & port will be the proxy address and port
                s = new Socket(Proxy.NO_PROXY);
            }
        } else {
            s = createSocket();
        }
        //連線
        if (connectTimeout >= 0) {
            s.connect(new InetSocketAddress(server, port), connectTimeout);
        } else {
            if (defaultConnectTimeout > 0) {
                s.connect(new InetSocketAddress(server, port), defaultConnectTimeout);
            } else {
                s.connect(new InetSocketAddress(server, port));
            }
        }
        if (readTimeout >= 0)
            s.setSoTimeout(readTimeout);
        else if (defaultSoTimeout > 0) {
            s.setSoTimeout(defaultSoTimeout);
        }
        return s;
    }

四.HTTP模組

HTTP模組實現了HTTP協議的各種細節。
在這裡插入圖片描述

相關文章