인터넷에서 받은 NIO 자료 :


Thread 기본
Thread 생성
//Runnable 권장 (상속(is a)보단 합성(has a))
class ExThread extends Thread {
public void run() {
try {
Thread.currentThread().sleep(500);
System.out.println("extended thread test");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ImplThread implements Runnable {
public void run() {
try {
Thread.currentThread().sleep(500);
System.out.println("implemented thread test");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
try {
//상속  스레드 클래스
Thread t1 = new ExThread();
t1.start();
//Runnable 스레드 클래스 (상속보단 합성)
Thread t2 = new Thread(new ImplThread());
t2.start();
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Thread Group
//스레드 그룹에 속한 스레드에 대한 일괄적인 관리
//계층적인 thread group 생성 및 등록
ThreadGroup tg1 = new ThreadGroup(Thread.currentThread().getThreadGroup(), "Thread Group 1");
ThreadGroup tg2 = new ThreadGroup(tg1, "Thread Group 2");
Thread th1 = new Thread(tg1, "Thread 1");
Thread th2 = new Thread(tg2, "Thread 2");
System.out.println(" th1 : " + th1);
System.out.println(" th2 : " + th2);
System.out.println("thread 그룹 : " + Thread.currentThread().getThreadGroup());
System.out.println("active thread 개수 : " + Thread.currentThread().getThreadGroup().activeCount());
System.out.println("active thread group 개수 : " + Thread.currentThread().getThreadGroup().activeGroupCount());
Thread 관련 속성 메소드
th.setDaemon(true); //데몬 스레드
th.setPriority(Thread.MIN_PRIORITY); //우선순위
th.start(); //스레드 시작
th.sleep(); //스레드 일시중지
th.join(); //스레드 종료 대기
wait() : 다른 스레드가 락 획득 할 수 있도록 대기 상태로 감.
notify() : 대기 상태(wait)인 높은 우선순위의 스레드를 꺠워줌.
notifyAll() : 대기 상태(wait)인 모든 스레드들을 깨워줌.
synchronized {}
동기화를 위해 키워드나 블록 형태 지원.
ThreadLocal
스레드마다 독립적인 데이터 공간을 갖도록 해줌.
class ImplThread implements Runnable {
public int cnt1 = 0;
public static int cnt2 = 0;
public static ThreadLocal<Integer> cnt3 = new ThreadLocal<Integer>(){
protected Integer initialValue() {
return new Integer(0);
}
};
public void run() {
System.out.println("cnt1 = " + ++cnt1);
System.out.println("cnt2 = " + ++cnt2);
cnt3.set(cnt3.get()+1);
System.out.println("cnt3 = " + cnt3.get());
}
}
public class Test {
public static void main(String[] args) {
try {
ImplThread imp = new ImplThread();
Thread th1 = new Thread(imp);
th1.start();
th1.join();
Thread th2 = new Thread(imp);
th2.start();
th2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
출력 결과]
cnt1 = 1
cnt2 = 1
cnt3 = 1
cnt1 = 2
cnt2 = 2
cnt3 = 1



File 클래스 : 파일 정보, 삭제, 목록 출력
createTempFile(), deleteOnExit() : 임시파일 생성 및 JVM 종료시 자동 삭제 메소드



Byte 단위 IO 클래스
InputStream, OutputStream : 추상클래스
ex)
System.in.read()
System.out.write()
FileInputStream, FileOutputStream : 파일에 대한 byte IO 클래스
ex)
FileInputStream fis = new FileInputStream("파일명");
fis.read(버퍼);
..
FileOutputStream fos = new FileOutputStream("파일명");
fos.write(버퍼, 0, 길이);
DataInputStream, DataOutputStream : Native Data Type에 대한 IO 클래스
ex)
DataOutputStream dos = new DataOutputStream(new FileOutputStream("파일명"));
dos.writeBoolean(true);
..
DataInputStream dis = new DataInputStream(new FileInputStream("파일명"));
dis.readBoolean();
..
ByteArrayInputStream, ByteArrayOutputStream : byte 배열 형식의 IO
ex)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(버퍼, 0, 길이);
byte[] 바이트배열변수 = baos.toByteArray();
..
ByteArrayInputStream bais = new ByteArrayInputStream(바이트배열변수);
int 길이 = bais.read(버퍼);
PipedInputStrea, PipedOutputStream : 스레드간의 데이터 전달에 이용할 수 있다.
ex)
import java.io.*;
class TestThread implements Runnable {
InputStream pi = null;
OutputStream po = null;
TestThread(InputStream pi, OutputStream po) {this.pi = pi; this.po = po;}
public void run() {
int bytes_read;
byte[] buffer = new byte[512];
try {
while(true) {
bytes_read = pi.read(buffer);
if (bytes_read == -1) {
return;
}
po.write(buffer, 0, bytes_read);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
try {
PipedInputStream WriteIn  = new PipedInputStream();
PipedOutputStream ReadOut = new PipedOutputStream(WriteIn);
Thread th1 = new Thread(new TestThread(System.in, ReadOut));
Thread th2 = new Thread(new TestThread(WriteIn, System.out));
th1.start();
th2.start();
th1.join();
th2.join();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end");
}
}



문자 단위 IO 클래스
Reader, Writer : 문자 기반 IO 추상 클래스
InputStreamReader, OutputStreamReader : 문자 단위 IO 클래스
ex)
InputStreamReader isr = new InputStreamReader(new FileInputStream(파일명));
isr.read(버퍼);
..
OutputStreamReader osr = new OutputStreamReader(new FileOutputStream(파일명));
osr.write(버퍼);
..

FileReader, FileWriter : FileInputStream,FileOutputStream의 문자 단위 IO 버전
ex)
FileReader fr = new FileReader("파일명");
fr.read(버퍼);
..
FileWriter fw = new FileWriter("파일명");
fw.write(버퍼,0,길이);
..
fr.close();
fw.close();
BufferedReader, BufferedWriter : 버퍼(메모리) 대상으로 IO 수행
ex)
BufferedReader br = new BufferedReaader(new FileReader(파일명));
br.read(버퍼);
..
BufferedWriter bw = new BufferedWriter(new FileWriter(파일명));
bw.write(버퍼);
..
br.close();
bw.close(); //bw.flush()
PrintWriter : 출력용 클래스
ex)
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(파일명)));
pw.println("출력");
..
pw.close();
CharArrayReader, CharArrayWriter : ByteArrayInputStream,ByteArrayOutputStream의 문자 단위 IO 버전
ex)
CharArrayWriter caw = new CharArrayWriter();
caw.write(버퍼, 0, 길이);
char[] 문자배열변수 = caw.toCharArray();
..
CharArrayReader car = new CharArrayReader(문자배열변수);
int 길이 = car.read(버퍼);
StringReader, StringWriter : 소스가 String인 문자 스트림 클래스


