通过服务端和客户端进行演示。
引入自写的方法类。
下载 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 { ByteBuffer buffer = ByteBuffer.allocate(16); ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.bind(new InetSocketAddress(8090)); List<SocketChannel> channelList = new ArrayList<>(); while (true) { log.debug("connectinging"); SocketChannel sc = ssc.accept(); log.debug("connected"); channelList.add(sc); for (SocketChannel channel : channelList) { log.debug("before read... {}", channel); channel.read(buffer); 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
|
在上述,我们可以得知,阻塞中,各种连接和接收都是线性执行的。