接上文:
源码阅读:理解反应器模式在javaNIO中的实现-Channel
上文的最后是这么说的:
接下来要先看看socketChannel具体connect的实现方式,以及两者对应的open方法,是如何通过SelectorProvider开启channel的(这就要先看看selector了)。
本文先看前半部分:connect的实现方式
那么就开始吧:
public boolean connect(SocketAddress var1) throws IOException {
boolean var2 = false;
//首先是加上读写锁
synchronized(this.readLock) {
synchronized(this.writeLock) {
//确保开启并未连接(这边通过本地变量state来判断
//如果state = 2,则已连接;如果state=1,则正在连接中
//以及如果isOpen为false,均抛出异常
this.ensureOpenAndUnconnected();
//判断连接地址是否符合ipv4或者ipv6格式
InetSocketAddress var5 = Net.checkAddress(var1);
//获得安全管理器
//这里简单地介绍一下SecurityManager
//当运行未知的Java程序的时候,该程序可能有恶意代码(删除系
//统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安
//全管理器。这里用于判断是否允许能够连接主机地址。
SecurityManager var6 = System.getSecurityManager();
if (var6 != null) {
var6.checkConnect(var5.getAddress().getHostAddress(), var5.getPort());
}
boolean var10000;
//用于在注册时阻塞进程
synchronized(this.blockingLock()) {
int var8 = 0;
try {
try {
this.begin();
synchronized(this.stateLock) {
if (!this.isOpen()) {
boolean var10 = false;
return var10;
}
if (this.localAddress == null) {
NetHooks.beforeTcpConnect(this.fd, var5.getAddress(), var5.getPort());
}
this.readerThread = NativeThread.current();
}
do {
InetAddress var9 = var5.getAddress();
if (var9.isAnyLocalAddress()) {
var9 = InetAddress.getLocalHost();
}
var8 = Net.connect(this.fd, var9, var5.getPort());
} while(var8 == -3 && this.isOpen());
} finally {
this.readerCleanup();
this.end(var8 > 0 || var8 == -2);
assert IOStatus.check(var8);
}
} catch (IOException var27) {
this.close();
throw var27;
}
synchronized(this.stateLock) {
this.remoteAddress = var5;
if (var8 <= 0) {
if (!this.isBlocking()) {
this.state = 1;
} else {
assert false;
}
} else {
this.state = 2;
if (this.isOpen()) {
this.localAddress = Net.localAddress(this.fd);
}
var10000 = true;
return var10000;
}
}
}
var10000 = false;
return var10000;
}
}
}
从begin()方法开始分析
private Interruptible interruptor;
private volatile Thread interrupted;
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (!open)
return;
open = false;
interrupted = target;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
Thread me = Thread.currentThread();
if (me.isInterrupted())
interruptor.interrupt(me);
}
首先,interruptor是啥东西?
引用https://www.cnblogs.com/jenkov/p/juc_interrupt.html
interrupt(),在一个线程中调用另一个线程的interrupt()方法,即会向那个线程发出信号——线程中断状态已被设置。至于那个线程何去何从,由具体的代码实现决定。
isInterrupted(),用来判断当前线程的中断状态(true or false)。
interrupted()是个Thread的static方法,用来恢复中断状态,名字起得额🙄。
可以看到,在Interruptible的初始化时去获取关闭锁,如果关闭锁是开启的,则设置interrupted为目标线程。通过AbstractInterruptibleChannel.this.implCloseChannel();方法去关闭channel。
在Thread类中找到blockedOn的实现:
是设置interruptible为Thread的blocker。
/* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code
*/
void blockedOn(Interruptible b) {
synchronized (blockerLock) {
blocker = b;
}
}
接着是获得当前线程,并interrput中断该线程。
if (me.isInterrupted())
interruptor.interrupt(me);
注意这里:当线程已中断,再中断该线程(这是啥意思,理解不了,哪位大神解释一下)
目前理解:begin()方法的作用就是中断线程。
先看下去:
synchronized(this.stateLock) {
if (!this.isOpen()) {
boolean var10 = false;
return var10;
}
if (this.localAddress == null) {
NetHooks.beforeTcpConnect(this.fd, var5.getAddress(), var5.getPort());
}
this.readerThread = NativeThread.current();
}
这里去同步获得stateLock这个常量,实现锁(stateLock就是锁,只有获得这个资源,才可以执行方法体里面的方法)
接下来,如果当前channel没有开启,直接返回false。
NetHooks.beforeTcpConnect(this.fd, var5.getAddress(), var5.getPort());这个方法点进去很深,大致就是获得tcp连接。
然后就是获得本地的读线程。
do {
InetAddress var9 = var5.getAddress();
if (var9.isAnyLocalAddress()) {
var9 = InetAddress.getLocalHost();
}
var8 = Net.connect(this.fd, var9, var5.getPort());
} while(var8 == -3 && this.isOpen());
} finally {
this.readerCleanup();
this.end(var8 > 0 || var8 == -2);
assert IOStatus.check(var8);
}
最后是一个do-while-finally方法
大致就是获得与本地地址进行连接,最终关闭读线程,并通过end()方法结束channel的io操作
(在var8>0 || var8 ==-2为true的条件下,这个参数值的含义貌似没地方能看到。)
总结:
connect方法不断地将本地地址与远程ip端口进行连接,直到连接成功,并中断当前线程。
接下来看看finishConnect方法:
public boolean finishConnect() throws IOException {
//同样地先给读写加上锁
synchronized(this.readLock) {
synchronized(this.writeLock) {
boolean var10000;
synchronized(this.stateLock) {
if (!this.isOpen()) {
throw new ClosedChannelException();
}
//如果state==2,即代表已经连接成功,return true
if (this.state == 2) {
var10000 = true;
return var10000;
}
//如果!=1,直接抛出异常,因为该方法是在连接pendi//ng(即正在连接中)的时候被调用
if (this.state != 1) {
throw new NoConnectionPendingException();
}
}
int var3 = 0;
try {
label525: {
boolean var29 = false;
boolean var6;
label506: {
try {
var29 = true;
this.begin();
synchronized(this.blockingLock()) {
label480: {
label494: {
synchronized(this.stateLock) {
if (!this.isOpen()) {
var6 = false;
break label494;
}
this.readerThread = NativeThread.current();
}
if (!this.isBlocking()) {
do {
var3 = checkConnect(this.fd, false, this.readyToConnect);
} while(var3 == -3 && this.isOpen());
} else {
do {
while(true) {
var3 = checkConnect(this.fd, true, this.readyToConnect);
if (var3 == 0) {
continue;
}
break;
}
} while(var3 == -3 && this.isOpen());
}
var29 = false;
break label480;
}
var29 = false;
break label506;
}
}
} finally {
if (var29) {
synchronized(this.stateLock) {
this.readerThread = 0L;
if (this.state == 3) {
this.kill();
var3 = 0;
}
}
this.end(var3 > 0 || var3 == -2);
assert IOStatus.check(var3);
}
}
synchronized(this.stateLock) {
this.readerThread = 0L;
if (this.state == 3) {
this.kill();
var3 = 0;
}
}
this.end(var3 > 0 || var3 == -2);
assert IOStatus.check(var3);
break label525;
}
synchronized(this.stateLock) {
this.readerThread = 0L;
if (this.state == 3) {
this.kill();
var3 = 0;
}
}
this.end(var3 > 0 || var3 == -2);
assert IOStatus.check(var3);
return var6;
}
} catch (IOException var38) {
this.close();
throw var38;
}
if (var3 > 0) {
synchronized(this.stateLock) {
this.state = 2;
if (this.isOpen()) {
this.localAddress = Net.localAddress(this.fd);
}
}
var10000 = true;
return var10000;
} else {
var10000 = false;
return var10000;
}
}
}
}
开始的方法类似,这边有这么一个用法
label494: {
synchronized(this.stateLock) {
if (!this.isOpen()) {
var6 = false;
break label494;
}
这个label494一个是定义了一个方法,如果被break了,就不在执行下去了。
public class test {
public static void main(String[] args) {
int i = 0;
fuck454:
{
i = i + 1;
if (i == 1) {
break fuck454;
}
System.out.println(i);
}
}
}
结果不会输出,因为已经break了。
继续看:
if (!this.isBlocking()) {
do {
var3 = checkConnect(this.fd, false, this.readyToConnect);
} while(var3 == -3 && this.isOpen());
} else {
do {
while(true) {
var3 = checkConnect(this.fd, true, this.readyToConnect);
if (var3 == 0) {
continue;
}
break;
}
} while(var3 == -3 && this.isOpen());
}
直到var3!=3或者该channel被关闭了,否则一直检查是否连接上。
最终无论是否成功都关闭线程并关闭channel。
总结:
finishChannel在connect方法被调用之后就可能会被调用,其会一直轮询判断是否已经连接成功,若成功,关闭开启的线程,结束channel的IO操作。
那么为什么要用finishChannel呢?
如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()的方法(如果成功连接会返回true)。