Serializable
ObjectOutputStream, ObjectInputStream
 : 객체를 serialize/deserialize 하여 IO를 수행하는 클래스
객체는 Serializable을 구현한다.
transient 키워드
 : Serialize를 원하지 않는 필드 지정


인터넷 관련 클래스
URLEncoder : 문자열 인코딩을 위한 클래스
URLDecoder : 문자열 디코딩을 위한 클래스
URL : 인터넷 주소 저장 및 GET 방식으로 웹페이지를 읽어올 수 있는 클래스
URLConnection : 인터넷 페이지를 GET/POST 방식으로 받거나 전달할 수 있는 클래스
HttpURLConnection : GET/POST 방식이나 기타 http 연결 설정을 위한 클래스

Network
SOCK_STREAM : TCP
SOCK_DGRAM : UDP
SOCK_RAW : ICMP같은 raw 소켓으로 Java는 보안상 미지원
InetAddress : IP/Port, 도메인 주소 표현을 위한 클래스
ex)
try {
InetAddress[] inet = InetAddress.getAllByName("www.google.com");
for (int i=0; i<inet.length; i++) {
System.out.println(inet[i].getHostName()+":"+inet[i].getHostAddress());
}
InetAddress localhost = InetAddress.getLocalHost();
System.out.println(localhost.getHostName()+":"+localhost.getHostAddress());
} catch (UnknownHostException e) {
e.printStackTrace();
}
객체 Serialize(직렬화) 전송의 경우 ObjectOutputStream, ObjectInputStream 를 사용하며
속도가 중요한 경우 serialize 비용이 드므로 사용하지 않는 것이 좋다.

