Netty源码分析---位运算详解

相关文章链接

位运算详解

waken方法详解

ThreadPerTaskExecutor与线程创建详解

processSelectedKeys() vs runAllTasks()

NioServerSocketChannel-Unsafe初始化详解

NioEventLoop的run方法详解

NioEventLoopGroup深度解析

inEventLoop方法详解

executionMask详解

Netty源码分析–认真系列(一)

Netty源码分析–认真系列(二)

位运算基础

什么是位运算?

位运算就是直接对二进制数字的每一位(0 或 1)进行操作的运算。

先理解二进制

我们平时用的是十进制(0-9),计算机用的是二进制(0-1):

十进制    二进制
0    →   0000
1    →   0001
2    →   0010
3    →   0011
4    →   0100
5    →   0101
6    →   0110
7    →   0111
8    →   1000

转换规则:从右往左,每一位代表 2 的 n 次方

二进制: 1001
       ↓↓↓↓
位置:   3210  (从右往左数,从0开始)

计算: 1×2³ + 0×2² + 0×2¹ + 1×2⁰
    = 1×8  + 0×4  + 0×2  + 1×1
    = 8 + 0 + 0 + 1
    = 9 (十进制)

1. 左移运算符 <<

基本概念

<< 表示左移,就是把二进制数字向左移动若干位。

语法数字 << 移动位数

图解左移
原始数字: 1 (二进制: 0001)

1 << 0  →  0001  (向左移0位,不动)  = 1
1 << 1  →  0010  (向左移1位)       = 2
1 << 2  →  0100  (向左移2位)       = 4
1 << 3  →  1000  (向左移3位)       = 8
1 << 4  → 10000  (向左移4位)       = 16

规律1 << n 等于 2^n(2 的 n 次方)

1 << 0 = 2⁰ = 1
1 << 1 = 2¹ = 2
1 << 2 = 2² = 4
1 << 3 = 2³ = 8
1 << 4 = 2⁴ = 16
为什么左移一位等于乘以2?
十进制类比:
123 向左移一位(补0) = 1230 = 123 × 10

二进制同理:
101 (5) 向左移一位 = 1010 (10) = 5 × 2

2. 位或运算 |

基本概念

| 表示位或运算,规则:只要有一个是 1,结果就是 1

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
组合多个事件
// 同时关心 READ 和 WRITE
int events = OP_READ | OP_WRITE;

// 计算过程:
OP_READ  = 0001
OP_WRITE = 0100
         ------  (对齐每一位进行或运算)
结果     = 0101  (十进制: 5)

// 验证:0: 0 | 0 = 01: 0 | 1 = 1OP_READ 的位
第2: 1 | 0 = 1OP_WRITE 的位
第3: 0 | 0 = 0
结果: 0101
更多组合示例
// 示例1: READ + WRITE + ACCEPT
int events = OP_READ | OP_WRITE | OP_ACCEPT;

OP_READ   = 00001
OP_WRITE  = 00100
OP_ACCEPT = 10000
          -------
结果      = 10101  (十进制: 21)

// 示例2: 所有事件
int events = OP_READ | OP_WRITE | OP_CONNECT | OP_ACCEPT;

OP_READ    = 00001
OP_WRITE   = 00100
OP_CONNECT = 01000
OP_ACCEPT  = 10000
           -------
结果       = 11101  (十进制: 29)

3. 位与运算 &

基本概念

& 表示位与运算,规则:两个都是 1,结果才是 1

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
判断是否包含某个事件
// 假设 interestOps = 5 (二进制: 0101)
// 表示同时关心 READ 和 WRITE

int interestOps = 5;  // 0101

// 判断是否包含 READ
(interestOps & OP_READ) != 0

// 计算过程:
interestOps = 0101
OP_READ     = 0001
            ------
结果        = 0001  (不等于0,说明包含 READ)

// 判断是否包含 WRITE
(interestOps & OP_WRITE) != 0

// 计算过程:
interestOps = 0101
OP_WRITE    = 0100
            ------
结果        = 0100  (不等于0,说明包含 WRITE)

// 判断是否包含 ACCEPT
(interestOps & OP_ACCEPT) != 0

// 计算过程:
interestOps = 0101
OP_ACCEPT   = 10000
            -------
