NIO 엔트리 스몰 케이스 (서버와 클라이언트 간의 간단한 데이터 통신을 위해)

              NIO (JDK1.4) 모델은 채널, 버퍼 및 선택기의 세 가지 핵심 부분이있는 동기식 비 차단 IO입니다. 전통적인 IO는 바이트 스트림과 문자 스트림을 기반으로 작동하는 반면 NIO는 채널 및 버퍼 (버퍼)에서 작동합니다. 데이터는 항상 채널에서 버퍼로 읽거나 버퍼에서 채널로 기록됩니다. 선택기 (멀티플렉서)는 여러 채널의 이벤트를 모니터링하는 데 사용됩니다 (예 : 연결 열기, 데이터 도착). 따라서 단일 스레드가 여러 데이터 채널을 모니터링 할 수 있습니다.
              NIO와 기존 IO (이후 IO라고 함)의 첫 번째 가장 큰 차이점은 IO가 스트림 지향이고 NIO가 버퍼 지향이라는 것입니다. Java IO 스트림 지향은 스트림에서 하나 이상의 바이트를 읽을 때마다 모든 바이트를 읽을 때까지 어디에도 캐시되지 않음을 의미합니다. 또한 스트림의 데이터를 앞뒤로 이동할 수 없습니다. 스트림에서 읽은 데이터를 앞뒤로 이동해야하는 경우 먼저 버퍼에 캐시해야합니다.
              다양한 IO 스트림이 차단됩니다. 이는 스레드가 read () 또는 write ()를 호출 할 때 일부 데이터를 읽거나 데이터가 완전히 기록 될 때까지 스레드가 차단됨을 의미합니다. 스레드는이 기간 동안 더 이상 아무것도 할 수 없습니다. NIO의 non-blocking 모드는 쓰레드가 채널에서 데이터 읽기 요청을 보낼 수 있도록하지만 현재 사용 가능한 데이터 만 가져올 수 있으며 현재 사용 가능한 데이터가 없으면 아무것도 얻지 못합니다. 스레드를 차단 된 상태로 유지하는 대신 스레드는 데이터를 읽을 수있을 때까지 다른 작업을 계속할 수 있습니다. 비 차단 쓰기도 마찬가지입니다. 스레드는 일부 데이터를 특정 채널에 쓰도록 요청하지만 완전히 기록 될 때까지 기다릴 필요가 없습니다.이 스레드는 동시에 다른 작업을 수행 할 수 있습니다. 스레드는 일반적으로 비 차단 IO의 유휴 시간을 사용하여 다른 채널에서 IO 작업을 수행하므로 이제 단일 스레드가 여러 입력 및 출력 채널 (채널)을 관리 할 수 ​​있습니다.

NIO 장점 :

  1. 클라이언트와 서버 간의 일종의 통신은 채널이 Selector에 등록한 상태를 통해 실현됩니다.
  2. Channel의 데이터는 non-blocking 읽기 방식 인 Buffer를 통해 읽습니다.
  3. 선택기 멀티플렉서 단일 스레드 모델, 스레드 리소스 오버 헤드가 상대적으로 적습니다.

 

서버 코드 : NIOServer.java 

import java.net.InetSocketAddress;
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;

public class NIOServer {
    public static void main(String[] args) throws Exception{
        //创建一个serverSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //创建Selector
        Selector selector = Selector.open();

        //绑定端口6666,在服务端监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));

        //设置非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //将channel注册到selector中,关心事件为op_ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //循环监听客户端连接
        while (true){
            //selector等待1s钟,如果没有事件发生则可以去做别的事情
            if(selector.select(1000)==0){
                System.out.println("服务端等待了1s,没有事件发生");
                continue;
            }

            //如果>0,则得到selectionKeys集合,已经获取到关注的事件了,selectionKeys是关注事件的集合
            //通过selectionKeys反向获取通道
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            //遍历selectionKeys集合,使用迭代器
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            while (iterator.hasNext()){
                SelectionKey key = iterator.next();

                //根据key对应的通道发生的事件做相应的处理
                //相当于有客户端连接,给该客户端生成一个socketchannel
                if(key.isAcceptable()){
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //设置为非阻塞
                    socketChannel.configureBlocking(false);
                    System.out.println("客户端连接成功,生成了一个socketchannel"+socketChannel.hashCode());
                    //将sockerchannel注册到selector,关注事件为read,同行关联一个buffer
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                if(key.isReadable()){
                    //通过key反向获取socketChannel
                    SocketChannel channel = (SocketChannel)key.channel();
                    //获取到该channel关联的buffer
                    ByteBuffer buffer=(ByteBuffer)key.attachment();
                    channel.read(buffer);
                    System.out.println("客户端"+new String(buffer.array()));
                }
                //手动从集合中移除当前的selectionkey,防止重复操作
                iterator.remove();
            }
        }
    }
}

 

두 개의 클라이언트 코드

NIOClient.java

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("127.0.0.1", 6666);
        //连接服务器
        if(!socketChannel.connect(inetSocketAddress)){
            while (!socketChannel.finishConnect()){
                System.out.println("因为连接需要时间,客户端没有阻塞,可以做其他工作。。");
            }
        }

        //如果连接成功,发送数据
        String str="客户端ZTY已连接";
        //直接将字符串对应的字节数组包裹到buffer中,不用指定大小
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
        //发送数据,将buffer写入到channel
        socketChannel.write(buffer);
        System.in.read();
    }
}

NIOClient2.java

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

public class NIOClient2 {
    public static void main(String[] args) throws Exception{
        //得到一个网络通道
        SocketChannel socketChannel = SocketChannel.open();
        //设置非阻塞
        socketChannel.configureBlocking(false);
        //提供服务器端的IP和端口
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);
        //连接服务器
        if(!socketChannel.connect(inetSocketAddress)){
            while (!socketChannel.finishConnect()){
                System.out.println("因为连接需要时间,客户端没有阻塞,可以做其他工作。。");
            }
        }

        //如果连接成功,发送数据
        String str="客户端LPJ已连接";
        //直接将字符串对应的字节数组包裹到buffer中,不用指定大小
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
        //发送数据,将buffer写入到channel
        socketChannel.write(buffer);
        System.in.read();
    }
}

먼저 서버를 실행하십시오. 아래 스크린 샷 :

클라이언트 1 (NIOClient.java)을 실행 한 후 서버 인터페이스는 출력을 생성합니다.

 클라이언트 2 (NIOClient2.java)를 실행 한 후 서버 인터페이스는 출력을 생성합니다.

 

NIO를 마친 후 Netty는 NIO를 기반으로하기 때문에 Netty를 마스터하는 것과 멀지 않습니다. 

추천

출처blog.csdn.net/Zhongtongyi/article/details/107348605