TCP (blocking)
TCP 에코 서버/클라이언트 샘플

UDP (blocking) :64KB 전송 제한
UDP 에코 서버/클라이언트 샘플

Multicast
멀티캐스트 그룹으로 패킷 전송.
라우터에서 멀티캐스트를 지원해줘야 가능. (라우터, 호스트 모두 IGMP 지원 및 패킷 처리 가능해야함)
UDP 이므로 데이터 유실 가능하며 순서 보장하지 않음.
MulticastSocket 클래스 사용.
*.멀티캐스트 그룹 : 클래스 D(244.0.0.0-239.255.255.255)에 속하는 IP
*.IGMP : Internet Group Management Protocol

Java NIO (New IO) : java.nio 패키지

Nonbloking을 지원하는 Java 1.4에 추가된 IO 기능
Scatter/Gather : 한번에 둘 이상의 버퍼를 동시에 사용할 수 있도록 하는 OS 수준의 기술 (vectored IO로 알려진 기술)
윈도우의 경우 WSASend, WSASendTo, WSARecv 등이 있으며 
Java에선 ScatteringByteChannel, GatheringByteChannel 인터페이스를 제공.
*.Vectored IO (동의어: Vector IO, 벡터 IO, Batch IO, Scatter/Gather IO)

특징 :
1.포인터 버퍼의 도입. NIO는 시스템 메모리를 직접 사용할 수 있는 Buffer 클래스가 도입됨. (DirectByteBuffer)
2.스트림의 업그레이드 버전인 Native IO 서비스를 제공하는 채널(channel)이 도입됨. (IO의 양방향 통신 지원)
3.셀렉터(Selector) 도입. (셀렉터는 Reactor 패턴(POSA2)의 구현체로서 하나의 스레드에서 수천~수만 동시 사용자를 처리할 수 있게 해준다.)

가상 메모리 : OS는 가상 메모리를 page로 나누고 디스크에 저장한다.
프로그램은 필요한 페이지의 가상 주소만을 메모리 주소로 바꾸어 실제 메인 메모리에 올린다.
실제 물리적 메모리 크기 보다 큰 가상 메모리 공간 사용이 가능해진다.
여러개의 가상주소가 하나의 물리적 메모리 주소를 참조하여 메모리를 효율적으로 사용할 수 있다.
(유저 영역의 버퍼와 커널 영역의 버퍼를 매핑 시켜 데이터 복사가 일어나지 않게 할 수 있다.)
Memory Mapped IO : 파일시스템의 페이지들과 유저영역 버퍼를 가상메모리로 매핑 시킨다.
(즉, 실제 데이터는 한곳에 저장되며 두군데에서 같은 곳을 참조하게 됨)
Java에선 MappedByteBuffer 클래스가 있음.
file lock (파일 락): 읽기 전용인 shared lock과 쓰기를 위한 exclusive lock이 있다.
Java NIO의 FileChannel과 FileLock이 이부분을 지원한다.
자바와 네트워크는 Big-endians(빅 인디안)을 기본으로 사용.

