Skip to content

Commit 9756221

Browse files
committed
Update Java Notes
1 parent cc4cd6c commit 9756221

File tree

2 files changed

+109
-27
lines changed

2 files changed

+109
-27
lines changed

Prog.md

Lines changed: 88 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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

1436014360
Buffer 基本操作:
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

1437914380
Buffer 数据操作:
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

1470114753
3. Channel 在 NIO 中是一个接口:`public interface Channel extends Closeable{}`
1470214754

14703-
14704-
1470514755
Channel 实现类:
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);
1505715111
3. 分配指定大小的缓冲区:`ByteBuffer buffer = ByteBuffer.allocate(1024)`
1505815112
4. 发送数据给服务端
1505915113

15060-
37 行代码,如果判断条件改为 !=-1,需要客户端 shutdown 一下
15114+
37 行代码,如果判断条件改为 !=-1,需要客户端 close 一下
1506115115

1506215116
```java
1506315117
public 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();

Web.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,7 +2175,28 @@ HTTP 和 HTTPS 的区别:
21752175

21762176
思想:锁上加锁
21772177

2178+
HTTPS 工作流程:服务器端的公钥和私钥,用来进行非对称加密,客户端生成的随机密钥,用来进行对称加密
21782179

2180+
![](https://gitee.com/seazean/images/raw/master/Web/HTTP-HTTPS加密过程.png)
2181+
2182+
1. 客户端向服务器发起 HTTPS 请求,连接到服务器的 443 端口
2183+
2184+
2. 服务器端有一个密钥对,即公钥和私钥,用来进行非对称加密,服务器端保存着私钥不能泄露,公钥可以发给任何客户端
2185+
2186+
3. 服务器将公钥发送给客户端
2187+
2188+
4. 客户端收到服务器端的数字证书之后,会对证书进行检查,验证其合法性,如果发现发现证书有问题,那么 HTTPS 传输就无法继续。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,将该密钥称之为 client key,即客户端密钥。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,HTTPS 中的第一次 HTTP 请求结束
2189+
2190+
5. 客户端会发起 HTTPS 中的第二个 HTTP 请求,将加密之后的客户端密钥发送给服务器
2191+
2192+
6. 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文
2193+
2194+
7. 服务器将加密后的密文发送给客户端
2195+
2196+
8. 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据,这样 HTTPS 中的第二个 HTTP 请求结束,整个 HTTPS 传输完成
2197+
2198+
2199+
参考文章:https://www.jianshu.com/p/14cd2c9d2cd2
21792200

21802201

21812202

0 commit comments

Comments
 (0)