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
- By calling the
NET_Socket
The Winsock function creates a socket handle, which passes through the Winsock librarysocket
The function creates a handle and passesSetHandleInformation
The function sets the inheritance flag of the handle. Here you can see that the category corresponding to the stream identifier isSOCK_STREAM
andSOCK_DGRAM
. Throw a CREATE exception if the handle is invalid. - Then through
setsockopt
The function sets the value of the socket’s options and throws a CREATE exception if an error occurs. - Finally passed again
SetHandleInformation
Returns 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:
- Judge whether it is closed, close, throw
SocketException("Socket is closed")
. - Check whether the bond is bound. If the bond is bound, the bond is discarded
SocketException("Already bound")
. - Check whether the address is empty. If it is empty, create an InetSocketAddress. Default is all valid local addresses
0.0.0.0
, the default port is 0, which is dynamically generated by the operating system. - Checks whether the object is of type InetSocketAddress
IllegalArgumentException("Unsupported address type")
. - Check whether the address has a value, no toss
SocketException("Unresolved address")
. - Backlog is set to 50 if less than 1.
- Check ports through security manager.
- Implement object calls through sockets
bind
andlisten
Methods. - 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:
- Get the local file descriptor nativefd.
- Check whether the address is empty.
- call
bind0
Local method. - It will also be called if the port is 0
localPort0
The 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,
- through
NET_InetAddressToSockaddr
The 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
NET_WinBind
The function’s logic is to see if an exclusive port is required according to the exclBind flag, and if so through the Winsock librarysetsockopt
The function settingSO_EXCLUSIVEADDRUSE
Type selection, in the Java layer to determine whether exclusive ports can passsun.net.useExclusiveBind
Parameter, which is exclusive by default. Then, through the operating systembind
The function completes the binding operation.- 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,
- Determines whether the socket is closed.
- Determines whether the socket is bound.
- Create the Socket object and call
implAccept
Method, - 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,
- If the Socket implementation in the incoming Socket object is empty, it passes
setImpl
Method sets the socket implementation and executes if it is not nullreset
Operation. - Of the socket implementation object
accept
Method to perform the receive operation. We do this because the SocketImpl object in our Socket object lacks the file descriptor for the underlying Socket. - Call security manager to check permissions.
- Get the complete SocketImpl object, assign it to the Socket object, and call
postAccept
Method 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:
- Gets the file descriptor for the operating system.
- Thrown if the SocketImpl object is empty
NullPointerException("socket is null")
. - If timeout is less than or equal to 0, call local directly
accept0
Method, always blocking. - Otherwise, if timeout is greater than 0, that is, timeout is set, then it is called first
configureBlocking
Local method that sets the specified socket to non-blocking mode. Then callwaitForNewConnection
Local method, if a new socket can be obtained within the timeout periodaccept0
Method gets a handle to the new socket and is called again if it succeedsconfigureBlocking
The 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. - 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:
- In C
memset
The SOCKETADDRESS union function sets the value in the body of the SOCKETADDRESS union to 0. - Through Winsock library
accept
Function to get the socket address. - Determine whether the received socket descriptor is invalid and may throw InterruptedIOException or SocketException, respectively.
- through
SetHandleInformation
The function sets the inheritance flag of the handle. NET_SockaddrToInetAddress
The java-layer InetAddress () function converts the resulting socket into a Java layer InetAddress object.- The generated InetAddress object is used to generate the Java layer InetSocketAddress object.
- The InetSocketAddress array object assigned to the Java layer.
- 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: