0%

java | 阻塞模式

通过服务端和客户端进行演示。

引入自写的方法类。

下载 JAVA 文件

netty 包

1
2
3
4
5
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.86.Final</version>
</dependency>

服务端

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
45
46
package com.redisc;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;

import static com.redisc.ByteBufferUtil.debugRead;

@Slf4j(topic = "c.Test")
public class Server {

public static void main(String[] args) throws IOException {
// 使用 nio 单线程
// bytebuffer
ByteBuffer buffer = ByteBuffer.allocate(16);
// 创建服务器
ServerSocketChannel ssc = ServerSocketChannel.open();
//绑定端口
ssc.bind(new InetSocketAddress(8090));
//连接集合
List<SocketChannel> channelList = new ArrayList<>();
while (true) {
// accept 建立客户断连接,SocketChannel 用来与客户端通信
log.debug("connectinging");
SocketChannel sc = ssc.accept(); // accept 是一个阻塞方法
log.debug("connected");
channelList.add(sc);
for (SocketChannel channel : channelList) {
// 接收客户端发送的数据
log.debug("before read... {}", channel);
channel.read(buffer); // read 也是一个阻塞方法,需要等待客户端发送数据
buffer.flip();
debugRead(buffer);
buffer.clear();
log.debug("after read...{}", channel);
}
}
}

}

要注意的是

  • ssc.accept();
  • channel.read(buffer);
    • 都是阻塞方法

下面使用客户端进行验证。

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.redisc;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;

public class Client {
public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost", 8090));
System.out.println("waiting...");
}
}

验证

开启服务端。

1
15:17:19.095 [main] DEBUG c.Test - connectinging 「阻塞」

开启第一个客户端。在客户端第 11 打一个断点。

1
2
3
15:17:19.095 [main] DEBUG c.Test - connectinging
15:18:05.970 [main] DEBUG c.Test - connected
15:18:05.970 [main] DEBUG c.Test - before read... java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]

客户端在断点处停下。在 sc 变量上右键,点击 evalute

输入

1
sc.write(Charset.defaultCharset().encode("hi"))

并执行。

客户端输出

1
2
3
4
5
6
7
8
9
10
11
12
13
15:17:19.095 [main] DEBUG c.Test - connectinging
15:18:05.970 [main] DEBUG c.Test - connected
15:18:05.970 [main] DEBUG c.Test - before read... java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
15:19:40.632 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
+--------+-------------------- read -----------------------+----------------+
position: [0], limit: [2]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 69 |hi |
+--------+-------------------------------------------------+----------------+
15:19:40.665 [main] DEBUG c.Test - after read...java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
15:19:40.666 [main] DEBUG c.Test - connectinging

evalute 再次执行

1
sc.write(Charset.defaultCharset().encode("world"))

服务端输出没有变化。

这是因为,客户端在执行完上一个 hi 后,while 重新轮回,进入了 SocketChannel sc = ssc.accept(); // accept 是一个阻塞方法 阻塞。导致,world 没有接收到。

此时,开启另一个客户端。服务端输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
15:17:19.095 [main] DEBUG c.Test - connectinging
15:18:05.970 [main] DEBUG c.Test - connected
15:18:05.970 [main] DEBUG c.Test - before read... java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
15:19:40.632 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
+--------+-------------------- read -----------------------+----------------+
position: [0], limit: [2]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 69 |hi |
+--------+-------------------------------------------------+----------------+
15:19:40.665 [main] DEBUG c.Test - after read...java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
15:19:40.666 [main] DEBUG c.Test - connectinging
15:21:44.402 [main] DEBUG c.Test - connected
15:21:44.402 [main] DEBUG c.Test - before read... java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
+--------+-------------------- read -----------------------+----------------+
position: [0], limit: [5]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 77 6f 72 6c 64 |world |
+--------+-------------------------------------------------+----------------+
15:21:44.403 [main] DEBUG c.Test - after read...java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
15:21:44.403 [main] DEBUG c.Test - before read... java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52600]

发现上一个连接的 world 输出出来了。在新开启的客户端执行。

这是因为,当有新的连接出现后,SocketChannel sc = ssc.accept(); // accept 是一个阻塞方法 向下执行。执行了所有连接的接收,所以,world 也就接收到了。

1
sc.write(Charset.defaultCharset().encode("123"))

服务端输出

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
15:17:19.095 [main] DEBUG c.Test - connectinging
15:18:05.970 [main] DEBUG c.Test - connected
15:18:05.970 [main] DEBUG c.Test - before read... java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
15:19:40.632 [main] DEBUG io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
+--------+-------------------- read -----------------------+----------------+
position: [0], limit: [2]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 69 |hi |
+--------+-------------------------------------------------+----------------+
15:19:40.665 [main] DEBUG c.Test - after read...java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
15:19:40.666 [main] DEBUG c.Test - connectinging
15:21:44.402 [main] DEBUG c.Test - connected
15:21:44.402 [main] DEBUG c.Test - before read... java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
+--------+-------------------- read -----------------------+----------------+
position: [0], limit: [5]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 77 6f 72 6c 64 |world |
+--------+-------------------------------------------------+----------------+
15:21:44.403 [main] DEBUG c.Test - after read...java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52515]
15:21:44.403 [main] DEBUG c.Test - before read... java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52600]
+--------+-------------------- read -----------------------+----------------+
position: [0], limit: [3]
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 31 32 33 |123 |
+--------+-------------------------------------------------+----------------+
15:22:51.812 [main] DEBUG c.Test - after read...java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:52600]
15:22:51.812 [main] DEBUG c.Test - connectinging

在上述,我们可以得知,阻塞中,各种连接和接收都是线性执行的。

请我喝杯咖啡吧~