1. SocketImpl的类树如下:
java.net.SocketImpl(抽象类)
--> java.net.AbstractPlainSocketImpl(抽象实现类)
--> java.net.TwoStacksPlainSocketImpl(真正实现类,Vista以下版本)
--> java.net.DualStackPlainSocketImpl(真正实现类,Vista及以上版本)
--> java.net.PlainSocketImpl(上述实现类的代理类)
--> java.net.SocksSocketImpl(SOCKS TCP套接字实现类)
2. 类字段
- SocketImpl
// 指向服务器或客户端实例,只能有一个非空
Socket socket = null;
ServerSocket serverSocket = null;
// 套接字文件描述符
FileDescriptor fd;
// 远端IP地址
InetAddress address;
// 远端端口
int port;
// 本地端口
int localport;
- PlainSocketImpl
// 代理真正的套接字实例
// TwoStacksPlainSocketImpl或DualStackPlainSocketImpl
AbstractPlainSocketImpl impl;
- ServerSocket
// 指向套接字代理类实例(SocksSocketImpl)
SocketImpl impl;
// 状态
// 是否创建套接字
boolean created = false;
// 是否绑定本地地址
boolean bound = false;
// 是否关闭套接字
boolean closed = false;
- Socket
// 指向套接字代理类实例(SocksSocketImpl)
SocketImpl impl;
// 状态
// 是否创建套接字
boolean created = false;
// 是否绑定本地地址
boolean bound = false;
// 是否建立连接
boolean connected = false;
// 是否关闭读
boolean shutIn = false;
// 是否关闭写
boolean shutOut = false;
// 是否关闭套接字
boolean closed = false;
3. 创建套接字
- ServerSocket
创建真正套接字实例:
public ServerSocket() throws IOException {
setImpl();
}
// ServerSocket
private void setImpl() {
// SocketImpl#impl
impl = new SocksSocketImpl();
impl.setServerSocket(this);
}
SocksSocketImpl() {
// Nothing needed
}
// SocksSocketImpl的父类
PlainSocketImpl() {
if (useDualStackImpl) {
// AbstractPlainSocketImpl#impl
impl = new DualStackPlainSocketImpl(exclusiveBind);
} else {
impl = new TwoStacksPlainSocketImpl(exclusiveBind);
}
}
public DualStackPlainSocketImpl(boolean exclBind) {
// 套接字是否互斥绑定,Windows为true
exclusiveBind = exclBind;
}
初始化套接字:
// ServerSocket
SocketImpl getImpl() throws SocketException {
if (!created)
createImpl();
return impl;
}
// ServerSocket
void createImpl() throws SocketException {
if (impl == null)
setImpl();
try {
impl.create(true);
created = true;
} catch (IOException e) {
throw new SocketException(e.getMessage());
}
}
// AbstractPlainSocketImpl
void create(boolean stream) throws IOException {
this.stream = stream;
if (!stream) {
// SocketImpl#fd
fd = new FileDescriptor();
try {
// 创建数据报套接字UDP
socketCreate(false);
} catch (IOException ioe) {
fd = null;
throw ioe;
}
} else {
fd = new FileDescriptor();
// 创建流式套接字TCP
socketCreate(true);
}
// Socket或ServerSocket置为已创建状态
if (socket != null)
socket.setCreated();
if (serverSocket != null)
serverSocket.setCreated();
}
// DualStackPlainSocketImpl
void socketCreate(boolean stream) throws IOException {
// 调用native方法创建套接字
int newfd = socket0(stream, false /*v6 Only*/);
// SocketImpl#fd
fdAccess.set(fd, newfd);
}
// ServerSocket:已创建
void setCreated() {
created = true;
}
- Socket
public Socket() {
setImpl();
}
void setImpl() {
impl = new SocksSocketImpl();
impl.setSocket(this);
}
SocketImpl getImpl() {
if (!created)
createImpl(true);
return impl;
}
// 后续同ServerSocket
4. 服务器绑定本地地址
// ServerSocket
void bind(SocketAddress endpoint, int backlog) {
// getImpl()触发套接字初始化
// 绑定本地地址
getImpl().bind(epoint.getAddress(), epoint.getPort());
getImpl().listen(backlog);
}
//AbstractPlainSocketImpl
void bind(InetAddress address, int lport) {
socketBind(address, lport);
// Socket或ServerSocket置为已绑定状态
if (socket != null)
socket.setBound();
if (serverSocket != null)
serverSocket.setBound();
}
// DualStackPlainSocketImpl
void socketBind(InetAddress address, int port) {
// 套接字文件描述符
int nativefd = checkAndReturnNativeFD();
// 调用native方法绑定本地地址
bind0(nativefd, address, port, exclusiveBind);
// 本地端口
if (port == 0) {
localport = localPort0(nativefd);
} else {
localport = port;
}
}
5. 服务器监听连接请求
// ServerSocket
void bind(SocketAddress endpoint, int backlog) {
// getImpl()触发套接字初始化
getImpl().bind(epoint.getAddress(), epoint.getPort());
// 监听连接请求
getImpl().listen(backlog);
}
// AbstractPlainSocketImpl
void listen(int count) {
socketListen(count);
}
// DualStackPlainSocketImpl
void socketListen(int backlog) {
// 套接字文件描述符
int nativefd = checkAndReturnNativeFD();
// 调用native方法监听请求
listen0(nativefd, backlog);
}
6. 客户端请求连接
// Socket
void connect(SocketAddress endpoint, int timeout) {
// 远程地址和端口
InetSocketAddress epoint = (InetSocketAddress) endpoint;
InetAddress addr = epoint.getAddress ();
int port = epoint.getPort();
// 连接请求
impl.connect(addr.getHostName(), port);
// 已连接
connected = true;
// 如果套接字在连接之前没有绑定,内核将选择临时端口和本地地址
bound = true;
}
// AbstractPlainSocketImpl
void connect(String host, int port){
InetAddress address = InetAddress.getByName(host);
if (address.isAnyLocalAddress()) {
doConnect(InetAddress.getLocalHost(), port, timeout);
} else {
doConnect(address, port, timeout);
}
}
// AbstractPlainSocketImpl
void doConnect(InetAddress address, int port, int timeout) {
socketConnect(address, port, timeout);
if (socket != null) {
// Socket设置为已绑定、已连接
socket.setBound();
socket.setConnected();
}
}
// DualStackPlainSocketImpl
void socketConnect(InetAddress address, int port, int timeout) {
// 套接字文件描述符
int nativefd = checkAndReturnNativeFD();
int connectResult;
if (timeout <= 0) {
// 阻塞直到连接成功
connectResult = connect0(nativefd, address, port);
} else {
// 阻塞,直到连接成功或超时
configureBlocking(nativefd, false);
try {
connectResult = connect0(nativefd, address, port);
if (connectResult == WOULDBLOCK) {
// 调用native方法超时等待连接
waitForConnect(nativefd, timeout);
}
} finally {
configureBlocking(nativefd, true);
}
}
}
7. 服务器接受连接
// ServerSocket
public Socket accept() {
Socket s = new Socket((SocketImpl) null);
implAccept(s);
return s;
}
// ServerSocket
void implAccept(Socket s) {
SocketImpl si = null;
// 创建SocketImpl实例
s.setImpl();
si = s.impl;
s.impl = null;
si.address = new InetAddress();
si.fd = new FileDescriptor();
getImpl().accept(si);
s.impl = si;
s.connected = true;
s.created = true;
s.bound = true;
}
// AbstractPlainSocketImpl
void accept(SocketImpl s) {
socketAccept(s);
}
// DualStackPlainSocketImpl
void socketAccept(SocketImpl s) {
// ServerSocket监听的文件描述符
int nativefd = checkAndReturnNativeFD();
// 与客户端连接的文件描述符
// 如果复用监听fd,则只能与一个客户端连接通信
// 为连接新建fd,使服务器能够连接多个客户端
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);
}
}
// 将新SocketImpl的fd设置为连接的文件描述符
// 使用该fd与客户端进行通信
fdAccess.set(s.fd, newfd);
// 远程,即客户端的地址和端口
InetSocketAddress isa = isaa[0];
s.port = isa.getPort();
s.address = isa.getAddress();
// 新SocketImpl的本地端口即ServerSocket监听的端口
s.localport = localport;
}
8. 通信的InputStream和OutputStream
// Socket
InputStream getInputStream() {
return impl.getInputStream();
}
OutputStream getOutputStream() {
return impl.getOutputStream();
}
// AbstractPlainSocketImpl
InputStream getInputStream() {
socketInputStream = new SocketInputStream(this);
return socketInputStream;
}
OutputStream getOutputStream() {
socketOutputStream = new SocketOutputStream(this);
return socketOutputStream;
}
- SocketInputStream extends FileInputStream
// 指向连接的文件描述符
FileDescriptor fd;
// 指向连接的真正套接字
AbstractPlainSocketImpl impl;
// 指向连接的Socket实例
Socket socket = null;
SocketInputStream(AbstractPlainSocketImpl impl) {
fd = impl.getFileDescriptor();
this.impl = impl;
socket = impl.getSocket();
}
int read(byte b[], int off, int length, int timeout) {
int n = socketRead(fd, b, off, length, timeout);
}
private int socketRead(FileDescriptor fd, byte b[], int off, int len, int timeout) {
// 使用套接字接受原语读取
return socketRead0(fd, b, off, len, timeout);
}
// 流的关闭会导致Socket关闭
public void close() {
if (socket != null) {
if (!socket.isClosed()) {
socket.close();
}
} else {
impl.close();
}
}
- SocketOutputStream extends FileOutputStream
// 指向连接的文件描述符
FileDescriptor fd;
// 指向连接的真正套接字
AbstractPlainSocketImpl impl;
// 指向连接的Socket实例
Socket socket = null;
SocketOutputStream(AbstractPlainSocketImpl impl) {
fd = impl.getFileDescriptor();
this.impl = impl;
socket = impl.getSocket();
}
void write(int b) {
temp[0] = (byte)b;
socketWrite(temp, 0, 1);
}
void socketWrite(byte b[], int off, int len) {
// 写到套接字
socketWrite0(fd, b, off, len);
}
// 流的关闭会导致Socket关闭
public void close() {
if (socket != null) {
if (!socket.isClosed()) {
socket.close();
}
} else {
impl.close();
}
}
- shutdownInput()和shutdownOutput()
// Socket
void shutdownInput() {
getImpl().shutdownInput();
shutIn = true;
}
void shutdownOutput() {
getImpl().shutdownOutput();
shutOut = true;
}
// AbstractPlainSocketImpl
protected void shutdownInput() throws IOException {
if (fd != null) {
// SHUT_RD = 0
socketShutdown(SHUT_RD);
if (socketInputStream != null) {
socketInputStream.setEOF(true);
}
shut_rd = true;
}
}
protected void shutdownOutput() {
if (fd != null) {
// SHUT_WR = 1
socketShutdown(SHUT_WR);
shut_wr = true;
}
}
// DualStackPlainSocketImpl
// howto:0,input;1,output;
void socketShutdown(int howto) throws IOException {
int nativefd = checkAndReturnNativeFD();
shutdown0(nativefd, howto);
}
9. 关闭
// ServerSocket/Socket
public void close() {
synchronized(closeLock) {
if (isClosed())
return;
if (created)
impl.close();
closed = true;
}
}
// AbstractPlainSocketImpl
protected void close() throws IOException {
synchronized(fdLock) {
if (fd != null) {
if (fdUseCount == 0) {
if (closePending) {
return;
}
closePending = true;
// 我们分两步关闭文件描述符——首先是“预关闭”,它关闭套接字,但不释放底层文件描述符。
// 由于未传输数据和长时间的逗留间隔,此操作可能很长。
// 一旦预关闭完成,我们做实际的套接字来释放fd。
try {
socketPreClose();
} finally {
socketClose();
}
fd = null;
return;
} else {
// 还需要递减fdUseCount来通知释放fd的最后一个线程关闭它。
if (!closePending) {
closePending = true;
fdUseCount--;
socketPreClose();
}
}
}
}
}
void socketPreClose() {
socketClose0(true);
}
void socketClose() {
socketClose0(false);
}
// DualStackPlainSocketImpl
void socketClose0(boolean useDeferredClose/*unused*/) throws IOException {
final int nativefd = fdAccess.get(fd);
fdAccess.set(fd, -1);
close0(nativefd);
}