Netty的相关知识

1、Java BIO(传统的Java IO模型)的相关知识

BIO同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。
![Image text](https://unpkg.zhimg.com/youthlql@1.0.0/netty/introduction/chapter_001/0003.png)
package BIO;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BIOServer {
    public static void main(String[] args) throws IOException {
        /**
         * 线程池机制
         * 思路:
         * 1、创建线程池
         * 2、如果有客户端连接,就创建一个线程,与之通信
         */
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

        // 创建ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);

        System.out.println("服务端启动了");

        while (true) {
            // 监听,等待客户端连接
            System.out.println("线程信息id = " + Thread.currentThread().getId()
                    + " 名字 = " + Thread.currentThread().getName() + " 正在等待客户端连接...");
            // 如果客户端一直未连接,服务端会阻塞在这里
            final Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端");
            // 创建一个线程,与之通讯
            cachedThreadPool.execute(new Runnable() {
                public void run() {
                    // 与客户端进行通讯
                    handler(socket);
                }
            });
        }
    }

    // 编写与客户端进行通讯的方法
    public static void handler(Socket socket) {
        try {
            byte[] bytes = new byte[1024];  // 字节流
            // 通过socket获取输入流
            InputStream inputStream = socket.getInputStream();
            // 循环读取客户发送的数据
            while (true) {
                System.out.println("线程信息id = " + Thread.currentThread().getId()
                        + " 名字 = " + Thread.currentThread().getName() + " read....");
                int read = inputStream.read(bytes);
                if (read != -1) {
                    // 输出客户端发送的数据
                    System.out.println(new String(bytes, 0, read));
                } else {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            System.out.println("关闭Client的连接");
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 BIO(blocking IO)同步阻塞IO的缺点

  1. 每个请求都需要创建独立的线程,与对应的客户端进行数据 Read,业务处理,数据 Write
  2. 当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。
  3. 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费

2、Java NIO

package NIO;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class NIOClient {
    public static void main(String[] args) throws Exception {
        // 得到一个网络通道
        SocketChannel socketChannel = SocketChannel.open();
        // 设置为非阻塞
        socketChannel.configureBlocking(false);
        // 提供服务端的ip和端口号
        InetSocketAddress inetSocketAddress = new InetSocketAddress("10.180.58.11", 6666);
        // 客户端连接服务器
        if (!socketChannel.connect(inetSocketAddress)) {
            while (!socketChannel.finishConnect()) {
                System.out.println("因为连接需要时间,客户端不会阻塞,可以做其他工作.");
            }
        }
        // 若连接成功
        String string = "Hello world!";
        // wrap a byte array into a buffer
        ByteBuffer buffer = ByteBuffer.wrap(string.getBytes());
        // 发送数据,将buffer中的数据写到channel
        socketChannel.write(buffer);
        // 将代码停在此处
        System.in.read();
    }
}
package NIO;

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

public class NIOServer {
    public static void main(String[] args) throws Exception{
        // 创建ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 将serverSocketChannel绑定到端口,在服务器端进行监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        // 设置为非阻塞
        serverSocketChannel.configureBlocking(false);

        // 创建一个选择器
        Selector selector = Selector.open();
        // 将serverSocketChannel 注册到selector,关心事件为accept
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 注册到selector的key的数量是
        System.out.println("注册到selectionKey的数量为:" +
                selector.keys().size());

        // 等待客户端连接
        while (true) {
            // 等待1s,如果没有连接则返回
            if (selector.select(1000) == 0) {
                System.out.println("服务器等待了1s,客户端无连接");
                continue;
            }

            // 已有连接,获取selectionKey的集合(关注事件的集合)
            Set<SelectionKey> selectionKeySet = selector.selectedKeys();
            System.out.println("selectionKeys数量 = " + selectionKeySet.size());

            // 通过selectionKeys反向获取通道
            Iterator<SelectionKey> iterator = selectionKeySet.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                // 根据selectionKey 对应的通道发生的事件做相应的处理
                if (selectionKey.isAcceptable()) {  // OP_ACCEPT,存在客户端的连接
                    // 生成一个socketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    System.out.println("客户端连接成功,生成一个SocketChannel: " +
                            socketChannel.hashCode());

                    // 将socketChannel设置为非阻塞
                    socketChannel.configureBlocking(false);
                    // 将socketChannel注册
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));

                    System.out.println("客户端连接后,注册到selectionKey的数量为:" +
                            selector.keys().size());
                }

                if (selectionKey.isReadable()) {    // 发生OP_READ
                    // 反向获取channel
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    // 获取channel关联的buffer
                    ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
                    // 读取socketChannel中的数据 写到buffer
                    socketChannel.read(buffer);
                    System.out.println("来自客户端 " + new String(buffer.array()));
                }
                // 事件处理后,手动从集合中移动当前的selectionKey,防止重复操作
                iterator.remove();
            }
        }
    }
}

 

3、netty的群聊系统

 

 

4、netty的心脏检测机制

 

posted @ 2021-12-23 21:03  Peterxiazhen  阅读(32)  评论(0编辑  收藏  举报