@@ -2726,7 +2726,7 @@ getInstance 方法对应的字节码为:
27262726* 21 表示利用一个对象引用,调用构造方法初始化对象
27272727* 24 表示利用一个对象引用,赋值给 static INSTANCE
27282728
2729- 步骤 21 和 24 之间不存在数据依赖关系,而且无论重排前后,程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的
2729+ ** 步骤 21 和 24 之间不存在数据依赖关系** ,而且无论重排前后,程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的
27302730
27312731* 关键在于 0:getstatic 这行代码在 monitor 控制之外,可以越过 monitor 读取 INSTANCE 变量的值
27322732* 当其他线程访问 instance 不为 null 时,由于 instance 实例未必已初始化,那么 t2 拿到的是将是一个未初始化完毕的单例返回,这就造成了线程安全的问题
@@ -14359,29 +14359,30 @@ Java NIO 系统的核心在于:通道和缓冲区,通道表示打开的 IO
1435914359
1436014360Buffer 基本操作:
1436114361
14362- | 方法 | 说明 |
14363- | ------------------------------------------- | ------------------------------------------------------- |
14364- | public Buffer clear() | 清空缓冲区,不清空内容,将位置设置为零,限制设置为容量 |
14365- | public Buffer flip() | 翻转缓冲区,将缓冲区的界限设置为当前位置,position 置 0 |
14366- | public int capacity() | 返回 Buffer的 capacity 大小 |
14367- | public final int limit() | 返回 Buffer 的界限 limit 的位置 |
14368- | public Buffer limit(int n) | 设置缓冲区界限为 n |
14369- | public Buffer mark() | 在此位置对缓冲区设置标记 |
14370- | public final int position() | 返回缓冲区的当前位置 position |
14371- | public Buffer position(int n) | 设置缓冲区的当前位置为n |
14372- | public Buffer reset() | 将位置 position 重置为先前 mark 标记的位置 |
14373- | public Buffer rewind() | 将位置设为为0,取消设置的 mark |
14374- | public final int remaining() | 返回当前位置 position 和 limit 之间的元素个数 |
14375- | public final boolean hasRemaining() | 判断缓冲区中是否还有元素 |
14376- | public static ByteBuffer wrap(byte[] array) | 将一个字节数组包装到缓冲区中 |
14377- | abstract ByteBuffer asReadOnlyBuffer() | 创建一个新的只读字节缓冲区 |
14362+ | 方法 | 说明 |
14363+ | ------------------------------------------- | ------------------------------------------------------------ |
14364+ | public Buffer clear() | 清空缓冲区,不清空内容,将位置设置为零,限制设置为容量 |
14365+ | public Buffer flip() | 翻转缓冲区,将缓冲区的界限设置为当前位置,position 置 0 |
14366+ | public int capacity() | 返回 Buffer的 capacity 大小 |
14367+ | public final int limit() | 返回 Buffer 的界限 limit 的位置 |
14368+ | public Buffer limit(int n) | 设置缓冲区界限为 n |
14369+ | public Buffer mark() | 在此位置对缓冲区设置标记 |
14370+ | public final int position() | 返回缓冲区的当前位置 position |
14371+ | public Buffer position(int n) | 设置缓冲区的当前位置为n |
14372+ | public Buffer reset() | 将位置 position 重置为先前 mark 标记的位置 |
14373+ | public Buffer rewind() | 将位置设为为 0,取消设置的 mark |
14374+ | public final int remaining() | 返回当前位置 position 和 limit 之间的元素个数 |
14375+ | public final boolean hasRemaining() | 判断缓冲区中是否还有元素 |
14376+ | public static ByteBuffer wrap(byte[] array) | 将一个字节数组包装到缓冲区中 |
14377+ | abstract ByteBuffer asReadOnlyBuffer() | 创建一个新的只读字节缓冲区 |
14378+ | public abstract ByteBuffer compact() | 缓冲区当前位置与其限制(如果有)之间的字节被复制到缓冲区的开头 |
1437814379
1437914380Buffer 数据操作:
1438014381
1438114382| 方法 | 说明 |
1438214383| ------------------------------------------------- | ----------------------------------------------- |
14383- | public abstract byte get() | 读取该缓冲区当前位置的单个字节,然后增加位置 |
14384- | public ByteBuffer get(byte[] dst) | 读取多个字节到字节数组dst中 |
14384+ | public abstract byte get() | 读取该缓冲区当前位置的单个字节,然后位置 + 1 |
14385+ | public ByteBuffer get(byte[] dst) | 读取多个字节到字节数组 dst 中 |
1438514386| public abstract byte get(int index) | 读取指定索引位置的字节,不移动 position |
1438614387| public abstract ByteBuffer put(byte b) | 将给定单个字节写入缓冲区的当前位置,position+1 |
1438714388| public final ByteBuffer put(byte[] src) | 将 src 字节数组写入缓冲区的当前位置 |
@@ -14452,6 +14453,57 @@ public class TestBuffer {
1445214453
1445314454
1445414455
14456+ ****
14457+
14458+
14459+
14460+ #### 粘包拆包
14461+
14462+ 网络上有多条数据发送给服务端,数据之间使用 \n 进行分隔,但这些数据在接收时,被进行了重新组合
14463+
14464+ ```java
14465+ // Hello,world\n
14466+ // I'm zhangsan\n
14467+ // How are you?\n
14468+ ------ > 黏包,半包
14469+ // Hello,world\nI'm zhangsan\nHo
14470+ // w are you?\n
14471+ ```
14472+
14473+ ```java
14474+ public static void main(String[] args) {
14475+ ByteBuffer source = ByteBuffer.allocate(32);
14476+ // 11 24
14477+ source.put("Hello,world\nI'm zhangsan\nHo".getBytes());
14478+ split(source);
14479+
14480+ source.put("w are you?\nhaha!\n".getBytes());
14481+ split(source);
14482+ }
14483+
14484+ private static void split(ByteBuffer source) {
14485+ source.flip();
14486+ int oldLimit = source.limit();
14487+ for (int i = 0; i < oldLimit; i++) {
14488+ if (source.get(i) == '\n') {
14489+ // 根据数据的长度设置缓冲区
14490+ ByteBuffer target = ByteBuffer.allocate(i + 1 - source.position());
14491+ // 0 ~ limit
14492+ source.limit(i + 1);
14493+ target.put(source); // 从source 读,向 target 写
14494+ // debugAll(target); 访问 buffer 的方法
14495+ source.limit(oldLimit);
14496+ }
14497+ }
14498+ // 访问过的数据复制到开头
14499+ source.compact();
14500+ }
14501+ ```
14502+
14503+
14504+
14505+
14506+
1445514507****
1445614508
1445714509
@@ -14700,11 +14752,12 @@ public class MappedByteBufferTest {
1470014752
14701147533. Channel 在 NIO 中是一个接口:`public interface Channel extends Closeable{}`
1470214754
14703-
14704-
1470514755Channel 实现类:
1470614756
14707- * FileChannel:用于读取、写入、映射和操作文件的通道
14757+ * FileChannel:用于读取、写入、映射和操作文件的通道,只能工作在阻塞模式下
14758+ * 通过 FileInputStream 获取的 Channel 只能读
14759+ * 通过 FileOutputStream 获取的 Channel 只能写
14760+ * 通过 RandomAccessFile 是否能读写根据构造 RandomAccessFile 时的读写模式决定
1470814761* DatagramChannel:通过 UDP 读写网络中的数据通道
1470914762* SocketChannel:通过 TCP 读写网络中的数据
1471014763* ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个SocketChannel。
@@ -14729,9 +14782,9 @@ Channel 基本操作:
1472914782| 方法 | 说明 |
1473014783| ------------------------------------------ | -------------------------------------------------------- |
1473114784| public abstract int read(ByteBuffer dst) | 从 Channel 中读取数据到 ByteBuffer,从 position 开始储存 |
14732- | public final long read(ByteBuffer[] dsts) | 将Channel中的数据“分散”到ByteBuffer[] |
14785+ | public final long read(ByteBuffer[] dsts) | 将 Channel 中的数据分散到 ByteBuffer[] |
1473314786| public abstract int write(ByteBuffer src) | 将 ByteBuffer 中的数据写入 Channel,从 position 开始写出 |
14734- | public final long write(ByteBuffer[] srcs) | 将ByteBuffer[]到中的数据“聚集”到Channel |
14787+ | public final long write(ByteBuffer[] srcs) | 将 ByteBuffer[] 到中的数据聚集到 Channel |
1473514788| public abstract long position() | 返回此通道的文件位置 |
1473614789| FileChannel position(long newPosition) | 设置此通道的文件位置 |
1473714790| public abstract long size() | 返回此通道的文件的当前大小 |
@@ -14941,7 +14994,7 @@ public class ChannelTest {
1494114994
1494214995创建 Selector:`Selector selector = Selector.open();`
1494314996
14944- 向选择器注册通道:`SelectableChannel.register(Selector sel, int ops)`
14997+ 向选择器注册通道:`SelectableChannel.register(Selector sel, int ops, Object att )`
1494514998
1494614999选择器对通道的监听事件,需要通过第二个参数 ops 指定。监听的事件类型用四个常量表示:
1494715000
@@ -14969,6 +15022,7 @@ SelectionKey API:
1496915022| ------------------------------------------- | -------------------------------------------------- |
1497015023| public abstract void cancel() | 取消该键的通道与其选择器的注册 |
1497115024| public abstract SelectableChannel channel() | 返回创建此键的通道,该方法在取消键之后仍将返回通道 |
15025+ | public final Object attachment() | 返回当前 key 关联的缓冲 |
1497215026| public final boolean isAcceptable() | 检测此密钥的通道是否已准备好接受新的套接字连接 |
1497315027| public final boolean isConnectable() | 检测此密钥的通道是否已完成或未完成其套接字连接操作 |
1497415028| public final boolean isReadable() | 检测此密钥的频道是否可以阅读 |
@@ -15057,7 +15111,7 @@ ssChannel.register(selector, SelectionKey.OP_ACCEPT);
15057151113. 分配指定大小的缓冲区:`ByteBuffer buffer = ByteBuffer.allocate(1024)`
15058151124. 发送数据给服务端
1505915113
15060- 37 行代码,如果判断条件改为 !=-1,需要客户端 shutdown 一下
15114+ 37 行代码,如果判断条件改为 !=-1,需要客户端 close 一下
1506115115
1506215116```java
1506315117public class Server {
@@ -15087,6 +15141,11 @@ public class Server {
1508715141 SocketChannel socketChannel = serverSocketChannel.accept();
1508815142 // 11 、切换成非阻塞模式
1508915143 socketChannel.configureBlocking(false);
15144+ /*
15145+ ByteBuffer buffer = ByteBuffer.allocate(16);
15146+ // 将一个 byteBuffer 作为附件【关联】到 selectionKey 上
15147+ SelectionKey scKey = sc.register(selector, 0, buffer);
15148+ */
1509015149 // 12、将本客户端通道注册到选择器
1509115150 socketChannel.register(selector, SelectionKey.OP_READ);
1509215151 } else if (key.isReadable()) {
@@ -15095,6 +15154,8 @@ public class Server {
1509515154 SocketChannel socketChannel = (SocketChannel) channel;
1509615155 // 14、读取数据
1509715156 ByteBuffer buffer = ByteBuffer.allocate(1024);
15157+ // 获取关联的附件
15158+ // ByteBuffer buffer = (ByteBuffer) key.attachment();
1509815159 int len;
1509915160 while ((len = socketChannel.read(buffer)) > 0) {
1510015161 buffer.flip();
@@ -15124,7 +15185,7 @@ public class Client {
1512415185 while (true){
1512515186 System.out.print("请说:");
1512615187 String msg = sc.nextLine();
15127- buffer.put(("波妞 :" + msg).getBytes());
15188+ buffer.put(("Client :" + msg).getBytes());
1512815189 buffer.flip();
1512915190 socketChannel.write(buffer);
1513015191 buffer.clear();
0 commit comments