结果        = 00000  (等于0,说明不包含 ACCEPT)

核心位运算符总结

运算符名称作用示例
<<左移向左移动若干位1 << 2 = 4
|位或组合多个标志OP_READ | OP_WRITE
&位与判断是否包含某个标志(ops & OP_READ) != 0
~位非取反~OP_WRITE

四、SelectionKey 中的位运算

事件常量定义

public static final int OP_READ    = 1 << 0;  // 二进制: 0001 (十进制: 1)
public static final int OP_WRITE   = 1 << 2;  // 二进制: 0100 (十进制: 4)
public static final int OP_CONNECT = 1 << 3;  // 二进制: 1000 (十进制: 8)
public static final int OP_ACCEPT  = 1 << 4;  // 二进制: 10000 (十进制: 16)

详细计算过程

// OP_READ = 1 << 0
原始:  0001  (1)
左移0: 0001  (不动)
结果:  1 (十进制)

// OP_WRITE = 1 << 2
原始:  0001  (1)
左移2: 0100  (向左移2位,右边补0)
结果:  4 (十进制)

// OP_CONNECT = 1 << 3
原始:  0001  (1)
左移3: 1000  (向左移3位,右边补0)
结果:  8 (十进制)

// OP_ACCEPT = 1 << 4
原始:   0001  (1)
左移4: 10000  (向左移4位,右边补0)
结果:   16 (十进制)

为什么要这样设计?

关键点:每个事件占用不同的二进制位!

OP_READ    = 0001   (第0位是1)
OP_WRITE   = 0100   (第2位是1)
OP_CONNECT = 1000   (第3位是1)
OP_ACCEPT  = 10000  (第4位是1)

这样设计的好处:可以用一个数字同时表示多个事件

interestOps() 方法返回值详解

返回值类型int

返回值含义:一个整数,用二进制位表示当前关心的所有事件。

// 假设只关心 READ 事件
key.interestOps(SelectionKey.OP_READ);
int interestSet = key.interestOps();
System.out.println(interestSet);  // 输出: 1
System.out.println(Integer.toBinaryString(interestSet));  // 输出: 1 (二进制: 0001)

// 假设关心 READ 和 WRITE 事件
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
int interestSet = key.interestOps();
System.out.println(interestSet);  // 输出: 5
System.out.println(Integer.toBinaryString(interestSet));  // 输出: 101 (二进制: 0101)
// 解释: 0001 (READ) | 0100 (WRITE) = 0101 (5)

// 假设关心所有事件
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_ACCEPT);
int interestSet = key.interestOps();
System.out.println(interestSet);  // 输出: 21
System.out.println(Integer.toBinaryString(interestSet));  // 输出: 10101 (二进制: 010101)
// 解释: 0001 (READ) | 0100 (WRITE) | 10000 (ACCEPT) = 10101 (21)

如何判断返回值中包含哪些事件?

使用位与运算(&)

int interestSet = key.interestOps();

// 使用位与运算判断
if ((interestSet & SelectionKey.OP_READ) != 0) {
    System.out.println("关心 READ 事件");
}

if ((interestSet & SelectionKey.OP_WRITE) != 0) {
    System.out.println("关心 WRITE 事件");
}

if ((interestSet & SelectionKey.OP_ACCEPT) != 0) {
    System.out.println("关心 ACCEPT 事件");
}

if ((interestSet & SelectionKey.OP_CONNECT) != 0) {
    System.out.println("关心 CONNECT 事件");
}

设置多个事件

可以! 使用**位或运算(|)**组合多个事件。

// 关心单个事件
key.interestOps(SelectionKey.OP_READ);

// 关心两个事件
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);

// 关心三个事件
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_ACCEPT);

// 关心所有事件(不常用)
key.interestOps(
    SelectionKey.OP_READ | 
    SelectionKey.OP_WRITE | 
    SelectionKey.OP_ACCEPT | 
    SelectionKey.OP_CONNECT
);

动态修改关心的事件

// 初始只关心读
key.interestOps(SelectionKey.OP_READ);

// 添加写事件(保留原有的读事件)
int currentOps = key.interestOps();
key.interestOps(currentOps | SelectionKey.OP_WRITE);
// 现在同时关心 READ 和 WRITE

