java nio 实例_JAVA NIO 实例

最近一直在忙着JAVA NIO的知识,花了一下午的时间,总算写出了一个可以运行的程序,废话少说,上代码!

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

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 {

/*标识数字*/

private int flag = 0;

/*缓冲区大小*/

private int BLOCK = 4096;

/*接受数据缓冲区*/

private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);

/*发送数据缓冲区*/

private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);

private Selector selector;

public NIOServer(int port) throws IOException {

// 打开服务器套接字通道

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

// 服务器配置为非阻塞

serverSocketChannel.configureBlocking(false);

// 检索与此通道关联的服务器套接字

ServerSocket serverSocket = serverSocketChannel.socket();

// 进行服务的绑定

serverSocket.bind(new InetSocketAddress(port));

// 通过open()方法找到Selector

selector = Selector.open();

// 注册到selector,等待连接

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

System.out.println("Server Start----8888:");

}

// 监听

private void listen() throws IOException {

while (true) {

// 选择一组键,并且相应的通道已经打开

selector.select();

// 返回此选择器的已选择键集。

Set selectionKeys = selector.selectedKeys();

Iterator iterator = selectionKeys.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--" + flag++;

//向缓冲区中输入数据

sendbuffer.put(sendText.getBytes());

//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位

sendbuffer.flip();

//输出到通道

client.write(sendbuffer);

System.out.println("服务器端向客户端发送数据--:"+sendText);

client.register(selector, SelectionKey.OP_READ);

}

}

/**

* @param args

* @throws IOException

*/

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

// TODO Auto-generated method stub

int port = 8888;

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 int flag = 0;

/*缓冲区大小*/

private static int BLOCK = 4096;

/*接受数据缓冲区*/

private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);

/*发送数据缓冲区*/

private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);

/*服务器端地址*/

private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(

"localhost", 1111);

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

// TODO Auto-generated method stub

// 打开socket通道

SocketChannel socketChannel = SocketChannel.open();

// 设置为非阻塞方式

socketChannel.configureBlocking(false);

// 打开选择器

Selector selector = Selector.open();

// 注册连接服务端socket动作

socketChannel.register(selector, SelectionKey.OP_CONNECT);

// 连接

socketChannel.connect(SERVER_ADDRESS);

// 分配缓冲区大小内存

Set selectionKeys;

Iterator iterator;

SelectionKey selectionKey;

SocketChannel client;

String receiveText;

String sendText;

int count=0;

while (true) {

//选择一组键,其相应的通道已为 I/O 操作准备就绪。

//此方法执行处于阻塞模式的选择操作。

selector.select();

//返回此选择器的已选择键集。

selectionKeys = selector.selectedKeys();

//System.out.println(selectionKeys.size());

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){

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 client--" + (flag++);

sendbuffer.put(sendText.getBytes());

//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位

sendbuffer.flip();

client.write(sendbuffer);

System.out.println("客户端向服务器端发送数据--:"+sendText);

client.register(selector, SelectionKey.OP_READ);

}

}

selectionKeys.clear();

}

}

}

个人感觉,JAVA NIO的操作时麻烦了不少,但是无疑这样做效率会得到很大的提升。

9

3

分享到:

18e900b8666ce6f233d25ec02f95ee59.png

72dd548719f0ace4d5f9bca64e1d7715.png

2010-09-18 03:32

浏览 45247

评论

22 楼

albert0707

2018-06-29

非常感谢!

21 楼

清风送月

2015-10-19

楼主:为什么我写的时候使用iterator.remove();  会报java.lang.UnsupportedOperationExceptionselectionKeys = selector.selectedKeys();

//System.out.println(selectionKeys.size());

iterator = selectionKeys.iterator();

20 楼

zaiyueming

2015-04-02

sj4268778 写道

都跑的通么??端口号都不一致。。

hekuilove 写道

端口不一样你是怎么跑起来的

这种小事何必纠结,自己不会改?

19 楼

hekuilove

2015-03-16

端口不一样你是怎么跑起来的

18 楼

sj4268778

2015-03-02

都跑的通么??端口号都不一致。。

17 楼

kennykinte

2014-06-18

xunke515 写道

-------------

NIOServer 类第101行有些问题

如果继续注册读事件,服务端会持续得进行循环.

这样客户端接受不到服务端返回得数据,会一直保持连接.

---------

应该将NIOServer 类第101行替换为client.close() ;

---------

欢迎拍砖.

兄台说的不太正确哦...

主贴代码就是让C/S两端不断循环读取,发送数据的, 只要客户端向服务端发送数据了,服务端就会回一条消息给客户端,如此反复.

如果将NIOServer 类第101行替换为client.close() ;

那么客户端就被关闭了.

16 楼

kennykinte

2014-06-18

softkf 写道

我也有点纳闷,如果把如下代码

else if (selectionKey.isWritable()) {

//将缓冲区清空以备下次写入

sendbuffer.clear();

// 返回为之创建此键的通道。

client = (SocketChannel) selectionKey.channel();

sendText="message from server--" + flag++;

//向缓冲区中输入数据

sendbuffer.put(sendText.getBytes());

//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位

sendbuffer.flip();

//输出到通道

client.write(sendbuffer);

System.out.println("服务器端向客户端发送数据--:"+sendText);

client.register(selector, SelectionKey.OP_READ);

}

中的红色部分去掉,就会出现服务器一直往客户端打印东西? 这是为何? 兄台弄清楚了么?

看我上一条回复, register方法会覆盖前一次register方法的效果. 所以如果你这边不加这条红色语句, 那么在这个channel里的事件就一直是OP_WRITE了,所以每次select()都会执行selectionKey.isWritable()这段的逻辑, 就一直给客户端发送数据了.

