JavaNIO

NIO基础:

三大核心组件:

Buffer(缓冲):

Buffer是一个缓冲区的数据对象。
任何时候访问NIO中的数据,都需要通过缓冲区(Buffer)操作。
包含一些要写入或者读出的数据。读写操作都是基于缓冲区操作的。
每一个Java基本类型都对应着一种Buffer。
这里写图片描述

Channel(通道):

Channel和流类似。但又有区别。

  1. Channel支持异步操作。而流不支持。
  2. Channel是多向的,而流是单向的。
  3. Channel必须结合Buffer使用,而流不需要用Buffer也可以操作。
  4. Channel性能较高,而流相对于Channel性能是较低的。

简单来说,Channel是数据的源头或者数据的目的地,
用于向buffer提供数据或者读取数据,并且对I/O提供异步支持。
这里写图片描述
Channel有很多中实现类和方法。而顶级接口Channel中都有这两种方法:

01.isopen():判断此通道是否处于打开状态。
02.close():关闭此通道

Channel中重要的实现类:

01.FileChannel:用来读、写、映射和操作文件的类。
02.DatagramChannel:能通过UDP读写网络数据。
03.SocketChannel:能通过TCP读写网络数据。相当于客户端。
04.ServerSocketChannel:监听新进来的TCP连接。每一个新的连接器都会创建一个SocketChannel。相当于服务器角色。

这里写图片描述
Selector(选择):

Selector又称为多路复用器。
多路复用器它是NIO编程的基础,它提供了选择已经就绪的任务的能力。
从底层来看,Selector提供了询问通道是否已经准备好执行每个I/O操作的能力,
简单说:Selector会不断的轮询注册在它上面的Channel,如果某个Channel发生了读写操作,
这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey获得就绪Channel的集合,来镜子那个后续的I/O操作。
Selector允许一个线程处理多个Channel,也就是说,一个县城复杂的seletcor轮询,就可以处理成千上万个Channel,相比于多线程处理,势必会减少线程的上下文切换带来的各种问题。

这里写图片描述

