ServerSocketChannel绑定端口的两种方式

近期学习NIO的时候,发现ServerSocketChannel有两种绑定端口的方式:

1
2
3
4
5
6
7
// 方式1

InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(inetSocketAddress);
SocketChannel scoketChannel = serverSocketChannel.accept();
1
2
3
4
5
6
7
// 方式2

InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.bind(inetSocketAddress);
SocketChannel socketChannel = serverSocketChannel.accept();

两种方式唯一的区别在于ServerSocketChannel是否先获取了ServerSoket

1
2
3
// 方式1
serverSocketChannel.socket() // 先获取ServerScoket
.bind(inetSocketAddress); // 再调用ServerScoket的bind方法
1
2
3
// 方式2
// 直接调用ServerSocketChannel的实现类ServerSocketChannelImpl的bind方法
serverSocketChannel.bind(inetSocketAddress);

通过追源码的方式可以看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// ServerScoket类的bind方法实现,JDK 1.4 引入
// backlog = 50
public void bind(SocketAddress endpoint, int backlog) throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
if (!oldImpl && isBound())
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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// ServerSocketChannelImpl类的bind方法实现,JDK 1.7 引入
// var2 = 0
public ServerSocketChannel bind(SocketAddress var1, int var2) throws IOException {
Object var3 = this.lock;
synchronized(this.lock) {
if (!this.isOpen()) {
throw new ClosedChannelException();
} else if (this.isBound()) {
throw new AlreadyBoundException();
} else {
InetSocketAddress var4 = var1 == null ? new InetSocketAddress(0) : Net.checkAddress(var1);
SecurityManager var5 = System.getSecurityManager();
if (var5 != null) {
var5.checkListen(var4.getPort());
}

NetHooks.beforeTcpBind(this.fd, var4.getAddress(), var4.getPort());
Net.bind(this.fd, var4.getAddress(), var4.getPort());
Net.listen(this.fd, var2 < 1 ? 50 : var2);
Object var6 = this.stateLock;
synchronized(this.stateLock) {
this.localAddress = Net.localAddress(this.fd);
}

return this;
}
}
}

结论

最后可以发现两个实现方式大同小异,都是调用JDK原生的函数来实现绑定操作,JDK1.7以上的版本调用我们上文提到的ServerSocketChannelImplbind方法实现,JDK1.7以下的版本调用ServerSocketbind方法实现,bind操作会将channelsocket绑定到本地地址,并配置socket以侦听连接。

Comments