// 移除写事件(只保留读事件)
currentOps = key.interestOps();
key.interestOps(currentOps & ~SelectionKey.OP_WRITE);
// 现在只关心 READ

常见组合场景

// 1. 服务端:只关心接受新连接
serverKey.interestOps(SelectionKey.OP_ACCEPT);

// 2. 客户端连接中:关心连接是否完成
clientKey.interestOps(SelectionKey.OP_CONNECT);

// 3. 正常通信:同时关心读和写
clientKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);

// 4. 只读模式:只关心读
clientKey.interestOps(SelectionKey.OP_READ);

// 5. 只写模式:只关心写(缓冲区满时常用)
clientKey.interestOps(SelectionKey.OP_WRITE);

为什么 SelectionKey 用位运算?

  1. 节省空间:一个 int (4字节) 可以表示 32 种事件
  2. 高效:位运算是 CPU 最快的运算
  3. 方便组合:用 | 轻松组合多个事件
  4. 方便判断:用 & 快速判断是否包含某个事件

五、完整示例代码

示例1:位运算演示

public class BitOperationDemo {
    // 定义事件常量
    public static final int OP_READ    = 1 << 0;  // 1
    public static final int OP_WRITE   = 1 << 2;  // 4
    public static final int OP_CONNECT = 1 << 3;  // 8
    public static final int OP_ACCEPT  = 1 << 4;  // 16
    
    public static void main(String[] args) {
        System.out.println("=== 事件常量值 ===");
        System.out.println("OP_READ    = " + OP_READ + 
            " (二进制: " + toBinaryString(OP_READ) + ")");
        System.out.println("OP_WRITE   = " + OP_WRITE + 
            " (二进制: " + toBinaryString(OP_WRITE) + ")");
        System.out.println("OP_CONNECT = " + OP_CONNECT + 
            " (二进制: " + toBinaryString(OP_CONNECT) + ")");
        System.out.println("OP_ACCEPT  = " + OP_ACCEPT + 
            " (二进制: " + toBinaryString(OP_ACCEPT) + ")");
        
        System.out.println("\n=== 组合多个事件(位或运算 |)===");
        
        // 组合 READ 和 WRITE
        int events1 = OP_READ | OP_WRITE;
        System.out.println("READ | WRITE = " + events1 + 
            " (二进制: " + toBinaryString(events1) + ")");
        
        // 组合 READ、WRITE 和 ACCEPT
        int events2 = OP_READ | OP_WRITE | OP_ACCEPT;
        System.out.println("READ | WRITE | ACCEPT = " + events2 + 
            " (二进制: " + toBinaryString(events2) + ")");
        
        System.out.println("\n=== 判断是否包含某个事件(位与运算 &)===");
        
        int interestOps = OP_READ | OP_WRITE;  // 5
        System.out.println("当前关心的事件: " + interestOps + 
            " (二进制: " + toBinaryString(interestOps) + ")");
        
        // 判断是否包含各个事件
        System.out.println("包含 READ?    " + 
            ((interestOps & OP_READ) != 0));
        System.out.println("包含 WRITE?   " + 
            ((interestOps & OP_WRITE) != 0));
        System.out.println("包含 CONNECT? " + 
            ((interestOps & OP_CONNECT) != 0));
        System.out.println("包含 ACCEPT?  " + 
            ((interestOps & OP_ACCEPT) != 0));
        
        System.out.println("\n=== 添加事件 ===");
        int ops = OP_READ;
        System.out.println("初始: " + ops + 
            " (二进制: " + toBinaryString(ops) + ")");
        
        ops = ops | OP_WRITE;  // 添加 WRITE
        System.out.println("添加 WRITE 后: " + ops + 
            " (二进制: " + toBinaryString(ops) + ")");
        
        ops = ops | OP_ACCEPT;  // 添加 ACCEPT
        System.out.println("添加 ACCEPT 后: " + ops + 
            " (二进制: " + toBinaryString(ops) + ")");
        
        System.out.println("\n=== 移除事件(位与非运算 & ~)===");
        ops = ops & ~OP_WRITE;  // 移除 WRITE
        System.out.println("移除 WRITE 后: " + ops + 
            " (二进制: " + toBinaryString(ops) + ")");
    }
    
