General situation of

The JDK provides the ServerSocket class as an implementation of a ServerSocket that allows a host to listen on a port to receive requests from other hosts, and then respond to the requester. The actual internal implementation is implemented through the SocketImpl class, which provides the factory schema so that other implementations can be changed through the factory schema if desired.

Inheritance structure

--java.lang.Object
  --java.net.ServerSocket
Copy the code

The class diagram

The ServerSocket class is implemented through SocketImpl, so you can see that it uses SocketImpl, but because of the differences between Windows and UNIX-like systems, different versions of Windows require different processing. So the classes of the two systems are not the same.

The SocketImpl class implements the SocketOptions interface. AbstractPlainSocketImpl is an abstraction of the implementation of the original socket. The PlainSocketImpl class is a proxy class that proxies two different implementations, TwoStacksPlainSocketImpl and DualStackPlainSocketImpl. The reason there are two implementations is that one works with Windows Vista below and the other works with Windows Vista and above.

The UniX-like implementation is less cumbersome than the Windows implementation. There is no versioning problem, so it is implemented directly by the PlainSocketImpl class. In addition, you can see that both operating systems also have a SocksSocketImpl class. In fact, it mainly implements firewall secure session conversion protocols, including SOCKS V4 and V5.

According to the above, it can be seen that in fact, different systems need to be treated differently. Basically, there are similar minor differences. The following socket implementations are analyzed with Windows Vista and above versions as examples.

The class definition

public class ServerSocket implements java.io.Closeable
Copy the code

The ServerSocket class is simply declared and implements the Closeable interface, which has only a close method.

The main properties

private boolean created = false;
private boolean bound = false;
private boolean closed = false;
private Object closeLock = new Object();
private SocketImpl impl;
private boolean oldImpl = false;
Copy the code
  • Created indicates whether a SocketImpl object has been created that the ServerSocket needs to rely on for socket operations.
  • Bound Specifies whether the address and port are bound.
  • Closed Indicates whether the socket has been closed.
  • CloseLock The lock used to close the socket.
  • Impl The real socket implementation object.
  • OldImpl is not using the old implementation.

The main method

The constructor

There are five classes of constructors that can be passed with no arguments or SocketImpl, port, backlog, address, etc. Focusing on the last constructor, the setImpl method is used to set the implementation object, then check that the port size is correct, check that the backlog is less than 0 to make it equal to 50, and finally do the port and address binding.

ServerSocket(SocketImpl impl) {
        this.impl = impl;
        impl.setServerSocket(this);
    }
    
public ServerSocket() throws IOException {
        setImpl();
    }
    
public ServerSocket(int port) throws IOException {
        this(port, 50, null);
    }
    
public ServerSocket(int port, int backlog) throws IOException {
        this(port, backlog, null);
    }
    
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; }}Copy the code

SetImpl method

Set the socket implementation object (SocksSocketImpl), which provides a factory mode to easily interconnect with other implementations. By default, there is no factory object, so the implementation of the mode is SocksSocketImpl.

private void setImpl() {
        if(factory ! = null) { impl = factory.createSocketImpl(); checkOldImpl(); }else {
            impl = new SocksSocketImpl();
        }
        if(impl ! = null) impl.setServerSocket(this); }Copy the code

CreateImpl method

This method is used to create the socket implementation object. If the implementation object is empty, the setImpl method is called to set it up. The socket is then created by calling the socket implementation object’s Create method.

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

What does the create method do? AbstractPlainSocketImpl class AbstractPlainSocketImpl class AbstractPlainSocketImpl class AbstractPlainSocketImpl class AbstractPlainSocketImpl class AbstractPlainSocketImpl class AbstractPlainSocketImpl class AbstractPlainSocketImpl class AbstractPlainSocketImpl class AbstractPlainSocketImpl class AbstractPlainSocketImpl Udp is non – connected and non – streaming.

The two types of connections are identified by Boolean types, true for TCP, false for UDP, and passed into the local implementation via socketCreate. Before that, both create FileDescriptor objects as socket references. FileDescriptor is a FileDescriptor that can be used to describe files, sockets, and resources. In addition, the udp protocol will also through the ResourceManager. BeforeUdpCreate udp socket () to statistics virtual machine number, more than a specified maximum will throw an exception, the default value is 25. Finally, set the created identifier of sockets to true, corresponding to the abstract client Socket object and ServerSocket object in Java.

