java nio 的读写操作代码示例

来源:互联网 发布:炒美股用什么软件 编辑:程序博客网 时间:2024/05/21 10:03
package cn.ac.yangge.service;import cn.ac.yangge.unity.AnalysisData;import cn.ac.yangge.unity.ByteBufferPrint;import org.springframework.stereotype.Service;import java.io.*;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.net.Socket;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.*;import java.util.concurrent.Callable;import java.util.concurrent.Executors;import java.util.concurrent.ThreadPoolExecutor;/** * Created by yangge on 2017/5/26 0026. */@Service("tcpService")public class TcpService {    // 主入口    public static void main() throws IOException {        sockectChannelSever();    }    //使用serversocketchannel 非程阻塞    public static void sockectChannelSever() {        int port = 8888;//端口        ServerSocketChannel serverSocketChannel;        Selector selector;        try {            serverSocketChannel = ServerSocketChannel.open();            ServerSocket ss = serverSocketChannel.socket();            InetSocketAddress address = new InetSocketAddress(port);            ss.bind(address);            serverSocketChannel.configureBlocking(false);            selector = Selector.open();            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);        } catch (IOException e) {            e.printStackTrace();            return;        }        while (true) {            try {                selector.select();            } catch (IOException e) {                e.printStackTrace();                break;            }            Set<SelectionKey> readyKeys = selector.selectedKeys();            Iterator<SelectionKey> iterator = readyKeys.iterator();            while (iterator.hasNext()) {                SelectionKey key = iterator.next();                iterator.remove();                try {                    if (key.isAcceptable()) {                        ServerSocketChannel server = (ServerSocketChannel) key.channel();                        SocketChannel client = server.accept();                        System.out.println("Accepted connection from " + client);                        client.configureBlocking(false);                        SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);                        ByteBuffer buffer = ByteBuffer.allocate(100);                        clientKey.attach(buffer);                    }                    if (key.isReadable()) {                        SocketChannel client = (SocketChannel) key.channel();                        ByteBuffer output = (ByteBuffer) key.attachment();                        client.read(output);                        output.flip();                        byte[] byteArray=output.array();                        for(byte b:byteArray){                            System.out.println(b);                        }                        System.out.println(ByteBufferPrint.getString(output));                    }                } catch (IOException ex) {                    key.cancel();                    try {                        key.channel().close();                    } catch (IOException e) {                    }                }            }        }    }}

Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道。可以通过以下2种方式创建SocketChannel:

  1. 打开一个SocketChannel并连接到互联网上的某台服务器。
  2. 一个新连接到达ServerSocketChannel时,会创建一个SocketChannel。

打开 SocketChannel

下面是SocketChannel的打开方式:

1SocketChannel socketChannel = SocketChannel.open();
2socketChannel.connect(new InetSocketAddress("http://jenkov.com"80));

关闭 SocketChannel

当用完SocketChannel之后调用SocketChannel.close()关闭SocketChannel:

1socketChannel.close();

从 SocketChannel 读取数据

要从SocketChannel中读取数据,调用一个read()的方法之一。以下是例子:

1ByteBuffer buf = ByteBuffer.allocate(48);
2int bytesRead = socketChannel.read(buf);

首先,分配一个Buffer。从SocketChannel读取到的数据将会放到这个Buffer中。

然后,调用SocketChannel.read()。该方法将数据从SocketChannel 读到Buffer中。read()方法返回的int值表示读了多少字节进Buffer里。如果返回的是-1,表示已经读到了流的末尾(连接关闭了)。

写入 SocketChannel

写数据到SocketChannel用的是SocketChannel.write()方法,该方法以一个Buffer作为参数。示例如下:

01String newData = "New String to write to file..." + System.currentTimeMillis();
02 
03ByteBuffer buf = ByteBuffer.allocate(48);
04buf.clear();
05buf.put(newData.getBytes());
06 
07buf.flip();
08 
09while(buf.hasRemaining()) {
10    channel.write(buf);
11}

注意SocketChannel.write()方法的调用是在一个while循环中的。Write()方法无法保证能写多少字节到SocketChannel。所以,我们重复调用write()直到Buffer没有要写的字节为止。

非阻塞模式

可以设置 SocketChannel 为非阻塞模式(non-blocking mode).设置之后,就可以在异步模式下调用connect(), read() 和write()了。

connect()

如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()的方法。像这样:

1socketChannel.configureBlocking(false);
2socketChannel.connect(new InetSocketAddress("http://jenkov.com"80));
3 
4while(! socketChannel.finishConnect() ){
5    //wait, or do something else...
6}

write()

非阻塞模式下,write()方法在尚未写出任何内容时可能就返回了。所以需要在循环中调用write()。前面已经有例子了,这里就不赘述了。

read()

非阻塞模式下,read()方法在尚未读取到任何数据时可能就返回了。所以需要关注它的int返回值,它会告诉你读取了多少字节。

调试的时候想要转化byteBuffer?

提供给你两个方法

String 转换 ByteBuffer:

  

public static ByteBuffer getByteBuffer(String str) {

return ByteBuffer.wrap(str.getBytes());

}


ByteBuffer 转换 String:

public static String getString(ByteBuffer buffer) {

Charset charset = null;

CharsetDecoder decoder = null;

CharBuffer charBuffer = null;

try {

charset = Charset.forName("UTF-8");

decoder = charset.newDecoder();

                       //用这个的话,只能输出来一次结果,第二次显示为空

// charBuffer = decoder.decode(buffer);

charBuffer = decoder.decode(buffer.asReadOnlyBuffer());

return charBuffer.toString();

} catch (Exception ex) {

ex.printStackTrace();

return "error";

}

}


想要直接转化为byte[] ?

可以直接使用byteBuffer.array();


遇到的坑:

SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);
在配置selectionKey的时候,之前是用了Selection.OP_WRITE的,这导致keys的事件集合中一直存在一个isWritable事件,原因是:
key.isWritable()是表示Socket可写,网络不出现阻塞情况下,一直是可以写的,所认一直为true.一般我们不注册OP_WRITE事件.


原创粉丝点击