    // 工具方法:转换为固定长度的二进制字符串
    private static String toBinaryString(int num) {
        String binary = Integer.toBinaryString(num);
        // 补齐到5位
        return String.format("%5s", binary).replace(' ', '0');
    }
}

运行结果

=== 事件常量值 ===
OP_READ    = 1 (二进制: 00001)
OP_WRITE   = 4 (二进制: 00100)
OP_CONNECT = 8 (二进制: 01000)
OP_ACCEPT  = 16 (二进制: 10000)

=== 组合多个事件(位或运算 |)===
READ | WRITE = 5 (二进制: 00101)
READ | WRITE | ACCEPT = 21 (二进制: 10101)

=== 判断是否包含某个事件(位与运算 &)===
当前关心的事件: 5 (二进制: 00101)
包含 READ?    true
包含 WRITE?   true
包含 CONNECT? false
包含 ACCEPT?  false

=== 添加事件 ===
初始: 1 (二进制: 00001)
添加 WRITE 后: 5 (二进制: 00101)
添加 ACCEPT 后: 21 (二进制: 10101)

=== 移除事件(位与非运算 & ~)===
移除 WRITE 后: 17 (二进制: 10001)

示例2:SelectionKey 完整生命周期

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class SelectionKeyLifecycleDemo {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(7397));
        serverChannel.configureBlocking(false);
        
        // 1. 创建 SelectionKey(注册时返回)
        SelectionKey serverKey = serverChannel.register(
            selector, 
            SelectionKey.OP_ACCEPT
        );
        
        System.out.println("=== SelectionKey 创建成功 ===");
        System.out.println("关联的 Channel: " + serverKey.channel());
        System.out.println("关联的 Selector: " + serverKey.selector());
        System.out.println("感兴趣的事件: " + serverKey.interestOps());
        
        while (true) {
            // 2. 等待事件就绪
            selector.select();
            
            // 3. 获取就绪的 SelectionKey 集合
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();
            
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove(); // 必须手动移除!
                
                // 4. 检查 key 的状态
                if (!key.isValid()) {
                    System.out.println("Key 已失效");
                    continue;
                }
                
                // 5. 根据就绪事件进行处理
                if (key.isAcceptable()) {
                    System.out.println("=== 处理 ACCEPT 事件 ===");
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel client = server.accept();
                    
                    client.configureBlocking(false);
                    
                    // 6. 新连接也注册,返回新的 SelectionKey
                    SelectionKey clientKey = client.register(
                        selector, 
                        SelectionKey.OP_READ
                    );
                    
                    // 7. 附加数据到 SelectionKey
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    clientKey.attach(buffer);
                    
                    System.out.println("新客户端连接,创建新的 SelectionKey");
                }
                
                if (key.isReadable()) {
                    System.out.println("=== 处理 READ 事件 ===");
                    SocketChannel client = (SocketChannel) key.channel();
                    
                    // 8. 获取附加的 Buffer
                    ByteBuffer buffer = (ByteBuffer) key.attachment();
                    
                    int len = client.read(buffer);
                    if (len > 0) {
                        buffer.flip();
                        byte[] data = new byte[buffer.remaining()];
                        buffer.get(data);
                        System.out.println("收到数据: " + new String(data));
                        buffer.clear();
                        
                        // 9. 修改感兴趣的事件(现在想写数据)
                        key.interestOps(SelectionKey.OP_WRITE);
                    } else if (len == -1) {
                        // 10. 客户端断开,取消 key
                        System.out.println("客户端断开连接");
                        key.cancel();
                        client.close();
                    }
                }
                
                if (key.isWritable()) {
                    System.out.println("=== 处理 WRITE 事件 ===");
                    SocketChannel client = (SocketChannel) key.channel();
                    
                    ByteBuffer response = ByteBuffer.wrap("Hello Client!".getBytes());
                    client.write(response);
                    
                    // 写完后,改回关注读事件
                    key.interestOps(SelectionKey.OP_READ);
                }
            }
        }
    }
}

示例3:实际应用场景

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

public class SelectionKeyPracticalDemo {
    