15 楼

kennykinte

2014-06-18

zk1878 写道

对nio不是很了解,这里有个疑问,在client那的代码

为什么当selectionKey.isConnectable()时,只注册了

SelectionKey.OP_READ,而不注册SelectionKey.OP_WRITE;

还有当selectionKey.isWritable()时,为什么每次都要注册

SelectionKey.OP_READ,我觉得应该是在selectionKey.isConnectable()

时同时注册OP_READ,OP_WRITE吧

对于某个指定的socketchannel,调用register方法会覆盖前一次调用register的效果,而你想在selectionKey.isConnectable()这里同时注册OP_READ和OP_WRITE事件的话, 可以这么写client.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);

但是主贴里selectKey循环遍历的时候, 又用的是if-else结构,所以你注册2个事件,也不会都执行到.

但是按照主贴里的写法,在isConnectable()里注册OP_READ,程序下次就会进入isReadable(),在isReadable()里又注册OP_WRITE, 这样就把OP_READ给覆盖掉了,程序下次就进入isWritable(),在isWritable()又注册了OP_READ,下一次就进入isReadable()....如此反复循环, 所以主贴代码是对的.

当然这种结构好不好, 我就不评论了.

14 楼

kennykinte

2014-06-17

josico 写道

问一下 为什么在遍历迭代器的时候 要做一次remove操作?

我觉得一点意义都没有啊

http://www.molotang.com/articles/906.html

这个帖子里有解释为什么要remove

13 楼

josico

2014-06-17

问一下 为什么在遍历迭代器的时候 要做一次remove操作?

我觉得一点意义都没有啊

12 楼

xunke515

2014-03-23

softkf 写道

zk1878 写道

对nio不是很了解,这里有个疑问,在client那的代码

为什么当selectionKey.isConnectable()时,只注册了

SelectionKey.OP_READ,而不注册SelectionKey.OP_WRITE;

还有当selectionKey.isWritable()时,为什么每次都要注册

SelectionKey.OP_READ,我觉得应该是在selectionKey.isConnectable()

时同时注册OP_READ,OP_WRITE吧

我也有点纳闷,如果把如下代码

else if (selectionKey.isWritable()) {

//将缓冲区清空以备下次写入

sendbuffer.clear();

// 返回为之创建此键的通道。

client = (SocketChannel) selectionKey.channel();

sendText="message from server--" + flag++;

//向缓冲区中输入数据

sendbuffer.put(sendText.getBytes());

//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位

sendbuffer.flip();

//输出到通道

client.write(sendbuffer);

System.out.println("服务器端向客户端发送数据--:"+sendText);

client.register(selector, SelectionKey.OP_READ);

}

中的红色部分去掉,就会出现服务器一直往客户端打印东西? 这是为何? 兄台弄清楚了么?

-------------

NIOServer 类第101行有些问题

如果继续注册读事件,服务端会持续得进行循环.

这样客户端接受不到服务端返回得数据,会一直保持连接.

---------

应该将NIOServer 类第101行替换为client.close() ;

---------

欢迎拍砖.

11 楼

softkf

2014-02-25

zk1878 写道

对nio不是很了解,这里有个疑问,在client那的代码

为什么当selectionKey.isConnectable()时,只注册了

SelectionKey.OP_READ,而不注册SelectionKey.OP_WRITE;

还有当selectionKey.isWritable()时,为什么每次都要注册

SelectionKey.OP_READ,我觉得应该是在selectionKey.isConnectable()

时同时注册OP_READ,OP_WRITE吧

我也有点纳闷,如果把如下代码

else if (selectionKey.isWritable()) {

//将缓冲区清空以备下次写入

sendbuffer.clear();

// 返回为之创建此键的通道。

client = (SocketChannel) selectionKey.channel();

sendText="message from server--" + flag++;

//向缓冲区中输入数据

sendbuffer.put(sendText.getBytes());

//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位

sendbuffer.flip();

//输出到通道

client.write(sendbuffer);

System.out.println("服务器端向客户端发送数据--:"+sendText);

client.register(selector, SelectionKey.OP_READ);

}

中的红色部分去掉,就会出现服务器一直往客户端打印东西? 这是为何? 兄台弄清楚了么?

10 楼

zk1878

2014-01-14

对nio不是很了解,这里有个疑问,在client那的代码

为什么当selectionKey.isConnectable()时,只注册了

SelectionKey.OP_READ,而不注册SelectionKey.OP_WRITE;

还有当selectionKey.isWritable()时,为什么每次都要注册

SelectionKey.OP_READ,我觉得应该是在selectionKey.isConnectable()

时同时注册OP_READ,OP_WRITE吧

9 楼

Jnerd

2014-01-13

运行不了啊,服务端没有接收到字符串,只是连接上了

8 楼

632578551a

2013-03-06

关于 一条读写操作完成之后修改通道关心事件为什么不用

key的interestOps方法???

7 楼

刚开始吧

2013-02-26

这个程序的目的是什么?弄的我很模糊

6 楼

luisfabiano

2013-01-14

listen 方法是私有方法啊。怎么能随便调用呢?

5 楼

wangxinshui87

2012-11-30

为什么不把服务端和客户端的port写一致呢?

4 楼

lgfy1984

2012-09-26

好文,收藏

3 楼

xm_king

2012-07-06

sahala3293 写道

构造方法里面是不是尽量不要抛出异常,会不会导致一些奇怪的问题

这个也是要根据具体情况和场景分析,比如说,在构造方法中需要建立数据库连接,如果数据库连接异常的话,那最好就抛个异常出来,让上层知道。

« 上一页 1 2 下一页 »

你可能感兴趣的:(java,nio,实例)