Netty学习前置知识(二)

1.前言

此篇博文主要根据尚硅谷的视频课程把使用Netty需要了解的知识进行整理并根据自己的理解加以补充。由于内容较多所以将笔记分为多个部分,同时更新速度可能会比较慢,希望读者多包涵。

2.ServerSocketChannel 知识点补充

2.1基本介绍

  • 一个面向流侦听的可选通道
  • 通过调用此类的open方法创建一个新的通道
  • 无法为任意预先存在的ServerSocket创建通道
  • 一个新的通道被打开但还未绑定,就尝试调用accept()方法会抛NotYetBoundException异常
  • 通过调用当前类定义的bind的方法来绑定一个server-socket channel
/**
 * A selectable channel for stream-oriented listening sockets.
 * <p> A server-socket channel is created by invoking the {@link #open() open}
 * method of this class.  It is not possible to create a channel for an arbitrary,
 * pre-existing {@link ServerSocket}. A newly-created server-socket channel is
 * open but not yet bound.  An attempt to invoke the {@link #accept() accept}
 * method of an unbound server-socket channel will cause a {@link NotYetBoundException}
 * to be thrown. A server-socket channel can be bound by invoking one of the
 * {@link #bind(java.net.SocketAddress,int) bind} methods defined by this class.
 *
 /

2.2配置套接字选项

使用setOption方法配置套接字选项

  • 选项定义在StandardSocketOptions:
SO_RCVBUF //   套接字缓冲区大小
SO_REUSEADDR//  重复使用的地址
SO_BROADCAST // 广播传输
SO_KEEPALIVE // 连接保持活动状态
SO_LINGER // 延时时间
IP_TOS  // Internet Protocol (IP)头中服务类型
IP_MULTICAST_IF // Internet协议(IP)多播数据报的网络接口 值类型是NetworkInterface
IP_MULTICAST_TTL // Internet协议(IP)多播数据报的网络接口,值类型为Integer范围0 <= value <= 255
IP_MULTICAST_LOOP // Internet协议(IP)多播数据报的环回,值类型Boolean
TCP_NODELAY // 禁用Nagel算法,值类型Boolean, TCP/IP协议使用Nagel合并短段提高网络效率  
  • 方法
public abstract <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
    throws IOException;

3.Selector

3.1基本介绍

  • Java NIO可以处理一个或多个客户端连接,需要使用Selector选择器。
  • Selecttor会检测注册中心通道是否有事件发生,如果有事件发生则对每个事件进行相应的处理。从而达到一个线程管理多个通道。
  • 只有连接的通道发生读写事件时才会进行读写,不需要每次建立线程,减少了线程上下文切换,从而减少了系统开销。

3.2.Selector 特点示意图

在这里插入图片描述

3.3.示意图说明:

  • Netty 的 IO 线程 NioEventLoop 聚合了 Selector(选择器,也叫多路复用器),可以同时并发处理成百上千个客户端连接。
  • 当线程从某客户端 Socket 通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。
  • 线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入和输出通道。
  • 由于读写操作都是非阻塞的,这就可以充分提升 IO 线程的运行效率,避免由于频繁 I/O 阻塞导致的线程挂起。
  • 一个 I/O 线程可以并发处理 N 个客户端连接和读写操作,这从根本上解决了传统同步阻塞 I/O 一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。

3.4.Selector 方法

// 获得选择器对象
public static Selector open() throws IOException {
    return SelectorProvider.provider().openSelector();
}
// 返回Selector 选定的key集合
public abstract Set<SelectionKey> selectedKeys();
// 如果没有事件发生则返回0
public abstract int selectNow() throws IOException;
// 设置超时timeout  毫秒数
public abstract int select(long timeout)throws IOException;
// 唤醒Selector
public abstract Selector wakeup();

3.5.Selector 注意事项

 - ServerSocketChannel类似ServerSocketSockertChannel类似Socket
 - select()阻塞,selectNow()方法不阻塞,立即返回
 - wakeup()唤醒Selector,同步

4.NIO 非阻塞网络编程

4.1.NIO非阻塞网络编程分析图

4.1.1.基本介绍

NIO非阻塞网络编程实际上是(ServerSocketChannel,SocketChannel,Selector,SelecttorKey)之间的关系图。
在这里插入图片描述

4.1.2.说明:

  • 当客户端连接时,会通过 ServerSocketChannel 得到 SocketChannel。
  • Selector 进行监听 select 方法,返回有事件发生的通道的个数。
  • 将 socketChannel 注册到 Selector 上,register(Selector sel, int ops),一个 Selector 上可以注册多个 SocketChannel。
  • 注册后返回一个 SelectionKey,会和该 Selector 关联(集合)。
  • 进一步得到各个 SelectionKey(有事件发生)。
  • 在通过 SelectionKey 反向获取 SocketChannel,方法 channel()。
  • 可以通过得到的 channel,完成业务处理。

4.2.NIO非阻塞网络编程入门实例

4.2.1.基本介绍

1.创建一个服务端ServerSocketChannel01
2.创建一个客户端SocketChannelClient01
3.实例

// ServerSocketChannel01
public class ServerSocketChannel01 {
    public static void main(String[] args) {

        try {
            // todo 目前发送消息有问题
            // 1、创建
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 2、获取Selector
            Selector selector = Selector.open();
            // 3、绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1",8888));
            // 4、设置非阻塞
            serverSocketChannel.configureBlocking(false);
            // 5、注册channel
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true){
                // 6、验证是否有连接事件
                if(selector.select(1000)==0){
                    System.out.println("服务器等待1秒,无连接");
                    continue;
                }
                // 7、处理其他事件
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectionKeys.stream().iterator();
                while (keyIterator.hasNext()){
                    SelectionKey next = keyIterator.next();
                    // 7.1 验证连接时间,接收,获得新的通道并把新通道注册到selector
                    if(next.isAcceptable()){
                        // 接收,获得新的通道
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        if(Objects.nonNull(socketChannel)){
                            System.out.println("客户端连接"+socketChannel.getRemoteAddress());
                            socketChannel.configureBlocking(false);
                            socketChannel.register(selector, SelectionKey.OP_READ,  ByteBuffer.allocate(1024));
                        }

                    }
                    // 7.2 读事件,重新写入buffer
                    if(next.isReadable()){
                        SocketChannel channel = (SocketChannel) next.channel();
                        // 获得Buffer
                        ByteBuffer buffer = (ByteBuffer) next.attachment();
                        channel.read(buffer);
                        System.out.println("服务端读取数据"+new String(buffer.array()));
                    }
                    keyIterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
// SocketChannelClient01
public static void main(String[] args) {

    try {
        // 1、创建
        SocketChannel socketChannel = SocketChannel.open();
        // 设置非阻塞
        socketChannel.configureBlocking(false);
       // 设置地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);

        // 验证是否连接上
        boolean connect = socketChannel.connect(inetSocketAddress);
        if(connect){
            // 将数据写入channel
            socketChannel.write(ByteBuffer.wrap("NIO测试".getBytes(StandardCharsets.UTF_8)));
            //System.in.read();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

5.SelectionKey

5.1.基本介绍

  • 每次向Selector注册通道时都会创建一个key。
  • 一直保持密钥有效直到调用cancel方法关闭通道或者关闭选择器来取消密钥。
  • 取消一个密钥不能立即从Selector中删除。而是把它添加到cancelled-key set中,以便在下一次选择操作中将秘钥删除。
  • 调用isValid方法测试秘钥有效性

5.2.SelectionKey的几种状态

// 读操作位置设置,值为1
public static final int OP_READ = 1 << 0;
// 写操作的操作设置位,值为4
public static final int OP_WRITE = 1 << 2;
// 用于套接字连接操作的操作设置位,值8
public static final int OP_CONNECT = 1 << 3;
// 用于套接字操作的操作设置位16
public static final int OP_ACCEPT = 1 << 4;

5.3.SelectionKey相关方法

// 返回为其创建密钥的通道
public abstract SelectableChannel channel();
// 获得选择器
public abstract Selector selector();
// 验证有效性
public abstract boolean isValid();
// 取消此密钥通道及其选择器的注册
public abstract void cancel();
// 是否可以只读
public final boolean isReadable();
// 是否可以只写
public final boolean isWritable();
// 测试此通道是否已完成或未能完成其套接字连接操作。
public final boolean isConnectable();
// 测试此密钥的通道是否准备好接受新套接字连接
public final boolean isAcceptable();

6.SocketChannel

6.1.基本介绍

SocketChannel,网络 IO 通道,具体负责进行读写操作。NIO 把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。
相关方法如下

// 获取channle通道
public static SocketChannel open() throws IOException {
    return SelectorProvider.provider().openSocketChannel();
}
// 返回标识此通道支持的操作的操作集
public final int validOps() {
    return (SelectionKey.OP_READ| SelectionKey.OP_WRITE| SelectionKey.OP_CONNECT);
}
// 绑定地址
public abstract SocketChannel bind(SocketAddress local)throws IOException;
// 设置套接字值类型
public abstract <T> SocketChannel setOption(SocketOption<T> name, T value)throws IOException;
// 在不关闭通道的情况下关闭连接以进行读取
public abstract SocketChannel shutdownInput() throws IOException;
// 在不关闭通道的情况下关闭连接以进行写入。
public abstract SocketChannel shutdownOutput() throws IOException;
// 获得套接字
public abstract Socket socket();
// 验证channel是否连接
public abstract boolean isConnected();
// 判断此通道上的连接操作是否正在进行中
public abstract boolean isConnectionPending();
// 连接服务器
public abstract boolean connect(SocketAddress remote) throws IOException;

6.2.SelectableChannel相关方法

// 将channel注册到Selctor中
public abstract boolean isRegistered();
// 设置channel阻塞或非阻塞
public abstract SelectableChannel configureBlocking(boolean block)throws IOException;

7.笔记回顾

1.主要记录了ServerSocketChannel,SocketChannel,Selector,SelecttorKey之间关系
2.分别介绍了Selector,ServerSocketChannel,SocketChannel,SelecttorKey相关方法
3.分别介绍了Selector,ServerSocketChannel,SocketChannel,SelecttorKey的作用方法及特点
4.套接字选项的状态StandardSocketOptions
5.SelectionKey的几种状态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值