import java.io.IOException;
import java.nio.channels.SelectionKey;
public interface TCPProtocol {
void handleAccept(SelectionKey key) throws IOException;
void handleRead(SelectionKey key) throws IOException;
void handleWrite(SelectionKey key) throws IOException;
}
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class EchoSelectorProtocol implements TCPProtocol {
private int bufSize;
public EchoSelectorProtocol(int bufSize) {
this.bufSize = bufSize;
}
/**
* 获取与该客户端的信道 并交给 selector 管理
* @param key
* @throws IOException
*/
@Override
public void handleAccept(SelectionKey key) throws IOException {
SocketChannel clnChannel = ((ServerSocketChannel) key.channel()).accept();
clnChannel.configureBlocking(false);
//TODO 这里为什么需要一个 Buffer
//buffer 将作为附件,与register()方法所返回的 SelectorKey 实例相关联。为了之后数据的存取
clnChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufSize));
}
@Override
public void handleRead(SelectionKey key) throws IOException {
//支持数据读取 是个 SocketChannel
SocketChannel clnChannel = (SocketChannel) key.channel();
//获取相关联的缓冲区(连接建立后,有一个ByteBuffer附加在该 SelectorKey 实例上)
ByteBuffer buf = (ByteBuffer) key.attachment();
long byteRead = clnChannel.read(buf);
if(byteRead == -1){
clnChannel.close();
}else if(byteRead > 0){
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
}
@Override
public void handleWrite(SelectionKey key) throws IOException {
ByteBuffer buf = (ByteBuffer) key.attachment();
buf.flip(); // prepare for writing 使写数据的操作开始消耗由读操作产生的数据
SocketChannel clnChan = (SocketChannel) key.channel();
clnChan.write(buf);
//如果缓冲区中之前接收的数据已经没有剩余,则修改该键关联的操作集,指示其只能进行读操作
if(!buf.hasRemaining()){
key.interestOps(SelectionKey.OP_READ);
}
//压缩缓冲区:如果缓冲区中还有剩余数据,该操作则将其移动到缓冲区的前端
//以使下次迭代能够读入更多数据
buf.compact(); // make room for more data to be read in
}
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
/**
* 服务端选择器
*/
public class TCPServerSelector {
static final int BUFSIZE = 256;
static final int TIMEOUT = 3000; //milliseconds
public static void main(String[] args) throws IOException {
//selector 管理 listenChannel 和 所有 clnChannel
Selector selector = Selector.open();
ServerSocketChannel listenChannel = ServerSocketChannel.open();
listenChannel.socket().bind(new InetSocketAddress("127.0.0.1", 9999));
listenChannel.configureBlocking(false);
listenChannel.register(selector, SelectionKey.OP_ACCEPT);
//create a handler that will implement the protocol
TCPProtocol protocol = new EchoSelectorProtocol(BUFSIZE);
//run forever, processing available IO operations
while(true){
//wait for some channel to be ready(or timeout)
if(selector.select(TIMEOUT) == 0){
System.out.print(".");
continue;
}
Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
while(keyIter.hasNext()){
SelectionKey key = keyIter.next();
if(key.isAcceptable()){
protocol.handleAccept(key);
}
if(key.isReadable()){
protocol.handleRead(key);
}
if(key.isValid() && key.isWritable()){
protocol.handleWrite(key);
}
keyIter.remove();
}
}
}
}