Java Socket、SocketServer的读写、连接事件监听,都是阻塞式的。Java提供了另外一种非阻塞式读写、连接事件监听方式——NIO。本文简单的介绍一个NIO Socket入门例子,原理以及详细用法,参考后续文章
服务端代码
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.util.Iterator; /**
*
* NIO Socket Server
* @author coshaho
*/
public class NIOServer
{
public static void main(String[] args) throws IOException
{
// 启动Socket Server Channel
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(8001));
server.configureBlocking(false); // 绑定选择器,注册连接监听事件
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT); SelectorHandler handler = new SelectorHandler();
while(true)
{
// 非阻塞监听注册事件
if(selector.select(2000) == 0)
{
System.out.println("No selection");
continue;
} // 发现注册事件,依次处理
Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
while(keyIter.hasNext())
{
SelectionKey key = keyIter.next();
if(key.isAcceptable())
{
handler.doAccept(key);
} if(key.isReadable())
{
handler.doRead(key);
} if(key.isValid() && key.isWritable())
{
handler.doWrite(key);
} keyIter.remove();
}
}
} /**
* 事件处理器
* @author h00219638
*/
public static class SelectorHandler
{
// 连接请求处理
public void doAccept(SelectionKey key) throws IOException
{
SocketChannel socket = ((ServerSocketChannel)key.channel()).accept();
socket.configureBlocking(false);
// 注册数据读取事件
socket.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(1024));
} public void doRead(SelectionKey key) throws IOException
{
SocketChannel socket = (SocketChannel)key.channel(); // 也可以临时分配ByteBuffer
ByteBuffer buf = (ByteBuffer) key.attachment();
buf.clear(); if(-1 == socket.read(buf))
{
socket.close();
}
else
{
System.out.println("Server received: " + new String(buf.array(), 0, buf.limit())); /**
* public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;
*/
// 增加写事件,写事件会不断被触发,数据写完后必须取消写事件监听
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
}
} public void doWrite(SelectionKey key) throws IOException
{
SocketChannel socket = (SocketChannel)key.channel();
ByteBuffer buf = (ByteBuffer) key.attachment(); // 写数据之前注意调用flip方法,重置指针
buf.flip();
System.out.println("Write: " + new String(buf.array(), 0, buf.limit()));
socket.write(buf);
if(!buf.hasRemaining())
{
// 取消写事件监听
key.interestOps(key.interestOps() &~ SelectionKey.OP_WRITE);
}
buf.compact();
}
} }
客户端代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel; import org.drools.core.util.StringUtils; /**
*
* NIO Socket Client
* @author coshaho
*/
public class NIOClient
{
public static void main(String[] args) throws IOException
{
SocketChannel socket = SocketChannel.open();
socket.configureBlocking(false); if(!socket.connect(new InetSocketAddress("localhost", 8001)))
{
System.out.println("Not connect");
// 正真的连接
while(!socket.finishConnect())
{
System.out.println("Not finishConnect");
}
} ByteBuffer wBuf = ByteBuffer.wrap("Hello, server".getBytes());
while(wBuf.hasRemaining())
{
socket.write(wBuf);
} ByteBuffer rBuf = ByteBuffer.allocate(8);
StringBuffer sBuf = new StringBuffer();
while(-1 != (socket.read(rBuf)))
{
rBuf.flip();
String s = new String(rBuf.array(), 0, rBuf.limit());
sBuf.append(s);
rBuf.clear();
if(StringUtils.isEmpty(s))
{
break;
} } System.out.println("Client received: " + sBuf.toString());
socket.close();
}
}