ServerSocket
//指定端口的構(gòu)造器
public ServerSocket(int port) throws IOException {
this(port, 50, null);
}
上個構(gòu)造器調(diào)用了該方法:port指定了服務(wù)器端socket監(jiān)聽的端口
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指服務(wù)器端處理請求的隊列最大的長度,默認(rèn)為50.
InetSocketAddress是SocketAddress的實現(xiàn)類.封裝了hostname,InetAddress,port等參數(shù).
想了解InetAddress可以參看這篇文章:http://www.reibang.com/p/0ba3a391de03
- 通過bind方法,對該ip+port進(jìn)行監(jiān)聽
public void bind(SocketAddress endpoint, int backlog) throws IOException {
if (isClosed())//判斷socket是否關(guān)閉
throw new SocketException("Socket is closed");
if (!oldImpl && isBound())//判斷socket的監(jiān)聽狀態(tài)
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;
}
}
這里調(diào)用SocketImpl的bind方法: getImpl().bind(epoint.getAddress(), epoint.getPort());
SocketImpl的抽象實現(xiàn)類: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)調(diào)用了默認(rèn)抽象實現(xiàn)類AbstractPlainSocketImpl的accept:
protected void accept(SocketImpl s) throws IOException {
//先獲取fd
acquireFD();
try {
//該方法還是調(diào)用了本地方法:PlainSocketImpl:native void socketAccept(SocketImpl s) throws IOException;
socketAccept(s);
} finally {
releaseFD();
}
}
//添加synchronized鎖,保證多線程情況的數(shù)據(jù)一致性
FileDescriptor acquireFD() {
synchronized (fdLock) {
fdUseCount++;
return fd;
}
}
- 需要特別注意的AbstractPlainSocketImpl有兩個參數(shù):fdLock對象是為了給每個操作socket的方法,添加同步鎖synchronized鎖,fdUseCount用來記錄當(dāng)前服務(wù)器端socket保持連接的線程數(shù).因為在關(guān)閉ServerSocket的時候,需要將socket中的數(shù)據(jù)清空之后,才能關(guān)閉.
/* 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();
}
}
}
}
}