protected synchronized void create(boolean stream) throws IOException {
        this.stream = stream;
        if(! stream) { ResourceManager.beforeUdpCreate(); fd = new FileDescriptor(); try { socketCreate(false); } catch (IOException ioe) { ResourceManager.afterUdpClose(); fd = null; throw ioe; }}else {
            fd = new FileDescriptor();
            socketCreate(true);
        }
        if(socket ! = null) socket.setCreated();if(serverSocket ! = null) serverSocket.setCreated(); }Copy the code

Look down at the logic of the socketCreate method called above, determine that the file descriptor cannot be empty, call the local Socket0 method, and finally associate the resulting handle to the file descriptor object.

void socketCreate(boolean stream) throws IOException {
        if (fd == null)
            throw new SocketException("Socket closed");

        int newfd = socket0(stream, false /*v6 Only*/);

        fdAccess.set(fd, newfd);
    }
    
static native int socket0(boolean stream, boolean v6Only) throws IOException;
Copy the code

Socket0: socket0: socket0: socket0

  1. By calling theNET_SocketThe Winsock function creates a socket handle, which passes through the Winsock librarysocketThe function creates a handle and passesSetHandleInformationThe function sets the inheritance flag of the handle. Here you can see that the category corresponding to the stream identifier isSOCK_STREAMandSOCK_DGRAM. Throw a CREATE exception if the handle is invalid.
  2. Then throughsetsockoptThe function sets the value of the socket’s options and throws a CREATE exception if an error occurs.
  3. Finally passed againSetHandleInformationReturns the handle by setting the inheritance flag for the handle.
JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_socket0
  (JNIEnv *env, jclass clazz, jboolean stream, jboolean v6Only /*unused*/) {
    int fd, rv, opt=0;

    fd = NET_Socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0);
    if (fd == INVALID_SOCKET) {
        NET_ThrowNew(env, WSAGetLastError(), "create");
        return- 1; } rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt));if (rv == SOCKET_ERROR) {
        NET_ThrowNew(env, WSAGetLastError(), "create");
    }

    SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);

    return fd;
}

int NET_Socket (int domain, int type, int protocol) {
    SOCKET sock;
    sock = socket (domain, type, protocol);
    if(sock ! = INVALID_SOCKET) { SetHandleInformation((HANDLE)(uintptr_t)sock, HANDLE_FLAG_INHERIT, FALSE); }return (int)sock;
}
Copy the code

The bind method

This method is used to bind a socket to a specified address and port. If the SocketAddress is empty, neither the address nor the port is specified. In this case, the system binds the socket to all valid local addresses and dynamically generates a port. The logic is as follows:

  1. Judge whether it is closed, close, throwSocketException("Socket is closed").
  2. Check whether the bond is bound. If the bond is bound, the bond is discardedSocketException("Already bound").
  3. Check whether the address is empty. If it is empty, create an InetSocketAddress. Default is all valid local addresses0.0.0.0, the default port is 0, which is dynamically generated by the operating system.
  4. Checks whether the object is of type InetSocketAddressIllegalArgumentException("Unsupported address type").
  5. Check whether the address has a value, no tossSocketException("Unresolved address").
  6. Backlog is set to 50 if less than 1.
  7. Check ports through security manager.
  8. Implement object calls through socketsbindandlistenMethods.
  9. The bound identifier is set to true.
public void bind(SocketAddress endpoint) throws IOException {
        bind(endpoint, 50);
    }
    
public void bind(SocketAddress endpoint, int backlog) throws IOException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        if(! oldImpl && isBound()) throw new SocketException("Already bound");
        if (endpoint == null)
            endpoint = new InetSocketAddress(0);
        if(! (endpoint instanceof InetSocketAddress)) throw new IllegalArgumentException("Unsupported address type");
        InetSocketAddress epoint = (InetSocketAddress) endpoint;
        if (epoint.isUnresolved())
            throw new SocketException("Unresolved address");
        if (backlog < 1)
          backlog = 50;
        try {
            SecurityManager security = System.getSecurityManager();
            if(security ! = null) security.checkListen(epoint.getPort()); getImpl().bind(epoint.getAddress(), epoint.getPort()); getImpl().listen(backlog); bound =true;
        } catch(SecurityException e) {
            bound = false;
            throw e;
        } catch(IOException e) {
            bound = false; throw e; }}Copy the code

The bind method of the socket implementation object indirectly calls the socketBind method with the following logic:

  1. Get the local file descriptor nativefd.
  2. Check whether the address is empty.
  3. callbind0Local method.
  4. It will also be called if the port is 0localPort0The local method gets a localport assigned to the localport property of the socket implementation object in order to get the port dynamically generated by the operating system.