Java NIO 포인터 버퍼
: 성능을 위해 포인터 형식의 사용을 지원하는 버퍼
Buffer, ByteBuffer, ByteOrder, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, MappedByteBuffer, ShortBuffer
allocate(), wrap() 을 이용하여 초기화 한다.
ByteBuffer
: 유일하게 시스템 메모리(커널 영역의 메모리 공간)를 사용할 수 있는 다이렉트 버퍼(Direct Buffer)
주어진 크기만큼의 시스템 메모리를 JNI(Java Native Interface)를 사용하여 할당.
시스템 메모리를 래핑하는 자바 객체를 리턴.
ex)
ByteBuffer DirectBuffer = ByteBuffer.allocateDirect(1024);
System.out.println("is direct? " + DirectBuffer.isDirect());
다이렉트 버퍼는 생성과 소멸의 비용이 크므로 오랫동안 사용되거나 풀을 만들어 사용하는 방법이 권장된다.
채널(channel)과 버퍼
채널에서 버퍼로 데이터를 읽을때 버퍼의 position이 읽은 만큼 늘어나며
버퍼에서 채널로 쓸땐 쓴 만큼 버퍼의 position이 줄어든다.
 
뷰 버퍼 : 자신만의 속성(position, mark, limit, capacity)을 갖으나 버퍼와 데이터는 공유하는 버퍼
(duplicate(), slice()는 뷰 버퍼와 비슷한 버퍼를 만들어주는 메소드이다.)
asCharBuffer(), asShortBuffer(), asIntBuffer(), asLongBuffer(), asFloatBuffer(), as DoubleBuffer()
즉, byte 버퍼 지만 int나 short등 다른 타입으로 조작할 수 있는 장점이 생기며
또한 ByteBuffer는 자체적으로 putChar(), getChar() 같이 다른 타입으로 데이터를 읽고 쓸수 있다.
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
public class ByteBufferSimple {
public static void main(String[] args) {
/* Direct Buffer */
ByteBuffer db = ByteBuffer.allocateDirect(10);
System.out.println("db is direct buffer? " + db.isDirect());
System.out.println("db position:"+ db.position() +", limit:"+ db.limit() +", capacity:"+ db.capacity());
//View Buffer
IntBuffer ib = db.asIntBuffer();
System.out.println("ib position:"+ ib.position() +", limit:"+ ib.limit() +", capacity:"+ ib.capacity()); //Int 타입 기준으로 정의되는 속성들
ib.put(1024).put(2048);
System.out.println("ib value: "+ ib.get(0) +","+ ib.get(1));
System.out.print("db value: ");
while (db.hasRemaining()) 
System.out.print(db.get() +",");
}
}

ByteOrder : 빅 인디안, 리틀 인디안 같의 변환을 도와준다.
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class ByteBufferSimple {
public static void main(String[] args) {
ByteBuffer db = ByteBuffer.allocateDirect(10);
System.out.println(db.order());
db.order(ByteOrder.LITTLE_ENDIAN);
System.out.println(db.order());
}
}

