关于Bio与Nio


  程序git:https://gitee.com/juncaoit/netty

  IO模型 IO模型就是说用什么样的通道进行数据的发送和接收,Java共支持3种网络编程IO模式:BIO,NIO,AIO

一:Bio

1.模型

  关于Bio与Nio

 

 

2.服务端

package com.jun.bio;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

/**
 * BIO的服务端
 */
public class SocketServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9000);
        while (true) {
            // 如果有链接,则进行处理
            Socket clientSocket = serverSocket.accept();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        handler(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }).start();
        }
    }

    /**
     * 数据处理
     */
    private static void handler(Socket clientSocket) throws IOException {
        byte[] bytes = new byte[1024];
        int read = clientSocket.getInputStream().read(bytes);
        if (read != -1) {
            System.out.println("接收到的数据是" + new String(bytes, 0, read));
        }
        // 返回客户端一些数据
        clientSocket.getOutputStream().write("收到你的数据了".getBytes(StandardCharsets.UTF_8));
        clientSocket.getOutputStream().flush();
    }
}

 

3.客户端

package com.jun.bio;

import java.io.IOException;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

/**
 * BIO的客户端
 */
public class SocketClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 9000);
        // 发送数据
        socket.getOutputStream().write("hello 服务端".getBytes(StandardCharsets.UTF_8));
        socket.getOutputStream().flush();
        // 接收返回值
        byte[] bytes = new byte[1024];
        int read = socket.getInputStream().read(bytes);
        if (read != -1) {
            System.out.println("接收到服务端的返回值是" + new String(bytes, 0, read));
        }
        socket.close();
    }
}

 

4.缺点

  1、IO代码里read操作是阻塞操作,如果连接不做数据读写操作会导致线程阻塞,浪费资源
  2、如果线程很多,会导致服务器线程太多,压力太大,比如C10K问题
  应用场景:
  BIO 方式适用于连接数目比较小且固定的架构, 这种方式对服务器资源要求比较高, 但程序简单易理解。

 

二:NIO

1.场景 

  同步非阻塞,服务器实现模式为一个线程可以处理多个请求(连接),客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有IO请求就进行处理,JDK1.4开始引入。
  应用场景:
  NIO方式适用于连接数目多且连接比较短(轻操作) 的架构, 比如聊天服务器, 弹幕系统, 服务器间通讯,编程比较复杂

 

2.无selector的程序

package com.jun.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 无select的NIO
 */
public class NoSelectServer {
    // 存客户端链接
    private static List<SocketChannel> clientChannelList = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(9000));
        serverSocketChannel.configureBlocking(false);

        while (true) {
            // 如果存在新的链接,则添加到链接队列中
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel != null) {
                System.out.println("链接成功");
                socketChannel.configureBlocking(false);
                clientChannelList.add(socketChannel);
            }
            // 循环队列,读取数据
            Iterator<SocketChannel> iterator = clientChannelList.iterator();
            while (iterator.hasNext()) {
                SocketChannel nextChannel = iterator.next();
                ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                int len = nextChannel.read(byteBuffer);
                if (len > 0) {
                    System.out.println("接收到的数据是" + new String(byteBuffer.array()));
                } else if (len == -1) {
                    iterator.remove();
                    System.out.println("客户断开了链接");
                }
            }
        }
    }
}

 

3.使用selector

  关于Bio与Nio

 

 

package com.jun.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 正式的NIO
 */
public class HasSelectorServer {
    public static void main(String[] args) throws IOException {
        // 定义多路复用器
        Selector selector = Selector.open();
        // 服务端链接
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(9000));
        serverSocketChannel.configureBlocking(false);
        // 将服务端通道注册到selector上
        SelectionKey register = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 开始循环是否有
        while (true) {
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey nextKey = iterator.next();
                // 如果是OP_ACCEPT事件,则进行连接获取和事件注册
                if (nextKey.isAcceptable()) {
                    ServerSocketChannel server = (ServerSocketChannel) nextKey.channel();
                    SocketChannel socketChannel = server.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("客户端连接成功");
                } else if (nextKey.isReadable()) {
                    SocketChannel client = (SocketChannel) nextKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(128);
                    int len = client.read(byteBuffer);
                    if (len > 0) {
                        System.out.println("接收到的数据是" + new String(byteBuffer.array()));
                    } else if (len == -1) {
                        client.close();
                        System.out.println("客户断开了链接");
                    }
                }
                iterator.remove();
            }
        }
    }
}

 

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/289642.html

(0)
上一篇 2022年9月15日
下一篇 2022年9月15日

相关推荐

发表回复

登录后才能评论