Nio Socket

Posted code2038

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nio Socket相关的知识,希望对你有一定的参考价值。

Niosocket

Buffer 要传输的数据,like货物

Channel  传输通道,like快递配送中心

Selector  选择器,like挑拣员

configureBlocking(false)设置非阻塞模式,之后才能调用register方法注册Selector到ServerSocketChannel或SocketChannel

Selector可以通过工厂方法open创建,可以通过它的select方法来等待请求(其long类型参数代表最长等待时间,如参数为0或调用无参的select()重载方法会采用阻塞模式)

Selector调用selectedKeys方法返回SelectionKey集合(保存当前请求的Channel和Selector,并提供四种操作类型,可通过register的第二个参数选择特定的类型)

OP_ACCEPT   接受请求操作

OP_CONNECT  连接操作

OP_READ   读操作

OP_WRITE  写操作

NioSocket中服务端的处理过程可以分5步:

1)创建ServerSocketChannel并设置相应参数

2)创建Selector并注册到ServerSocketChannel

3)调用Selector的select方法等待请求

4)Selector接收到请求后使用selectedKeys返回SelectionKey集合

5)使用SelectionKey获取Channel、Selector和操作类型并进行具体操作

 

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.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.nio.charset.Charset;

import java.util.Iterator;



public class NIOServer {

    public static void main(String[] args) throws Exception{

        //创建ServerSocketChannel,监听8080端口

        ServerSocketChannel ssc=ServerSocketChannel.open();

        ssc.socket().bind(new InetSocketAddress(8080));

        //设置为非阻塞模式

        ssc.configureBlocking(false);

        //为ssc注册选择器

        Selector selector=Selector.open();

        ssc.register(selector, SelectionKey.OP_ACCEPT);

        //创建处理器

        Handler handler = new Handler(1024);

        while(true){

            // 等待请求,每次等待阻塞3s,超过3s后线程继续向下运行,如果传入0或者不传参数将一

            // 直阻塞

            if(selector.select(3000)==0){

                System.out.println("等待请求超时……");

                continue;

            }

            System.out.println("处理请求……");

            // 获取待处理的SelectionKey

            Iterator<SelectionKey> keyIter=selector.selectedKeys().iterator();



            while(keyIter.hasNext()){

                SelectionKey key=keyIter.next();

                try{

                    // 接收到连接请求时

                    if(key.isAcceptable()){

                        handler.handleAccept(key);

                    }

                    // 读数据

                    if(key.isReadable()){

                        handler.handleRead(key);

                    }

                } catch(IOException ex) {

                    keyIter.remove();

                    continue;

                }

                // 处理完后,从待处理的SelectionKey迭代器中移除当前所使用的key

                keyIter.remove();

            }

        }

    }
private static class Handler {

        private int bufferSize = 1024;

        private String  localCharset = "UTF-8";

        public Handler(){}

        public Handler(int bufferSize){

            this(bufferSize, null);

        }

        public Handler(String  LocalCharset){

            this(-1, LocalCharset);

        }

        public Handler(int bufferSize, String  localCharset){

            if(bufferSize>0)

                this.bufferSize=bufferSize;

            if(localCharset!=null)

                this.localCharset=localCharset;

        }

        public void handleAccept(SelectionKey key) throws IOException {

            SocketChannel sc=((ServerSocketChannel)key.channel()).accept();

            sc.configureBlocking(false);

            sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate (bufferSize));

        }



        public void handleRead(SelectionKey key) throws IOException {

            // 获取channel

            SocketChannel sc=(SocketChannel)key.channel();

            // 获取buffer并重置

            ByteBuffer buffer=(ByteBuffer)key.attachment();

            buffer.clear();

            // 没有读到内容则关闭

            if(sc.read(buffer)==-1){

                sc.close();

            } else {

                // 将buffer转换为读状态

                buffer.flip();

                // 将buffer中接收到的值按localCharset格式编码后保存到receivedString

                String receivedString = Charset.forName(localCharset).newDecoder(). decode(buffer).toString();

                System.out.println("received from client: " + receivedString);



                // 返回数据给客户端

                String sendString = "received data: " + receivedString;

                buffer = ByteBuffer.wrap(sendString.getBytes(localCharset));

                sc.write(buffer);

                // 关闭Socket

                sc.close();

            }

        }

    }

}

 

  

 

java.nio.Buffer  专门用于存储数据,其中的四个属性:capacity(容量,Buffer最多可以保存的元素数量,在创建时设置,使用过程中不可改变)limit(可以使用的上限,不可以超过capacity)position(当前操作元素所在的索引位置,从0开始,随着get和put方法自动更新)mark(用来暂时保存position的值,position修改后可以通过reset方法将mark值恢复到position;默认值-1,其值必须小于position值)

mark<=position<=limit<=capacity

clear()重新初始化limit=capacity、position=0、mark=-1三个属性

 

 1 public final Buffer clear() {
 2 
 3     position = 0;
 4 
 5     limit = capacity;
 6 
 7     mark = -1;
 8 
 9     return this;
10 
11 }
12 
13 public final Buffer flip() {
14 
15     limit = position;
16 
17     position = 0;
18 
19     mark = -1;
20 
21     return this;
22 
23 }

 

 

 实际开发多使用多线程处理

 

以上是关于Nio Socket的主要内容,如果未能解决你的问题,请参考以下文章

170407java基于nio工作方式的socket通信

NIO学习之ServerSocketChannel和SocketChannel

NIO Channel Socket

Java NIO4:Socket通道

Java NIO4:Socket通道

Java--NIO-TCP Socket