SelectionKey,read,
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方法來建立的,