CharBuffer : String 조작을 편리하게 하기 위한 버퍼 클래스
CharSequence를 구현하여 정규 표현식(Regular Expression)의 사용이 가능하다.
읽기 전용 뷰 버퍼를 만들 수 있다. (CharBuffer buf = CharBuffer.wrap("스트링");
ByteBuffer의 asCharBuffer()로 뷰 버퍼를 받아 string 조작을 편리하게 할 수 있다.



Channel (채널)
데이터를 전달하는 면에선 스트림과 유사하지만 양방향 통신이 가능하여 게이트웨이에 유사하다고 볼수 있다.
(소켓 채널은 양방향이 가능하지만 파일 채널은 그렇지 않다)
데이터 버퍼로 ByteBuffer를 사용한다.
채널 관련 클래스들
Channel : isOpen(), close() 제공 하는 인터페이스
Channels : 채널 사용을 위한 유틸리티 클래스. 채널과 스트림간의 변환을 도와준다.
InterruptibleChannel: 비동기적인 close()나 inturrupt 제공 하는 인터페이스
ByteChannel : ReadableByteChannel, WritableByteChannel를 구현하여 read(), write()하기 위한 인터페이스
ReadableByteChannel : 채널의 데이터를 ByteBuffer로 읽어들이는 read()를 제공하는 인터페이스
WritableByteChannel : ByteBuffer에 저장된 데이터를 채널로 쓰는 write()를 제공하는 인터페이스
read()와 write()는 여러 스레드에서 동시에 사용할 수 없으며 선점한 스레드의 작업이 끝날때까지 block 된다.
ex) 콘솔 입출력 예제
public class ByteBufferSimple {
public static void main(String[] args) {
ReadableByteChannel src = Channels.newChannel(System.in);
WritableByteChannel dest = Channels.newChannel(System.out);
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
try {
while (src.read(buf) != -1) {
buf.flip(); // flip() : ByteBuffer의 데이터를 읽기 위해 limit을 현재 position으로 설정하고 position을 0으로 세팅함.
while (buf.hasRemaining()) dest.write(buf);
buf.clear();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

ScatteringByteChannel (read()), GatheringByteChannel (write())
: 버퍼 여러개를 한번의 IO 작업으로 처리할 수 있는 Scatter/Gatter(Vectored IO)를 지원하는 클래스
ByteBuffer 배열을 받는 read(), write()를 사용하여 
반복문 제거, 시스템 콜과 커널 영역에서 프로세스 영역으로 버퍼 복사를 줄이거나 완전이 없애준다.
ex) 파일 입출력 예제
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
public class ByteBufferSimple {
public static void main(String[] args) {
/* ScatteringByteChannel 파일 읽기 */
try {
FileInputStream in = new FileInputStream(".classpath");
ScatteringByteChannel channel = in.getChannel(); //채널 획득
ByteBuffer[] buffers = {ByteBuffer.allocateDirect(100), ByteBuffer.allocateDirect(100)};
int readCount = (int) channel.read(buffers); //읽기
channel.close();
//출력
System.out.println("read count : " + readCount);
byte[] tmp = new byte[100];
for (ByteBuffer buf : buffers) {
buf.flip();
buf.get(tmp);
System.out.println("ByteBuffer : " + new String(tmp));
}
in.close();
} catch (Exception e) {
e.printStackTrace();
}
/* GatheringByteChannel 파일 쓰기*/
try
{
FileOutputStream out = new FileOutputStream("test.txt");
GatheringByteChannel channel = out.getChannel();
ByteBuffer[] buffers = {ByteBuffer.allocateDirect(20), ByteBuffer.allocateDirect(20)};
buffers[0].put("First ByteBuffer\n".getBytes());
buffers[1].put("Second ByteBuffer\n".getBytes());
buffers[0].flip();
buffers[1].flip();
channel.write(buffers); //파일 쓰기
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

파일 채널 (FileChannel, RandomAccessFile 클래스)
non-blocking을 지원하지 않으므로 항상 blocking 모드이다. (추후 지원 가능성은 있음)
파일객체의 getChannel()을 통해 파일 채널 객체를 생성한다.
가능하면 Native IO 서비스를 사용하길 권장된다.
동일한 프로세스 내에서 파일 채널 객체는 스레드에 안전하다. (동기화 보장)
주요 클래스
ByteChannel : 양방향성(read,write)을 위해 사용. (항상 양방향성을 갖지는 않음)
AbstractInterruptibleChannel
: 비동기적인 채널 close를 위해 InterruptibleChannel 인터페이스를 구현한 추상 클래스
ScateringByteChannel,GatheringByteChannel
: ByteBuffer 를 사용한 Scatter/Gather(vectored IO)를 위한 클래스
POSIX 시스템 콜에 대응되는 FileChannel, RandomAccessFile 메소드
FileChannel RandomAccessFile POSIX system call
read() .. ..
write() .. ..
size() length() fstat()
position() getFilePointer() lseek()
position(long newPosition) seek() seek()
truncate() setLength() ftruncate()
force() getFD().sync() fsync()
*. 하나의 파일에 대해 여러개의 파일객체나 채널객체를 생성해도 파일 디스크립터 공유되므로
하나의 객체에서 수정을 가하면 다른 객체에 영향이 간다.
ex)
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw"); //파일객체
FileChannel fc = raf.getChannel(); //채널객체
raf.seek(1000); //raf.getFilePointer()와 fc.position()은 동일하게 1000을 갖음.
..
*. read() : EOF(End of File)을 만나면 -1을 리턴한다.
(read()시 position 값이 파일크기보다 크면 아무것도 읽지 않고 리턴된다.)
*. write() : 파일을 쓸때 EOF에 다다르면 필요한 만큼 자동으로 파일 크기가 늘어난다.
(버퍼는 Exception을 던짐)
(write()시 position 값이 파일크기보다 크면 자동으로 파일크기가 늘어난다.)
*. truncate(): 인자로 지정한 크기만큼 파일을 잘라준다.
*. force() : 파일의 업데이트된 내용을 강제로 물리적인 파일에 쓴다.
(업데이트된 내용은 성능을 위해 캐시같은 작업을 하기 때문에 제공되는 메소드)
(인자로 받는 boolean은 파일의 메타데이터(소유자, 접근권한, 마지막 수정일 등..)도 업데이트 할지를 지정함)
파일 락킹 (file locking) - FileLock 클래스
shared lock(공유 락), exclusive lock(베타 락) 지원.
파일 사용시에만 지원되며(채널은 안됨) 외부 프로세스들 간의 파일 접근을 제어하는데 쓰인다.
FileChannel의 lock 획득 메소드들 (FileLock 객체를 리턴함)
lock() : 락 획득용. 다른 프로세스가 선점중이면 풀릴때까지 blocking 된다.
tryLock() : lock()과 동일하지만 다른 프로세스가 선점중일땐 바로 null을 리턴하는 non-blocking 메소드.
ex) lock(0L, Long.MAX_VALUE, false); //파일 전체크기 lock 획득 : 0의 위치부터 Long.MAX_VALUE 크기만큼 배타 락 획득
FileLock 클래스
channel() : 자신을 생성한 FileChannel 객체를 돌려줌
position() : 락 시작 위치를 돌려줌
size() : 락된 크기를 돌려줌
isShared() : shared lock인지 exclusive lock인지 알려줌
overlaps() : 주어진 락 영역이 중첩되었는지(다른곳에서도 락을 잡은 부분이 있는지) 여부를 알려줌
isValid() : FileChannel로 부터 생성된 정상적인 Lock인지 여부를 알려줌
release() : 락 해제
ex)
FileChannel ch = 파일객체.getChannel();
FileLock lock = ch.lock(0, Long.MAX_VALUE, true);
.. 작업 ..
lock.release();
메모리 매핑 - MappedByteBuffer 클래스
파일을 메모리 기반의 버퍼 처럼 사용하며 수정시 실제 파일에 바로 반영된다.
ByteBuffer를 상속하였으며 대용량 파일 핸들링시 사용하기 좋다.
*. 생성 : 
FileChannel의 map() 메소드를 통해 MappedByteBuffer 클래스를 생성한다.
map() 메소드 호출시 READ_ONLY, READ_WRITE, PRIVATE 모드를 선택할 수 있다.
PRIVATE 모드는 읽기/쓰기가 가능하지만 복사본을 만들어 변경 내용을 별도 보관하며 원본은 변경되지 않는다.
ex) 파일 전체크기에 대한 메모리 매핑 생성
mapBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
*.load() : OS의 시스템 메모리에 매핑된 파일 데이터를 로드할 수 있는 만큼 로드한다.
*.isLoaded(): 시스템 메모리에 로드되었는지를 조회하지만 신뢰성은 없다.
실제 OS에서 페이지 인/아웃 으로 인해 다른 값이 나올 수 있기 때문이다.
*.force() : 메모리의 변경 내용을 물리적인 실제 파일로 강제로 쓰도록 한다.
채널에서 채널로 데이터 직접 전송 기능
소켓 채널 끼리는 불가능
FileChannel 클래스의
*.transferTo() : 주어진 target 채널로 데이터 전송(ex. 파일 -> 소켓)
*.transferFrom(): 주어진 src 채널로부터 데이터 수신(ex. 소켓 -> 파일)
100MB 파일카피 속도 측정 결과
채널간 직접 전송 사용 : 1625ms (transferTo 사용)
파일 매핑 사용 : 2547ms
ByteBuffer 사용 : 2578ms (allocateDirect 사용)
채널간 직접 전송 사용 : 2610ms (transferFrom 사용)
ByteBuffer 사용 : 3109ms (allocateDirect 미사용)
버퍼 사용 : 6438ms


소켓 채널 (SocketChannel 클래스)
파일 채널과 다른점 : non-blocking, Selector, Multiplex IO 지원
SelectableChannel의 non-blocking 설정
*.configureBlocking(): false를 주어 non-blocking 모드로 설정. (기본은 blocking 모드로 동작)
*.isBlocking() : blocking 여부
*.blockingLock() : blocking 모드 변경시 다른 스레드의 접근을 막기 위한 락용 Object를 얻는다.
리턴받은 Object를 synchronized(Object) {..} 하여 다른 스레드로 인한 모드 변경을 막을 수 있다.
ServerSocketChannel (서버용)
: 요청을 받을 서버용 소켓 채널. 
non-blocking 모드 지원 외엔 기존 ServerSocket과 동일하다.
ex)
ServerSocket servChannel = ServerSocketChannel.open(); //생성
servChannel.configureBlocking(false); //non-blocking 설정
//서버 소켓에 주소, 포트 설정
InetSocketAddress isa = new InetSocketAddress(InetAddress.getLocalHost(), 5000);
ServerSocket servSock = servChannel.socket();
servSock.bind(isa);
//accept()
while (true) {
SocketChannel sc = servChannel.accept(); //non-blocking 동작
if (sc == null)
Thread.sleep(1000);
else
sc.socket().getRemoteSocketAddress(); //client 주소정보
}
..
SocketChannel (클라이언트 용)
주의.
소켓 채널 생성시 소켓이 같이 생성되지만
소켓을 먼저 생성하면 소켓 채널은 생성되지 않음.
결국 (new Socket()).getChannel(); 의 리턴은 항상 null이 된다.
*.open() : SocketChannel 생성 및 서버 연결
SocketChannel.open(new InetSocketAddress("IP", port)); 는 
(SocketChannel.open()).connect(new InetSocketAddress("IP", port))와 같다.
*.configureBlocking() : non-blocking 사용 여부를 지정
*.finishConnect() : non-blocking 모드시 연결 종료시 호출. (non-blocking 모드시 필수)
*.isConnectionPending() : 현재 서버로 연결 진행 중인지 확인
ex)
SocketChannel sc = SocketChannel.open(new InetSocketAddress(InetAddress.getLocalHost(), port));
sc.configureBlocking(false);
sc.finishConnect();
혹은
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
sc.connect(new InetSocketAddress(InetAddress.getLocalHost(), port));
sc.finishConnect();
DatagramChannel : UDP용

