NIO服务端客户端简单实现
- 客服端不断的向nio服务端发送消息、接收消息;
- 服务端不断的向nio服务端发送消息、接收消息;
NIO服务端
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;
public class NioService {
private static final int BUF_SIZE=1024;
private static final int PORT = 9070;
private static final int TIMEOUT = 3000;
public static void main(String[] args)
{
selector();
}
//处理新连接
public static void handleAccept(SelectionKey key) throws IOException{
ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();
SocketChannel sc = ssChannel.accept();
sc.configureBlocking(false);
sc.register(key.selector(), SelectionKey.OP_READ,ByteBuffer.allocateDirect(BUF_SIZE));
}
//处理客户端发送过来的消息
public static void handleRead(SelectionKey key) throws IOException{
SocketChannel sc = (SocketChannel)key.channel();
ByteBuffer buf = (ByteBuffer)key.attachment();
long bytesRead = sc.read(buf);
while(bytesRead>0){
buf.flip();
while(buf.hasRemaining()){
System.out.print((char)buf.get());
}
System.out.println();
buf.clear();
bytesRead = sc.read(buf);
}
key.interestOps(key.interestOps()|SelectionKey.OP_WRITE);//此时注册写事件
if(bytesRead == -1){
sc.close();
}
}
//向客户端发送消息
public static int i = 0;
public static void handleWrite(SelectionKey key) throws IOException{
ByteBuffer buf = (ByteBuffer)key.attachment();
SocketChannel sc = (SocketChannel) key.channel();
String info = "I'm "+i+++"-th information from server";
buf.clear();
buf.put(info.getBytes());
buf.flip();
while(buf.hasRemaining()){
sc.write(buf);
}
buf.clear();
key.interestOps(key.interestOps()&~SelectionKey.OP_WRITE);//移除注册的写事件
}
public static void selector() {
Selector selector = null;
ServerSocketChannel ssc = null;
try{
selector = Selector.open();//打开Selector
ssc= ServerSocketChannel.open();//打开ServerSocketChannel
ssc.socket().bind(new InetSocketAddress(PORT));//Socket绑定端口
ssc.configureBlocking(false);//非阻塞
ssc.register(selector, SelectionKey.OP_ACCEPT);//向Selector注册Channel
while(true){
if(selector.select(TIMEOUT) == 0){
continue;
}
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while(iter.hasNext()){
SelectionKey key = iter.next();
if(key.isAcceptable()){
handleAccept(key);
}
if(key.isReadable()){
handleRead(key);
}
if(key.isWritable() && key.isValid()){
handleWrite(key);
}
if(key.isConnectable()){
System.out.println("isConnectable = true");
}
iter.remove();
}
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(selector!=null){
selector.close();
}
if(ssc!=null){
ssc.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
NIO客户端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.TimeUnit;
public class NioClient {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel socketChannel = null;
try
{
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost",9070));
if(socketChannel.finishConnect())
{
int i=0;
while(true)
{
//客户端向服务端发送消息
TimeUnit.SECONDS.sleep(1);
String info = "I'm "+i+++"-th information from client";
buffer.clear();
buffer.put(info.getBytes());
buffer.flip();
while(buffer.hasRemaining()){
socketChannel.write(buffer);//发送消息
}
//客户端从服务端接收消息
TimeUnit.SECONDS.sleep(1);
buffer.clear();
socketChannel.read(buffer);//接收消息
buffer.flip();
while(buffer.hasRemaining()){
System.out.print((char)buffer.get());
}
System.out.println();
}
}
}
catch (IOException | InterruptedException e)
{
e.printStackTrace();
}
finally{
try{
if(socketChannel!=null){
socketChannel.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
NIO就绪处理之OP_WRITE
写就绪相对有一点特殊,一般来说,你不应该注册写事件。写操作的就绪条件为底层缓冲区有空闲空间,而写缓冲区绝大部分时间都是有空闲空间的,所以当你注册写事件后,写操作一直是就绪的,选择处理线程全占用整个CPU资源。所以,只有当你确实有数据要写时再注册写操作,并在写完以后马上取消注册。
-
在收到客户端消息后,注册写事件;
key.interestOps(key.interestOps() SelectionKey.OP_WRITE);//此时注册写事件 -
服务端消息发送完毕,移除写事件。
key.interestOps(key.interestOps()&~SelectionKey.OP_WRITE);//移除注册的写事件