스레드는 운영 채제애 비용을 먾이 들이는 작업으로 하나의 스레드에서 여러 채널을 관리하며 비용 소모가 적게 들게 되니다. 이런 경우 Selector에 여러개의 스레드를 묶어서 하나의 스레드로 처리하게 됩니다.
작업 순서는 다음과 같습니다.
Selector 생성
선택 가능한 채널 등록
1. selector 생성
Selector selector = Selector.open();
2. 선택 가능한 채널 등록
선택기에는 채널을 사용하기 위해서는 register 메서드로 채널을 등록 해야 하는데 우선 비동기 모드로 설정 되어 있어야 됩니다. 이 말의 의미는 FileChannel을 사용 할 수 없다는 이야기 입니다. 왜냐 하면 FileChannel는 비동기 모드로 동작을 하지 않기 때문입니다.
다음은 channel에 selector
register 의 시스니처 입니다.
public final SelectionKey register(Selector sel, int ops, Object att)
throws ClosedChannelException { ... }
selector에는 하나 또는 여러개의 채널을 연결할 수 있는데 selector의 select 메소드를 사용하여 선택 합니다. select 메서드는 하나 이상의 채널이 작업을 수행할 준비( (Interest Set : connect, accept, read or write))가 될 때까지 차단됩니다. 반환되는 정수는 채널이 조작을 위해 준비된 키의 수를 나타내는 것으로 다음과 같은 방법으로 얻을 수 있습니다.
int select() : 등록된 이벤트가 하나 이상의 채널이 준비 될 때까지 차단
int select(long timeout) : 최대 허용 시간 까지 차단단
int selectNow() : 차단 하지 않습니다.
5-1. selectedKeys()
준비된 채널의 SelectionKey Set를 통해서 얻을 수 있습니다.
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
} else if (key.isConnectable()) {
// a connection was established with a remote server.
} else if (key.isReadable()) {
// a channel is ready for reading
} else if (key.isWritable()) {
// a channel is ready for writing
}
keyIterator.remove();
}
6. 전체 예제
서버
package org.example;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
public class JavaNioSocketSever {
public static void main(String[] args) {
System.out.println("Hello world!");
try {
JavaNioSocketSever.selectorServerSocketChannel();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void selectorServerSocketChannel() throws IOException {
// Select 오픈 ( 생성 )
Selector selector = Selector.open();
System.out.println("Selector open: " + selector.isOpen());
// 서버 소켓 채널 오픈 ( 생성 )
ServerSocketChannel serverSocket = ServerSocketChannel.open();
InetSocketAddress hostAddress = new InetSocketAddress("localhost", 5454);
serverSocket.bind(hostAddress);
// 비동기 설정
serverSocket.configureBlocking(false);
// 해당 채널을 연결 수락을 식별하는 작업 SET
int ops = serverSocket.validOps();
// 서버 소켓 채널에 selector 지정
SelectionKey selectKy = serverSocket.register(selector, ops, null);
System.out.println("등록 이후 : " + selectKy);
for (;;) {
System.out.println("Waiting for select...");
int noOfKeys = selector.select();
System.out.println("Number of selected keys: " + noOfKeys);
Set selectedKeys = selector.selectedKeys();
Iterator iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey ky = (SelectionKey) iter.next();
if (ky.isAcceptable()) {
// Accept the new client connection
SocketChannel client = serverSocket.accept();
client.configureBlocking(false);
// Add the new connection to the selector
client.register(selector, SelectionKey.OP_READ);
System.out.println("Accepted new connection from client: " + client);
}
else if (ky.isReadable()) {
// Read the data from client
SocketChannel client = (SocketChannel) ky.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
client.read(buffer);
String output = new String(buffer.array()).trim();
System.out.println("Message read from client: " + output);
if (output.equals("Bye.")) {
client.close();
System.out.println("Client messages are complete; close.");
}
} // end if (ky...)
iter.remove();
} // end while loop
} // end for loop
}
}
클라이언트
package org.example;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class JavaNioClient {
public static void main(String[] args) {
try {
JavaNioClient.socketClient();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void socketClient() throws IOException, InterruptedException {
InetSocketAddress hostAddress = new InetSocketAddress("localhost", 5454);
SocketChannel client = SocketChannel.open(hostAddress);
System.out.println("Client sending messages to server..." + client.isConnected());
Thread.sleep(3000);
// 메세지 발송
String [] messages = new String [] {"Time goes fast.", "What now?", "Bye."};
for (int i = 0; i < messages.length; i++) {
byte [] message = new String(messages [i]).getBytes();
ByteBuffer buffer = ByteBuffer.wrap(message);
client.write(buffer);
System.out.println(messages [i]);
buffer.clear();
Thread.sleep(3000);
}
client.close();
}
}