以下编程实现:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
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 {
    //接收数据缓冲流
    private ByteBuffer sendbuffer=ByteBuffer.allocate(1024);
    //发送数据缓冲流
    private ByteBuffer receivebuffer=ByteBuffer.allocate(1024);
    //定义多路复用器
    private Selector selector;

    public NIOServer(int port)throws IOException{
        //打开服务器套接字通道
        ServerSocketChannel channel=ServerSocketChannel.open();
        //服务器配置为非阻塞
        channel.configureBlocking(false);
        //检索与此通道关联的服务器套接字
        ServerSocket serverSocket=channel.socket();
        //进行服务的绑定
        serverSocket.bind(new InetSocketAddress(port));
        //通过open方法找到Selector
        selector = Selector.open();
        //注册到selector,等待连接
        channel.register(selector, SelectionKey.OP_ACCEPT);
    }


    private void listen() throws IOException{
        while(true){
            selector.select();
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                iterator.remove();
                handleKey(selectionKey);
            }
        }
    }

    private void handleKey(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel server=null;
        SocketChannel client=null;
        String receiveText;
        String sendText;
        int count=0;

        //测试遍历出的通道是否已经准备好接收新的套接字连接
        if(selectionKey.isAcceptable()){
            //返回为此通道创建的通道
            server=(ServerSocketChannel) selectionKey.channel();
            //接收此通道的套接字连接,但是接收的套接字通道默认为阻塞模式
            client=server.accept();
            //配置为非阻塞
            client.configureBlocking(false);
            //注册到selector,等待连接。
            client.register(selector, SelectionKey.OP_READ);
        }else if(selectionKey.isReadable()){
            //如果遍历出的通道处于可读状态。
            //返回为此创建的通道
            client = (SocketChannel) selectionKey.channel();
            //将缓冲区清空以备下次读取
            receivebuffer.clear();
            //读取服务器发送过来的数据到缓冲区中。
            count=client.read(receivebuffer);
            if(count>0){
                receiveText=new String(receivebuffer.array(), 0, count);
                System.out.println("服务器端接受客户端数据-->"+receiveText);
                client.register(selector, SelectionKey.OP_WRITE);
            }
        }else if(selectionKey.isWritable()){
            //如果遍历出的通道处于可写入状态
            //先将缓冲区清空以备下次写入
            sendbuffer.clear();
            //返回为此创建的通道
            client=(SocketChannel) selectionKey.channel();
            sendText="message from server-->";
            //向缓冲区中输入数据
            sendbuffer.put(sendText.getBytes());
            //将缓冲区各标志复位,因为向里面put了数据标志被改变。
            //想要从中读取数据发向服务器,就要复位。
            sendbuffer.flip();
            //输出到通道
            client.write(sendbuffer);
            System.out.println("服务器端向客户端发送数据-->"+sendText);
        }
    }
    public static void main(String[] args) throws IOException {
        int port=8989;
        NIOServer server=new NIOServer(port);   
        server.listen();
    }
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOClient {
    //接受数据缓冲区
    private static ByteBuffer sendbuffer=ByteBuffer.allocate(1024);
    //发送数据缓冲区
    private static ByteBuffer receivebuffer=ByteBuffer.allocate(1024);

    public static void main(String[] args) throws IOException {
        //打开socket通道
        SocketChannel socketChannel=SocketChannel.open();
        //设置为非阻塞方式
        socketChannel.configureBlocking(false);
        //打开选择器
        Selector selector=Selector.open();
        //注册连接服务器socket动作
        socketChannel.register(selector, SelectionKey.OP_CONNECT);
        //连接
        socketChannel.connect(new InetSocketAddress("127.0.0.1",8989));

        Set<SelectionKey>selectionKeys;
        Iterator<SelectionKey>iterator;
        SelectionKey selectionKey;
        SocketChannel client;
        String reviceText;
        String sendText;
        int count=0;
        while(true){
            //选择一组键,其相应的通道已经为I/O操作准备就绪
            //此方法执行处于阻塞模式的选择操作。
            selector.select();
            //返回次选择器的已选择键集。
            selectionKeys = selector.selectedKeys();
            iterator=selectionKeys.iterator();
            while(iterator.hasNext()){
                selectionKey=iterator.next();
                if(selectionKey.isConnectable()){
                    System.out.println("client connect");
                    client=(SocketChannel) selectionKey.channel();
                    //判断此通道上是否正在进行连接操作
                    //完成套接字通道的连接过程
                    if(client.isConnectionPending()){
                        client.finishConnect();
                        System.out.println("完成连接");
                        sendbuffer.clear();
                        sendbuffer.put("Hello,Server".getBytes());
                        sendbuffer.flip();
                        client.write(sendbuffer);
                    }
                    client.register(selector, SelectionKey.OP_READ);
                }else if(selectionKey.isReadable()){
                    client=(SocketChannel) selectionKey.channel();
                    //将缓冲区清空以备下次读取
                    receivebuffer.clear();
                    //读取服务器发送过来的数据到缓冲区中
                    count = client.read(receivebuffer);
                    if(count>0){
                        reviceText=new String(receivebuffer.array(), 0,count);
                        System.out.println("客户端接受服务器短的数据-->"+reviceText);
                        client.register(selector, SelectionKey.OP_WRITE);
                    }
                }else if(selectionKey.isWritable()){
                    sendbuffer.clear();
                    client=(SocketChannel) selectionKey.channel();
                    sendText="message from client-->";
                    sendbuffer.put(sendText.getBytes());
                    sendbuffer.flip();
                    client.write(sendbuffer);
                    System.out.println("客户端向服务器发送数据-->"+sendText);
                    client.register(selector, SelectionKey.OP_READ);
                }
            }
            selectionKeys.clear();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值