    // 客户端会话信息
    static class ClientSession {
        String clientId;
        long connectTime;
        ByteBuffer buffer;
        
        public ClientSession() {
            this.clientId = UUID.randomUUID().toString();
            this.connectTime = System.currentTimeMillis();
            this.buffer = ByteBuffer.allocate(1024);
        }
    }
    
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        
        // 场景1:服务端 ServerSocketChannel
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(7397));
        serverChannel.configureBlocking(false);
        
        // 服务端通常只关心 ACCEPT 事件
        SelectionKey serverKey = serverChannel.register(
            selector, 
            SelectionKey.OP_ACCEPT
        );
        
        System.out.println("=== 服务端启动,监听端口 7397 ===");
        printInterestOps("服务端", serverKey);
        
        while (true) {
            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();
            
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                
                if (!key.isValid()) {
                    continue;
                }
                
                if (key.isAcceptable()) {
                    // 接受客户端连接
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel client = server.accept();
                    client.configureBlocking(false);
                    
                    // 场景2:为每个客户端创建会话信息
                    ClientSession session = new ClientSession();
                    
                    // 场景3:客户端 SocketChannel 同时关心 READ 和 WRITE
                    SelectionKey clientKey = client.register(
                        selector,
                        SelectionKey.OP_READ | SelectionKey.OP_WRITE
                    );
                    
                    // 场景4:将会话信息附加到 SelectionKey
                    clientKey.attach(session);
                    
                    System.out.println("\n=== 新客户端连接 ===");
                    System.out.println("客户端ID: " + session.clientId);
                    System.out.println("连接时间: " + session.connectTime);
                    printInterestOps("客户端", clientKey);
                }
                
                if (key.isReadable()) {
                    System.out.println("\n=== 可读事件触发 ===");
                    SocketChannel client = (SocketChannel) key.channel();
                    
                    // 场景5:从 SelectionKey 获取会话信息
                    ClientSession session = (ClientSession) key.attachment();
                    
                    int len = client.read(session.buffer);
                    
                    if (len > 0) {
                        session.buffer.flip();
                        byte[] data = new byte[session.buffer.remaining()];
                        session.buffer.get(data);
                        System.out.println("客户端ID: " + session.clientId);
                        System.out.println("收到数据: " + new String(data));
                        session.buffer.clear();
                        
                        // 场景6:读完数据后,只关心写事件
                        key.interestOps(SelectionKey.OP_WRITE);
                        System.out.println("修改为只关心 WRITE 事件");
                        printInterestOps("客户端", key);
                    } else if (len == -1) {
                        System.out.println("客户端断开: " + session.clientId);
                        key.cancel();
                        client.close();
                    }
                }
                
                if (key.isWritable()) {
                    System.out.println("\n=== 可写事件触发 ===");
                    SocketChannel client = (SocketChannel) key.channel();
                    ClientSession session = (ClientSession) key.attachment();
                    
                    ByteBuffer response = ByteBuffer.wrap(
                        ("Hello, " + session.clientId).getBytes()
                    );
                    client.write(response);
                    
                    System.out.println("发送响应给客户端: " + session.clientId);
                    
                    // 场景7:写完后,改回只关心读事件
                    key.interestOps(SelectionKey.OP_READ);
                    System.out.println("修改为只关心 READ 事件");
                    printInterestOps("客户端", key);
                }
            }
        }
    }
    
    // 工具方法:打印当前关心的事件
    private static void printInterestOps(String name, SelectionKey key) {
        int ops = key.interestOps();
        System.out.println(name + " interestOps: " + ops + 
            " (二进制: " + Integer.toBinaryString(ops) + ")");
        System.out.println("关心的事件:");
        
        if ((ops & SelectionKey.OP_READ) != 0) {
            System.out.println("  - OP_READ (可读)");
        }
        if ((ops & SelectionKey.OP_WRITE) != 0) {
            System.out.println("  - OP_WRITE (可写)");
        }
        if ((ops & SelectionKey.OP_ACCEPT) != 0) {
            System.out.println("  - OP_ACCEPT (接受连接)");
        }
        if ((ops & SelectionKey.OP_CONNECT) != 0) {
            System.out.println("  - OP_CONNECT (连接完成)");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值