ServerSocketChannel类
获得ServerSocketChannel与ServerSocket对象
ServerSocketChannel类是抽象的,并不能直接new实例化,但API中提供了public static ServerSocketChannel open()方法来创建ServerSocketChannel类的实例。open() 方法是静态的,作用是打开服务器套接.字通道。新通道的套接字最初是未绑定的;可以接受连接之前,必须通过它的某个套接字的bind()方法将其绑定到具体的地址。
通过调用open()方法创建ServerSocketChannel类的实例后,可以调用它的public abstract ServerSocket socket()方法来返回ServerSocket类的对象,然后与客户端套接字进行通信。socket()方法的作用是获取与此通道关联的服务器套接字ServerSocket类的对象。
public final void close()方法的作用是关闭此通道。如果已关闭该通道,则此方法立即返回。否则,它会将该通道标记为已关闭,然后调用implCloseChannel()方法以完成关闭操作。
public static void main(String[] args) {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(“localhost”, 8888));
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
char[] charArray = new char[1024];
int readLength = inputStreamReader.read(charArray);
while (readLength != -1) {
System.out.println(new String(charArray, 0, readLength));
readLength = inputStreamReader.read(charArray);
}
inputStreamReader.close();
inputStream.close();
socket.close();
serverSocket.close();
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
执行绑定操作
serverSocket.bind()方法将ServerSocket类绑定到指定的地址,而ServerSocketChannel类也有bind() 方法,该方法public final ServerSocketChannel bind(SocketAddress local)的作用是将通道的套接字绑定到本地地址并侦听连接。
public abstract ServerSocketChannel bind(SocketAddress local; int backlog)方法的作用是将通道的套接字绑定到本地地址并侦听连接,通过使用参数backlog来限制客户端连接的数量。
阻塞与非阻塞以及accept()方法的使用效果
public abstract Socket Channel accept()方法的作用是接受此通道套接字的连接。如果此通道处于非阻塞模式,那么在不存在挂起的连接时,此方法将直接返回nul否则,在新的连接可用或者发生I/O错误之前会无限期地阻塞它。无论此通道的阻塞模式如何,此方法返回的套接字通道(如果有)将处于阻塞模式。
调用 ServerSocketChannel的 public final Selectable Channel configure Blocking( boolean block)方法即可。 public final Selectable Channel configure Blocking( boolean block)方法的作用是调整此通道的阻塞模式,传入tue是阻塞模式,传人 false是非阻塞模式。
阻塞模式:
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
System.out.println(“serverSocketChannel.isBlocking()=” + serverSocketChannel.isBlocking());
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
System.out.println("begin " + System.currentTimeMillis());
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("end " + System.currentTimeMillis());
socketChannel.close();
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
输出:
serverSocketChannel.isBlocking()=true
begin 1591676749339
1
2
非阻塞模式:
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
System.out.println(“serverSocketChannel.isBlocking()=” + serverSocketChannel.isBlocking());
serverSocketChannel.configureBlocking(false);
System.out.println(“serverSocketChannel.isBlocking()=” + serverSocketChannel.isBlocking());
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
System.out.println("begin " + System.currentTimeMillis());
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("end " + System.currentTimeMillis());
socketChannel.close();
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
注意:非阻塞模式下,accept()方法在没有客户端连接时,返回null值。
使用 Server Socketchannel类的 accepto方法的优势是返回1个 Socketchannel通道,此通道是 Selectablechannel(可选择通道)的子类,可以把这个 Socketchannel通道注册到选择器中实现I/O多路复用,另外, Socketchannel通道使用缓冲区进行数据的读取操作。
获得Selector对象
由于Selector类是抽象类,需要调用 open方
法获得 Selector对象。
Selector类的public static Selector open方法的作用是打开1个选择器,使 Selectablechannel能将自身注册到这个选择器上。
执行注册操作与获得SelectionKey对象
Selectable Channel类的public final SelectionKey register( Selector sel, Int ops)方法的作用是向给定的选择器注册此通道,返回一个选择键( SelectionKey)。
参数sel代表要向其注册此通道的选择器,参数ops代表 register方法的返回值 SelectionKey的可用操作集,操作集是在 SelectionKey类中以常量的形式进行提供的:
OP_ACCEPT:用于套接字接受操作的操作集位
OP_CONNECT:用于套接字连接操作的操作集位
OP_READ:用于读取操作的操作集位
OP_WRITE:用于写入操作的操作集位
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//必须将ServerSocketChannel设置成非阻塞模式,否则抛出IllegalBlockingModeException
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(“localhost”, 8888));
//开始
Selector selector = Selector.open();
SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//结束
System.out.println("selector=" + selector);
System.out.println("key= " + key);
serverSocket.close();
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
selector=sun.nio.ch.KQueueSelectorImpl@511d50c0
key= sun.nio.ch.SelectionKeyImpl@610455d6
1
2
判断注册的状态
Selectablechannel类的 public final boolean isRegisteredo方法的作用是判断此通道当前是否已向任何选择器进行了注册。新创建的通道总是未注册的。由于对 SelectionKey执行取消操作和通道进行注销之间有延迟,因此在已取消某个通道的所有 SelectionKey后,该通道可能在定时间内还会保持已注册状态。关闭通道后,该通道可能在一定时间内还会保持已注册状态。
将通道设置成非阻塞模式再注册到选择器
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
Selector selector = Selector.open();
System.out.println("Selector=" + selector);
System.out.println("A serverSocketChannel1.isRegistered()=" + serverSocketChannel.isRegistered());
SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("B serverSocketChannel1.isRegistered()=" + serverSocketChannel.isRegistered());
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
register()方法的第二个参数OP_ACCEPT代表检测此通道套接字的连接。
出现异常的原因是没有将通道设置成非阻塞模式。如果想把通道注册到选择器中,就必须将通道设置成非阻塞模式。
使用configureBlocking(false)方法解决异常
public final Selectable Channel configure Blocking( boolean block()方法的作用是调整此通道的阻塞模式。如果向一个或多个选择器注册了此通道,则尝试将此通道置于阻塞模式将导致抛出 IllegalBlocking Mode Exception。可在任意时间调用此方法。
新的阻塞模式仅影响在此方法返回后发起的IO操作。对于某些实现,这可能需要阻塞,直到所有挂起的IO操作已完成。如果调用此方法的同时正在进行另一个此方法或register(方法的调用,则在另个操作完成前将首先阻塞该调用。
public final boolean is Blocking方法的作用是判断此通道上的每个I/O操作在完成前是否被阻塞。新创建的通道总是处于阻塞模式。如果此通道已关闭,则此方法返回的值是未指
定的。
判断打开的状态
public final boolean isOpen方法的作用是判断此通道是否处于打开状态。
获得阻塞对象
public final Object blocking Lock()方法的作用是获取其 configure Blocking和 register方法实现同步的对象,防止重复注册。
该方法的源代码如下:
private final Object regLock = new Obect();
public final Object blockingLock() {
return regLock;
}
1
2
3
4
获得支持的SocketOption列表
Set<SocketOption<?> supportedOptionso方法的作用是返回通道支持的 Socket Option。
Thread t = new Thread() {
@Override
public void run() {
try {
Thread.sleep(2000);
Socket socket = new Socket(“localhost”, 8888);
socket.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
t.start();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
SocketChannel socketChannel = serverSocketChannel.accept();
Set<SocketOption<?>> set1 = serverSocketChannel.supportedOptions();
Set<SocketOption<?>> set2 = socketChannel.supportedOptions();
Iterator<SocketOption<?>> it1 = set1.iterator();
Iterator<SocketOption<?>> it2 = set2.iterator();
System.out.println("ServerSocketChannel supportedOptions:");
while (it1.hasNext()) {
SocketOption<?> next = it1.next();
System.out.println("name:" + next.getClass().getName());
}
System.out.println();
System.out.println();
System.out.println("SocketChannel supportedOptions:");
while (it2.hasNext()) {
SocketOption<?> next = it2.next();
System.out.println("name:" + next.getClass().getName());
}
socketChannel.close();
serverSocketChannel.close();
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
ServerSocketChannel supportedOptions:
name:java.net.StandardSocketOptions
S
t
d
S
o
c
k
e
t
O
p
t
i
o
n
n
a
m
e
:
j
a
v
a
.
n
e
t
.
S
t
a
n
d
a
r
d
S
o
c
k
e
t
O
p
t
i
o
n
s
StdSocketOption name:java.net.StandardSocketOptions
StdSocketOptionname:java.net.StandardSocketOptionsStdSocketOption
name:java.net.StandardSocketOptions$StdSocketOption
SocketChannel supportedOptions:
name:java.net.StandardSocketOptions
S
t
d
S
o
c
k
e
t
O
p
t
i
o
n
n
a
m
e
:
j
a
v
a
.
n
e
t
.
S
t
a
n
d
a
r
d
S
o
c
k
e
t
O
p
t
i
o
n
s
StdSocketOption name:java.net.StandardSocketOptions
StdSocketOptionname:java.net.StandardSocketOptionsStdSocketOption
name:java.net.StandardSocketOptions
S
t
d
S
o
c
k
e
t
O
p
t
i
o
n
n
a
m
e
:
j
a
v
a
.
n
e
t
.
S
t
a
n
d
a
r
d
S
o
c
k
e
t
O
p
t
i
o
n
s
StdSocketOption name:java.net.StandardSocketOptions
StdSocketOptionname:java.net.StandardSocketOptionsStdSocketOption
name:java.net.StandardSocketOptions$StdSocketOption
name:sun.nio.ch.ExtendedSocketOption
1
n
a
m
e
:
j
a
v
a
.
n
e
t
.
S
t
a
n
d
a
r
d
S
o
c
k
e
t
O
p
t
i
o
n
s
1 name:java.net.StandardSocketOptions
1name:java.net.StandardSocketOptionsStdSocketOption
name:java.net.StandardSocketOptions$StdSocketOption
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
获得与设置SocketOption
public abstract ServerSocket Channel setOption(SocketOption name, T value)的作用是设置 Socket Option值。
T getOption( SocketOptionname)方法的作用是获取 Socket Option值。
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//通道支持什么,Socket Option就只能设置什么,设置其他的Option就会出现异常
System.out.println(“A SO_REVBUF=” + serverSocketChannel.getOption(StandardSocketOptions.SO_RCVBUF));
serverSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 5678);
System.out.println(“B SO_REVBUF=” + serverSocketChannel.getOption(StandardSocketOptions.SO_RCVBUF));
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
A SO_REVBUF=131072
B SO_REVBUF=5678
1
2
获得SocketAddress对象
public abstract SocketAddress getLocalAddresso方法的作用是获取绑定的 Socketaddress对象。
阻塞模式的判断
public final boolean is Blocking方法的作用是判断此通道上的每个IO操作在完成前是否被阻塞。新创建的通道总是处于阻塞模式。如果此通道已关闭,则此方法返回的值是未指
定的。返回值代表当且仅当此通道处于阻塞模式时才返回true。
根据Selector找到对应的SelectionKey
public final SelectionKey keyFor( Selector sel)方法的作用是获取通道向给定选择器注册的 SelectionKeyo同一个 Selectable channel通道可以注册到不同的选择器对象,然后返回新创建的 SelectionKey对象,可以使用 public final SelectionKey keyFor( Selector sel)方法来取得当前通道注册在指定选择器上的 SelectionKey对象。
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println(“A=” + selectionKey.hashCode());
SelectionKey selectionKey1 = serverSocketChannel.keyFor(selector);
System.out.println(“B=” + selectionKey1.hashCode());
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
A=1627674070
B=1627674070
1
2
获得SelectorProvider对象
public final Selector Provider provider()方法的作
用是返回创建此通道的 Selector Provider。
Selector Provider类的作用是用于选择器和可选择通道的服务提供者类。选择器提供者的实现类是 Selector Provider类的一个子类,它具有零参数的构造方法,并实现了抽象方法。给定的对Java虚拟机的调用维护了单个系统级的默认提供者实例,它由 provider方法返回。
在第一次调用该方法时,将查找以下指定的默认提供者。系统级的默认提供者由 DatagramChannel、 Pipe、Selector、 Server SocketChannel和 Socketchannel类的静态 open方法使用。
除了默认提供者之外,程序还可以使用其他提供者,方法是通过实例化一个提供者,然后直接调用此类中定义的 open方法。多个并发线程可安全地使用此类中的所有方法。
SelectorProvider provider = SelectorProvider.provider();
System.out.println(provider);
ServerSocketChannel serverSocketChannel = null;
serverSocketChannel = ServerSocketChannel.open();
SelectorProvider provider1 = serverSocketChannel.provider();
System.out.println(provider1);
serverSocketChannel.close();
1
2
3
4
5
6
7
8
//同一个实例
sun.nio.ch.KQueueSelectorProvider@61bbe9ba
sun.nio.ch.KQueueSelectorProvider@61bbe9ba
1
2
3
通道注册与选择器
- 相同的通道可以注册到不同的选择器,返回的SelectionKey不是同一个对象:
//1. 相同的通道可以注册到不同的选择器,返回的SelectionKey不是同一个对象
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(“localhost”, 8888));
serverSocketChannel.configureBlocking(false);
Selector selector1 = Selector.open();
Selector selector2 = Selector.open();
SelectionKey selectionKey1 = serverSocketChannel.register(selector1, SelectionKey.OP_ACCEPT);
SelectionKey selectionKey2 = serverSocketChannel.register(selector2, SelectionKey.OP_ACCEPT);
System.out.println("selectionKey1=" + selectionKey1);
System.out.println("selectionKey2=" + selectionKey2);
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//不同的实例
selectionKey1=sun.nio.ch.SelectionKeyImpl@610455d6
selectionKey2=sun.nio.ch.SelectionKeyImpl@511d50c0
1
2
3
2. 不同的通道注册到相同的选择器返回的SelectionKey不是同一个对象:
-
不同的通道注册到不同的选择器,返回的SelectionKey不是同一个对象
-
相同的通道重复注册相同的选择器,返回的SelectionKey是同一个对象
返回此通道所支持的操作
public final int validOps()方法的作用是返回一个操作集,标识此通道所支持的操作。因为服务器套接字通道仅支持接受新的连接,所以此方法返回 SelectionKey. OP ACCEPT。
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
SocketChannel socketChannel = SocketChannel.open();
int value1 = serverSocketChannel.validOps();
int value2 = socketChannel.validOps();
System.out.println("value1=" + value1);
System.out.println("value2=" + value2);
System.out.println();
//ServerSocketChannel只支持OP_ACCEPT
System.out.println(SelectionKey.OP_ACCEPT & ~serverSocketChannel.validOps());
System.out.println(SelectionKey.OP_CONNECT & ~serverSocketChannel.validOps());
System.out.println(SelectionKey.OP_READ & ~serverSocketChannel.validOps());
System.out.println(SelectionKey.OP_WRITE & ~serverSocketChannel.validOps());
System.out.println();
//SocketChannel支持OP_CONNECT、OP_READ、OP_WRITE
System.out.println(SelectionKey.OP_ACCEPT & ~socketChannel.validOps());
System.out.println(SelectionKey.OP_CONNECT & ~socketChannel.validOps());
System.out.println(SelectionKey.OP_READ & ~socketChannel.validOps());
System.out.println(SelectionKey.OP_WRITE & ~socketChannel.validOps());
socketChannel.close();
serverSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
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
//输出为0,就说明支持哪个选项:
value1=16
value2=13
0
8
1
4
16
0
0
0
————————————————
版权声明:本文为CSDN博主「吴声子夜歌」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cold___play/article/details/106632505