学习 java netty (一) -- java nio

前言:近期在研究java netty这个网络框架,第一篇先介绍java的nio。


java nio在jdk1.4引入,事实上也算比較早的了。主要引入非堵塞io和io多路复用。内部基于reactor模式。


nio核心:
- buffer
- channel
- selector

buffer:
相似网络编程中的缓冲区,有
ByteBuffer 字节
CharBuffer 字符
IntBuffer
DoubleBuffer…
经常使用的有ByteBuffer和CharBuffer

java nio buffer是开辟了一块空间。内部有4个标记,分别为position,limit。capicity。mask。
这里写图片描写叙述
position是当前读缓冲区位置,limit是数据的结尾。表示最多能读到limit位置,capacity是缓冲区的容量。在position和limit之间还会有mask用来标记一个位置,能够移动position到达mask位置
buffer有两个重要的方法clear和flip
clear()方法没有清空数据而是将position置为0,limit置为capacity,
为再次向buffer装数据做好准备
flip()方法在装载完数据后。将limit设为当前position位置,然后将position设置为0,简单来说就是flip()为从buffer中取出数据做好准备。


>

channel:
相似网络编程中的套接字,Java nio中称为Channel(通道),个人觉得这样更加方便我们理解,服务器和客户端之间通过Channel来数据传输。
channel 有
FileChannel 文件管道
DatagramChannel 数据报管道
SocketChannel socket管道
ServerSocketChannel 服务端socket管道
channel能够直接文件的部分或所有映射为buffer
注意:
程序不能直接訪问channel的数据。channel必须和buffer结合起来使用。
所有的channel都不应该用构造器,从传统的流节点Inputstream 等来获取channel


selector:
事件分发收集器,内部封装的epoll。我们能够往上面注冊事件,然后监听多个channel通道上的事件等。


时序图
nio时序图
注:图片非原创

从时序图能够看出java nio和我们平时写的网络编程非常像。


java nio实现简单的回射服务器

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.util.Set;

/**
 * Created by wwh on 15-7-25.
 */
public class NioSocket {
    //字符序列和字节序列的编码和解码
    private Charset charset = Charset.forName("UTF-8");

    void run(String ip, int port) throws IOException {
        try {
            //创建服务端套接字
            ServerSocketChannel server = ServerSocketChannel.open();
            //绑定ip和端口
            server.socket().bind(new InetSocketAddress(ip, port));
            //设置非堵塞
            server.configureBlocking(false);
            //创建selector事件选择器
            Selector selector = Selector.open();
            //将自己的监听套接字注冊到selector上。监听 accept事件
            //SelectionKey代表SelectableChannel和Selector的关系。Selectable是Selector可监听的事件channel.
            server.register(selector, SelectionKey.OP_ACCEPT);
            while(selector.select() > 0){
                //selector.select()返回事件
                for(SelectionKey sk : selector.selectedKeys()) {
                    //从事件集合中删除正要处理的事件
                    selector.selectedKeys().remove(sk);
                    //推断事件的类型,依次处理
                    if(sk.isAcceptable()){
                        //假设事件为接受连接accpet事件
                        System.out.println("accpet 事件");
                        //调用accept接受请求连接
                        SocketChannel client = server.accept();
                        //设置为非堵塞
                        client.configureBlocking(false);
                        //向selector上注冊读事件
                        client.register(selector, SelectionKey.OP_READ);
                        //将sk相应的channel设置为准备接受其它请求
                        sk.interestOps(SelectionKey.OP_ACCEPT);
                    }
                    if(sk.isReadable()){
                        //假设事件为可读事件
                        System.out.println("read 事件");
                        SocketChannel client = (SocketChannel)sk.channel();
                        //定义缓冲区
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        String mesg = "";
                        try {
                            while (client.read(buffer) > 0) {
                                buffer.flip();
                                mesg += charset.decode(buffer);
                            }
                            System.out.println("收到:" + mesg);
                            sk.interestOps(SelectionKey.OP_READ);
                        }catch (IOException e){
                            //假设出现异常,则取消当前的client连接
                            sk.cancel();
                            if(sk.channel() != null){
                                sk.channel().close();
                            }
                        }
                        //回复给发来消息的client
                        client.write(charset.encode(mesg));
                        System.out.println("回复:" + mesg);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        NioSocket Server = new NioSocket();
        Server.run("192.168.10.75", 10000);
    }
}
原文地址:https://www.cnblogs.com/jzssuanfa/p/7136211.html