void socketBind(InetAddress address, int port) throws IOException {
        int nativefd = checkAndReturnNativeFD();

        if (address == null)
            throw new NullPointerException("inet address argument is null.");

        bind0(nativefd, address, port, exclusiveBind);
        if (port == 0) {
            localport = localPort0(nativefd);
        } else {
            localport = port;
        }

        this.address = address;
    }
static native void bind0(int fd, InetAddress localAddress, int localport, boolean exclBind)
                             
static native int localPort0(int fd) throws IOException;
Copy the code

The bind0 local method logic is as follows,

  1. throughNET_InetAddressToSockaddrThe SOCKETADDRESS () function populates the SOCKETADDRESS union with the attributes of the Java layer InetAddress object, which correspond to the Winsock library structure, in order to fill them.
typedef union {
    struct sockaddr     sa;
    struct sockaddr_in  sa4;
    struct sockaddr_in6 sa6;
} SOCKETADDRESS;
Copy the code
  1. NET_WinBindThe function’s logic is to see if an exclusive port is required according to the exclBind flag, and if so through the Winsock librarysetsockoptThe function settingSO_EXCLUSIVEADDRUSEType selection, in the Java layer to determine whether exclusive ports can passsun.net.useExclusiveBindParameter, which is exclusive by default. Then, through the operating systembindThe function completes the binding operation.
  2. If the binding fails, an exception is thrown.
JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_bind0
  (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port,
   jboolean exclBind)
{
    SOCKETADDRESS sa;
    int rv, sa_len = 0;

    if(NET_InetAddressToSockaddr(env, iaObj, port, &sa, &sa_len, JNI_TRUE) ! = 0) {return;
    }

    rv = NET_WinBind(fd, &sa, sa_len, exclBind);

    if (rv == SOCKET_ERROR)
        NET_ThrowNew(env, WSAGetLastError(), "NET_Bind");
}
Copy the code

The localPort0 local method is implemented mainly by obtaining the socket address through getsockName function of Winsock library, and then converting the network byte into host byte and int through NTOHS function.

JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_localPort0
  (JNIEnv *env, jclass clazz, jint fd) {
    SOCKETADDRESS sa;
    int len = sizeof(sa);

    if (getsockname(fd, &sa.sa, &len) == SOCKET_ERROR) {
        if (WSAGetLastError() == WSAENOTSOCK) {
            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException"."Socket closed");
        } else {
            NET_ThrowNew(env, WSAGetLastError(), "getsockname failed");
        }
        return- 1; }return (int) ntohs((u_short)GET_PORT(&sa));
}
Copy the code

The LISTEN method of the socket implementation object indirectly calls the socketListen method. The logic is simple, obtaining the local file descriptor and then calling the listen0 local method. You can see that the local method is very simple, just call the Winsock library’s Listen function to complete the listening operation.

void socketListen(int backlog) throws IOException {
        int nativefd = checkAndReturnNativeFD();

        listen0(nativefd, backlog);
    }
    
static native void listen0(int fd, int backlog) throws IOException;

JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_listen0
  (JNIEnv *env, jclass clazz, jint fd, jint backlog) {
    if (listen(fd, backlog) == SOCKET_ERROR) {
        NET_ThrowNew(env, WSAGetLastError(), "listen failed"); }}Copy the code

Accept method

This method is used to receive socket connections. After listening is enabled on the socket, the socket will block and wait for the socket connection. Once there is a connection that can be received, the socket receives the connection through this method. Logic is,

  1. Determines whether the socket is closed.
  2. Determines whether the socket is bound.
  3. Create the Socket object and callimplAcceptMethod,
  4. Returns the Socket object.
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);
        return s;
    }

Copy the code

The implAccept method logic is,

  1. If the Socket implementation in the incoming Socket object is empty, it passessetImplMethod sets the socket implementation and executes if it is not nullresetOperation.
  2. Of the socket implementation objectacceptMethod to perform the receive operation. We do this because the SocketImpl object in our Socket object lacks the file descriptor for the underlying Socket.
  3. Call security manager to check permissions.
  4. Get the complete SocketImpl object, assign it to the Socket object, and callpostAcceptMethod sets the Socket object to created, connected, or bound.
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(); }Copy the code

The accept method of the socket implementation object calls the socketAccept method as follows:

  1. Gets the file descriptor for the operating system.
  2. Thrown if the SocketImpl object is emptyNullPointerException("socket is null").
  3. If timeout is less than or equal to 0, call local directlyaccept0Method, always blocking.
  4. Otherwise, if timeout is greater than 0, that is, timeout is set, then it is called firstconfigureBlockingLocal method that sets the specified socket to non-blocking mode. Then callwaitForNewConnectionLocal method, if a new socket can be obtained within the timeout periodaccept0Method gets a handle to the new socket and is called again if it succeedsconfigureBlockingThe local method sets the new socket to blocking mode. Finally, if non-blocking mode fails, the original socket is set to purple plug mode, using finally so that it can be executed even if an exception occurs.
  5. Finally, the new file descriptor is assigned to the SocketImpl object, along with the remote port, remote address, local port, and other variables.
