SelectionKey:
- 每次一个Channel注册到一个Selector时,都会返回一个SelectionKey的实例,
- 在使用一个SelectionKey实例之前,我们可以通过isValid()来判断这个实例的合法性(有没有被其他线程取消,对应channel有没有被关闭,对应的selector有没有被关闭,等等)
- 一个SelectionKey包含有两个被操作的集,一个是interest set(决定了selector调用select()方法时对它对应Channel的哪一种操作进行测试(一共有四种可以选择)),另一个是ready set(用于标识这个key是否处于准备状态),这两个集都是通过整数来标识的
- 针对第三点中提到的“四种选择”,不是对每一个Channel都有效的,这还取决于这个Channel的类型,具体的还可以通过SelectableChannel提供的validOps()方法,来查询那些set是被当前这个Channel所支持的
- SelectionKey可以有一个attachmen(附件),这个附件作为一个对象可以携带一些其他的东西。通过attach方法可以添加这个附件
- SelectionKey是线程安全的,这意味它可以用于多并发编程之中,因为它会被某个正在操作它的Selector强制同步
常用方法分析
- public final boolean isAcceptable();//测试key对应的channel是否准备创建一个新的socket connection,如果一个key对应的channel不支持socket-accept操作,通常会返回false
- public final boolean isConnectable();//测试key对应的channel是否已经完成或者失败了socket-connection(socket建立)操作,也就是说,要让这个方法返回true,他必须满足两个条件,第一,key调用的readyOps()函数的返回值必须是true,即调用这个方法之后返回的int数字,必须是SelectionKey的Operation-set当中的一种,第二,这个key的Operation-set bit 必须是OP_CONNECTION(对应的是一个数字),
- public final boolean isAcceptable();//测试这个key所对应的channel是否已经准备好来建立一个新的socket连接,这个方法需要返回true,也需要满足两个条件,,第一,key调用的readyOps()函数的返回值必须是true,即调用这个方法之后返回的int数字,必须是SelectionKey的Operation-set当中的一种,第二,这个key的Operation-set bit 必须是OP_ACCEPT(对应的是一个数字),
- 关乎上面两个方法的两个条件,第一个条件代表的是这个key对应的channel准备好了,后一个条件是在准备好了的基础之上,满足特定的某个操作。
- 类似的还有 public final boolean isReadable(),public final boolean isWritable()
下面给出测试代码:
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;
import java.util.Set;
public class NIOServer {
public static void main(String[] args) {
// TODO Auto-generated method stub
String host="127.0.0.1";
int port = 8090;
new Thread(new NIOServerHandler(host,port)).start();
}
}
class NIOServerHandler implements Runnable {
private ServerSocketChannel ssc;
private Selector selector;
public NIOServerHandler(String host,int port) {
// TODO Auto-generated constructor stub
try {
ssc=ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(host,port));
ssc.configureBlocking(false);
selector=Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
selector.select(3000);
Set<SelectionKey> sk=selector.selectedKeys();
Iterator<SelectionKey> it=sk.iterator();
while(it.hasNext()){
SelectionKey key=it.next();
it.remove();
handlerKey(key);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void handlerKey(SelectionKey key) throws IOException {
// TODO Auto-generated method stub
if(key.isValid()){
//判断是是否有接入请求,
if(key.isAcceptable()){
SocketChannel sc=((ServerSocketChannel)key.channel()).accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
}
//判断是否有等待读取的消息
if(key.isReadable()){
ByteBuffer readBuffer=ByteBuffer.allocate(1024);
((SocketChannel)key.channel()).read(readBuffer);
readBuffer.flip();
byte[] bytes=new byte[readBuffer.remaining()];
readBuffer.get(bytes);
System.out.println("收到新消息"+new String(bytes,"UTF-8"));
//返回一个结果
byte[] writeBytes ="我收到了一条消息".getBytes("UTF-8");
ByteBuffer writeBuffer = ByteBuffer.allocate(writeBytes.length);
writeBuffer.put(writeBytes);
writeBuffer.flip();
((SocketChannel)key.channel()).write(writeBuffer);
}
}
}
}
package nettyJava.Chapter2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String host="127.0.0.1";
int port = 8090;
new Thread(new NIOClientHandler(host,port)).start();
}
}
class NIOClientHandler implements Runnable{
private SocketChannel sc;
private String host;
int port;
private Selector selector;
public NIOClientHandler(String host,int port) {
// TODO Auto-generated constructor stub
this.host=host;
this.port=port;
try {
sc=SocketChannel.open();
sc.configureBlocking(false);
selector =Selector.open();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
socketChannelConnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while(true){
try {
selector.select(6000);
//获取被选择key,并且以此检查并操作每个key;
Set<SelectionKey> selectedKey=selector.selectedKeys();
Iterator<SelectionKey> it=selectedKey.iterator();
while(it.hasNext()){
SelectionKey key=it.next();
it.remove();
handlerKey(key);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void handlerKey(SelectionKey key) throws IOException{
if(key.isValid()){
SocketChannel socket=(SocketChannel) key.channel();
//判断socket是否处于连接成功,如果成功则需要注册
if(key.isConnectable()){
if(socket.finishConnect()){//表明连接已经建立
/*
* Finishes the process of connecting a socket channel.
* true if, and only if, this channel's socket is now connected
*/
socket.register(selector, SelectionKey.OP_READ);//注册这个channel,并且说明感兴趣的方面
}else{
System.exit(1);
}
}
//判断是否可读
if(key.isReadable()){
doRead(key);
doWrite(socket);
}
}
}
private void doRead(SelectionKey key) throws IOException{
System.out.println("从server输入的数据");
ByteBuffer readBuffer=ByteBuffer.allocate(1024);
SocketChannel sc=(SocketChannel) key.channel();
int readSize=sc.read(readBuffer);
readBuffer.flip();
if(readSize>0){
byte[] bytes=new byte[readBuffer.remaining()];
readBuffer.get(bytes);
System.out.println(new String(bytes,"UTF-8"));
}
}
private void socketChannelConnect() throws IOException{
//将这个sc连接到server,并且注册到监听器
sc.connect(new InetSocketAddress(host,port));
if(sc.finishConnect()){
sc.register(selector, SelectionKey.OP_READ);
doWrite(sc);
}
else
sc.register(selector, SelectionKey.OP_CONNECT);
}
private void doWrite(SocketChannel socket) throws IOException{
//byte[] bytes="this is a test".getBytes("UTF-8");
System.out.println("向server输出数据:回车后ok结束");
BufferedReader consoleIn=new BufferedReader(new InputStreamReader(System.in));
String sendMessage="";
while(true){
String temp=consoleIn.readLine();
if(temp.equals("ok"))break;
sendMessage+=temp+"\n";
}
byte[] sendBytes=sendMessage.getBytes("UTF-8");
ByteBuffer writeBuffer=ByteBuffer.allocate(sendBytes.length);
writeBuffer.put(sendBytes);
writeBuffer.flip();
socket.write(writeBuffer);
}
}
服务器端的每一个和客户端通信的socket,都是通过serverSocket的accept方法来创建的,