Selector (셀렉터)
 - Selector, SelectableChannel, SelectionKey 클래스

Reactor 패턴의 구현체로 멀티플렉스(Multiplex) IO를 지원한다.
(Selector는 이벤트 요청에 따라 등록된 SelectableChannel을 적절한 서비스 제공자(서비스)에게 보내 처리하도록 한다.)
*.Reactor 패턴 : 클라리언트의 요청을 큐에 저장하고 큐를 모니터링하는 스레드에 이벤트를 보낸다.
이 스레드는 요청을 분석하여 적절한 서비스 제공자(서비스)에게 요청을 보내어 처리 한다.
*.멀티플랙스 IO : 하나의 스레드로 동시에 많은 IO 채널을 관리.
SelectableChannel 클래스
: blocking/non-blocking 설정 및 채널을 Selector 등록을 위한 추상클래스
blocking/non-blocking 모드 설정 관련 메소드 : configureBlocking(), isBlocking(), blockingLock()
Selector에 채널 등록 관련 메소드 : register(), isRegistered(), keyFor(), validOps()
*.register() : non-blocking 채널을 Selector에 등록시킨다.
ex) Selector에 서버소켓채널 등록
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
ServerSocket socket = server.socket();
socket.bind(new InetSocketAddress(port));
server.register(selector, SelectionKey.OP_ACCEPT); //등록
ex) Selector에 서버소켓 등록
ServerSocketChannel server = (ServerSocketChannel)key.channel();
SocketChannel sc = servver.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ); //등록
*.Selector 등록 가능한 채널별 모드
ServerSocketChannel : OP_ACCEPT
SocketChannel : OP_CONNECT, OP_READ, OP_WRITE
DatagramChannel : OP_READ, OP_WRITE
Pipe.SourceChannel : OP_READ
Pipe.SinkChannel : OP_WRITE
*.isRegistered(): Selector에 등록된 채널인지 확인
*.keyFor() : 인자로 받는 Selector에 자신이 등록되어있다면 중계자 역할인 SelectionKey를 리턴
*.validOps() : 자신이 등록된 Selector의 이벤트 값 리턴
SelectionKey 클래스
: 하나의 채널과 Selector 사이의 등록 관계를 갖는 클래스
*.이벤트
OP_ACCEPT : 클라이언트가 ServerSocketChannel에 접속 시도시 발생하는 이벤트
OP_CONNECT : 서버가 클라이언트 접속 허락시 발생하는 이벤트
OP_READ : 서버가 클라이언트 요청을 읽을때 발생하는 이벤트
OP_WRITE : 서버가 클라이언트 응답을 write 할수 있을 때 발생하는 이벤트
*.channel() : 자신과 연관된 채널 리턴
*.selector(): 자신과 연관된 Selector 리턴
*.cancle() : 자신을 Selector에서 등록 해제
*.isValid() : 자신이 유효한 SelectorKey인지 알려줌
*.interestOps() : 등록된 이벤트 값들을 알려주거나 재설정
*.readyOps() : 등록된 이벤트 중에 준비된 이벤트에 대한 값을 알려줌
*.isReadable() : OP_READ    이벤트가 준비 되었는지 알려줌
*.isWritable() : OP_WRITE   이벤트가 준비 되었는지 알려줌
*.isConnectable() : OP_CONNECT 이벤트가 준비 되었는지 알려줌
*.isAcceptable() : OP_ACCEPT  이벤트가 준비 되었는지 알려줌
*.attach() : 참조할 객체 추가
*.attachment() : 참조 객체가 있는 경우 해당 객체를 리턴함
Selector 클래스
: 등록된 채널과 이벤트에 대한 처리를 하는 핸들러로 멀티스레드에 안전하지만
key Set의 접근은 사용자의 동기화 처리가 필요하다.
*.open() : Selector 객체 생성. ex) Selector selector = Selector.open();
*.isOpen() : open() 메소드 사용 가능 상태인지 알려줌
*.close() : 자원 해제
*.keys() : 유효하든 아니든 등록된 모든 키의 Set을 돌려줌
*.selectedKeys(): 수행 준비가 된 이벤트 키들의 Set을 돌려줌
*.select() :
동작1. 취소된 키들 정리
동작2. 등록된 키들의 정리와 상태를 갱신
동작3. 수행준비가 된 키들의 이벤트 형식에 따라 적절한 핸들러에게 처리를 넘긴다.
.select() : blocking 메소드이며
.select(long timeout) : 주기적으로 select()를 수행한다.
.selectNow(): select(0)과 같지만 non-blocking로 동작한다.
*.wakeup() : Selection 메소드들 중 스레드가 blocking 된 스레드를 깨우는데 사용
Selector에 등록가능한 SelectionKey는 Integer.MAX_VALUE(약 2억개:2,147,483,647)개 이다.

NIO를 사용한 채팅 서버 샘플
향상되고 구조적인 NIO 채팅 서버 : "자바 I/O & NIO 네트워크 프로그래밍" 16장 소스



RMI(Rmote Method Invocation)
 : EJB의 내부에서도 사용되는 RMI, 윈도우의 DCOM과 같은 개념



"자바 IO & NIO 네트워크 프로그래밍" 전체 샘플 소스 코드