void socketAccept(SocketImpl s) throws IOException {
        int nativefd = checkAndReturnNativeFD();
        if (s == null)
            throw new NullPointerException("socket is null");
        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);
            }
        }
        fdAccess.set(s.fd, newfd);
        InetSocketAddress isa = isaa[0];
        s.port = isa.getPort();
        s.address = isa.getAddress();
        s.localport = localport;
    }
Copy the code

The Winsock library iocTLSocket function is used to set the socket to blocking or non-blocking, according to the blocking flag.

JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_configureBlocking
  (JNIEnv *env, jclass clazz, jint fd, jboolean blocking) {
    u_long arg;
    int result;

    if (blocking == JNI_TRUE) {
        arg = SET_BLOCKING;    // 0
    } else {
        arg = SET_NONBLOCKING;   // 1
    }

    result = ioctlsocket(fd, FIONBIO, &arg);
    if (result == SOCKET_ERROR) {
        NET_ThrowNew(env, WSAGetLastError(), "configureBlocking"); }}Copy the code

The Winsock library select function will wait for a timeout to see if the specified file descriptor is active. If it does, it will return 0. A SocketTimeoutException is thrown to the Java layer. If -1 is returned, the socket is closed and a SocketException is thrown. If -2 is returned, InterruptedIOException is thrown.

JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_waitForNewConnection
  (JNIEnv *env, jclass clazz, jint fd, jint timeout) {
    int rv;

    rv = NET_Timeout(fd, timeout);
    if (rv == 0) {
        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException"."Accept timed out");
    } else if (rv == -1) {
        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException"."socket closed");
    } else if (rv == -2) {
        JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException"."operation interrupted");
    }
}

JNIEXPORT int JNICALL
NET_Timeout(int fd, long timeout) {
    int ret;
    fd_set tbl;
    struct timeval t;
    t.tv_sec = timeout / 1000;
    t.tv_usec = (timeout % 1000) * 1000;
    FD_ZERO(&tbl);
    FD_SET(fd, &tbl);
    ret = select (fd + 1, &tbl, 0, 0, &t);
    return ret;
}
Copy the code

The accept0 local method implements the following logic:

  1. In CmemsetThe SOCKETADDRESS union function sets the value in the body of the SOCKETADDRESS union to 0.
  2. Through Winsock libraryacceptFunction to get the socket address.
  3. Determine whether the received socket descriptor is invalid and may throw InterruptedIOException or SocketException, respectively.
  4. throughSetHandleInformationThe function sets the inheritance flag of the handle.
  5. NET_SockaddrToInetAddressThe java-layer InetAddress () function converts the resulting socket into a Java layer InetAddress object.
  6. The generated InetAddress object is used to generate the Java layer InetSocketAddress object.
  7. The InetSocketAddress array object assigned to the Java layer.
  8. Returns the file descriptor for the newly received socket.
JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_accept0
  (JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) {
    int newfd, port=0;
    jobject isa;
    jobject ia;
    SOCKETADDRESS sa;
    int len = sizeof(sa);

    memset((char *)&sa, 0, len);
    newfd = accept(fd, &sa.sa, &len);

    if (newfd == INVALID_SOCKET) {
        if (WSAGetLastError() == -2) {
            JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException"."operation interrupted");
        } else {
            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException"."socket closed");
        }
        return- 1; } SetHandleInformation((HANDLE)(UINT_PTR)newfd, HANDLE_FLAG_INHERIT, 0); ia = NET_SockaddrToInetAddress(env, &sa, &port); isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port); (*env)->SetObjectArrayElement(env, isaa, 0, isa);return newfd;
}
Copy the code

————- Recommended reading ————

My 2017 article summary – Machine learning

My 2017 article summary – Java and Middleware

My 2017 article summary – Deep learning

My 2017 article summary — JDK source code article

My 2017 article summary – Natural Language Processing

My 2017 Article Round-up — Java Concurrent Article

—————— advertising time —————-

Planet of Knowledge: The Ocean Liner

The public menu has been divided into “distributed”, “machine learning”, “deep learning”, “NLP”, “Java depth”, “Java concurrent core”, “JDK source”, “Tomcat kernel” and so on, there may be a suitable for your appetite.

Why to write “Analysis of Tomcat Kernel Design”

Welcome to: