ServerSocket
//指定端口的构造器
public ServerSocket(int port) throws IOException {
this(port, 50, null);
}
上个构造器调用了该方法:port指定了服务器端socket监听的端口
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;
}
}
backlog指服务器端处理请求的队列最大的长度,默认为50.
InetSocketAddress是SocketAddress的实现类.封装了hostname,InetAddress,port等参数.
想了解InetAddress可以参看这篇文章:http://www.jianshu.com/p/0ba3a391de03
- 通过bind方法,对该ip+port进行监听
public void bind(SocketAddress endpoint, int backlog) throws IOException {
if (isClosed())//判断socket是否关闭
throw new SocketException("Socket is closed");
if (!oldImpl && isBound())//判断socket的监听状态
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;
}
}
这里调用SocketImpl的bind方法: getImpl().bind(epoint.getAddress(), epoint.getPort());
SocketImpl的抽象实现类:AbstractPlainSocketImpl:
protected synchronized void bind(InetAddress address, int lport)
throws IOException
{
synchronized (fdLock) {
if (!closePending && (socket == null || !socket.isBound())) {
NetHooks.beforeTcpBind(fd, address, lport);
}
}
socketBind(address, lport);
if (socket != null)
socket.setBound();
if (serverSocket != null)
serverSocket.setBound();
}
- 通过accept方法来接收socket连接:
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;
}
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();
//接收socket
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();
}
这里getImpl().accept(si)调用了默认抽象实现类AbstractPlainSocketImpl的accept:
protected void accept(SocketImpl s) throws IOException {
//先获取fd
acquireFD();
try {
//该方法还是调用了本地方法:PlainSocketImpl:native void socketAccept(SocketImpl s) throws IOException;
socketAccept(s);
} finally {
releaseFD();
}
}
//添加synchronized锁,保证多线程情况的数据一致性
FileDescriptor acquireFD() {
synchronized (fdLock) {
fdUseCount++;
return fd;
}
}
- 需要特别注意的AbstractPlainSocketImpl有两个参数:fdLock对象是为了给每个操作socket的方法,添加同步锁synchronized锁,fdUseCount用来记录当前服务器端socket保持连接的线程数.因为在关闭ServerSocket的时候,需要将socket中的数据清空之后,才能关闭.
/* number of threads using the FileDescriptor */
protected int fdUseCount = 0;
/* lock when increment/decrementing fdUseCount */
protected final Object fdLock = new Object();
protected void close() throws IOException {
synchronized(fdLock) {
if (fd != null) {
if (!stream) {
ResourceManager.afterUdpClose();
}
if (fdUseCount == 0) {
if (closePending) {
return;
}
closePending = true;
/*
* We close the FileDescriptor in two-steps - first the
* "pre-close" which closes the socket but doesn't
* release the underlying file descriptor. This operation
* may be lengthy due to untransmitted data and a long
* linger interval. Once the pre-close is done we do the
* actual socket to release the fd.
*/
try {
socketPreClose();
} finally {
socketClose();
}
fd = null;
return;
} else {
/*
* If a thread has acquired the fd and a close
* isn't pending then use a deferred close.
* Also decrement fdUseCount to signal the last
* thread that releases the fd to close it.
*/
if (!closePending) {
closePending = true;
fdUseCount--;
socketPreClose();
}
}
}
}
}