ServerSocket构造函数源码分析
总结:
(1) 创建SocketImpl并设置为ServerSocket属性
/**
* Create a server with the specified port, listen backlog, and
* local IP address to bind to. The <i>bindAddr</i> argument
* can be used on a multi-homed host for a ServerSocket that
* will only accept connect requests to one of its addresses.
* If <i>bindAddr</i> is null, it will default accepting
* connections on any/all local addresses.
* The port must be between 0 and 65535, inclusive.
* A port number of {@code 0} means that the port number is
* automatically allocated, typically from an ephemeral port range.
* This port number can then be retrieved by calling
* {@link #getLocalPort getLocalPort}.
*
* <P>If there is a security manager, this method
* calls its {@code checkListen} method
* with the {@code port} argument
* as its argument to ensure the operation is allowed.
* This could result in a SecurityException.
*
* The {@code backlog} argument is the requested maximum number of
* pending connections on the socket. Its exact semantics are implementation
* specific. In particular, an implementation may impose a maximum length
* or may choose to ignore the parameter altogther. The value provided
* should be greater than {@code 0}. If it is less than or equal to
* {@code 0}, then an implementation specific default will be used.
* <P>
* @param port the port number, or {@code 0} to use a port
* number that is automatically allocated.
* @param backlog requested maximum length of the queue of incoming
* connections.
* @param bindAddr the local InetAddress the server will bind to
*
* @throws SecurityException if a security manager exists and
* its {@code checkListen} method doesn't allow the operation.
*
* @throws IOException if an I/O error occurs when opening the socket.
* @exception IllegalArgumentException if the port parameter is outside
* the specified range of valid port values, which is between
* 0 and 65535, inclusive.
*
* @see SocketOptions
* @see SocketImpl
* @see SecurityManager#checkListen
* @since JDK1.1
*/
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
setImpl(); //1
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;
}
}
1. setImpl()
创建SocksSocketImpl对象
SocksSocketImpl构造函数
与ServerSocket绑定
其实是将SocksSocketImpl的Impl也就是DualStackPlainSocketImpl的serverSocke属性设置为该ServerSocket(看了SocksSocketImpl构造函数就明白了这句话)
这里千万不要搞混了:
SocksSocketImpl 作为 ServerSocket的Impl,
DualStackPlainSocketImpl作为SocksSocketImpl对象的Impl属性。
因为DualStackPlainSocketImpl是接近底层的实现,可以说SocksSocketImpl 封装功能,通过底下的DualStackPlainSocketImpl实现,有点上级交给下级,下级交给下下级的感觉,老打工人了。(调侃一下,这样的代码结构还是有实际意义的)
2. 验证port
3. 绑定端口,关键一步
将我们的套接字信息(IP + port) 封装为要给InetSocketAddress对象,然后作为bind()的入参
还是通用的经过一系列判断之后使用一个try catch封装工作核心
就两步需要看
getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);
这里得到的是ServerSocket的Impl也就是我们说的SocksSocketImpl
SocksSocketImpl#bind
其实SocksSocketImpl是没有实现这个方法的,只需要继承父类的方法就行了
自然是交给我们的下下级 DualStackPlainSocketImpl实现的
其实这里DualStackPlainSocketImpl也没有实现这个方法,直接继承的它的父类的bind方法,更确切的说,bind方法还不够底层,通过AbstractPlainSocketImpl处理一下再交给DualStackPlainSocketImpl#bind0来最终实现底层操作
最后交给Jdk底层去处理,处理结束后,最后执行
底层代码在附录:
表示建立了连接:
SocksSocketImpl#listen
一样的道理
附录
bind0源码
/*
* Class: java_net_PlainDatagramSocketImpl
* Method: bind
* Signature: (ILjava/net/InetAddress;)V
*/
JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
jint localport, jobject iaObj) {
/* fdObj is the FileDescriptor field on this */
jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID);
/* fd is an int field on fdObj */
int fd;
int len = 0;
SOCKADDR him;
if (IS_NULL(fdObj)) {
JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
"Socket closed");
return;
} else {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
}
if (IS_NULL(iaObj)) {
JNU_ThrowNullPointerException(env, "iaObj is null.");
return;
}
/* bind */
if (NET_InetAddressToSockaddr(env, iaObj, localport, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) {
return;
}
setDefaultScopeID(env, (struct sockaddr *)&him);
if (NET_Bind(fd, (struct sockaddr *)&him, len) < 0) {
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
errno == EPERM || errno == EACCES) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException",
"Bind failed");
} else {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Bind failed");
}
return;
}
/* initialize the local port */
if (localport == 0) {
/* Now that we're a connected socket, let's extract the port number
* that the system chose for us and store it in the Socket object.
*/
if (JVM_GetSockName(fd, (struct sockaddr *)&him, &len) == -1) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
"Error getting socket name");
return;
}
localport = NET_GetPortFromSockaddr((struct sockaddr *)&him);
(*env)->SetIntField(env, this, pdsi_localPortID, localport);
} else {
(*env)->SetIntField(env, this, pdsi_localPortID, localport);
}
}
listen0源码
/*
* Class: java_net_DualStackPlainSocketImpl
* Method: listen0
* Signature: (II)V
*/
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");
}
}