슬기로운 개발생활

[Java] NIO 기반 입출력 및 네트워킹 - TCP 블로킹 채널

by coco3o
반응형

NIO 네트워크?

NIO를 이용해서 TCP 서버/클라이언트 애플리케이션을 개발하려면 블로킹, 넌블로킹, 비동기 구현 방식 중에서 하나를 결정해야 한다.

 

이 결정에 따라 구현이 완전히 달라지기 때문이다.

 

이번 절에서는 블로킹 방식만 설명하겠다.

 

[Java] NIO 기반 입출력 및 네트워킹 - TCP 넌블로킹 채널


TCP 블로킹 채널

NIO에서 TCP 네트워크 사용을 위해서는 아래 채널을 사용한다.

java.nio.channels.ServerSocketChanne  //서버용
java.nio.channels.SocketChannel                //클라이언트용

서버를 개발하려면 우선 ServerSocketChannel 객체를 얻어야 한다.

 

ServerSocketChannel은 정적 메소드인 open()으로 생성하고,

블로킹 방식으로 동작하기 위해 configureBlocking(true)메소드를 호출한다.

 

기본적으로 블로킹 방식으로 동작하지만, 명시적으로 설정하는 이유는 넌블로킹과 구분하기 위해서이다.

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(true); // 블로킹 방식
serverSocketChannel.bind(new InetSocketAddress(5001)); // 포트 5001 연결을 위해서 열어놓음

SocketChannel socketChannel = serverSocketChannel.accept(); // 이 부분에서 연결이 될때까지 블로킹

 

클라이언트쪽에서는 채널을 열기 위해서 아래와 같이 SocketChannel을 열어야 한다.

SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(true);
socketChannel.connect(new InetSocketAddress("localhost", 5001));

둘 다 사용이 끝나면 close()로 닫아주어야 한다.

serverSocketChannel.close();
socketChannel.close();

 

다음 예제는 반복적으로 accept() 메소드를 호출해서 다중 클라이언트 연결을 수락하는 가장 기본적인 예제이다.

 

* ServerExample.java

package javaStudy;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class ServerExample {

    public static void main(String[] args) {
        ServerSocketChannel serverSocketChannel = null;

        try {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(true);
            serverSocketChannel.bind(new InetSocketAddress(5001));

            while (true) {
                System.out.println("[연결 기다림]");
                SocketChannel socketChannel = serverSocketChannel.accept();
                InetSocketAddress isa = (InetSocketAddress) socketChannel.getRemoteAddress();
                System.out.println("[연결 수락함] " + isa.getHostName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (serverSocketChannel.isOpen()) {
            try {
                serverSocketChannel.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

}

ServerExample.java 결과

 

 

다음 예제는 localhost 5001 포트로 연결 요청하는 코드이다. connect()메소드가 정상적으로 리턴되면 연결 성공한 것이다.

 

* ClientExample.java

package javaStudy;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;

public class ClientExample {

    public static void main(String[] args) {
        SocketChannel socketChannel = null;

        try {
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(true);

            System.out.println("[연결 요청]");
            socketChannel.connect(new InetSocketAddress("localhost", 5001));
            System.out.println("[연결 성공]");

        } catch (Exception e) {
            e.printStackTrace();
        }

        if (socketChannel.isOpen()) {
            try {
                socketChannel.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}

ClientExample.java 결과
ServerExample.java 결과


 

클라이언트가 연결 요청하고 서버가 수락했다면, 양쪽 SocketChannel 객체의 read(), write() 메소드를 호출해서 데이터 통신을 할 수 있다.

 

이 메소드들은 모두 버퍼를 가지고 있기 때문에 버퍼로 읽고, 쓰는 작업을 해야 한다.

 

다음은 SocketChannel의 write() 메소드를 이용해서 문자열을 보내는 코드이다.

Charset charset = Charset.forName("UTF-8");
ByteBuffer buffer = charset.encode("Hello Server");
socketChannel.write(buffer);

 

다음은 SocketChannel의 read() 메소드를 이용해서 문자열을 받는 코드이다.

ByteBuffer buffer = ByteBuffer.allocate(100);
int byteCount = socketChannel.read(buffer);
buffer.flip();
Charset charset = Charset.forName("UTF-8");
String data = charset.decode(buffer).toString();

 

 

다음 예제는 연결 성공 후, 클라이언트가 먼저 "Hello Server"를 보내면 서버가 이 데이터를 받고,

다시 서버가 "Hello Client"를 클라이언트로 보내면 클라이언트가 이 데이터를 받는 예제이다.

 

* ClientExample.java

package javaStudy;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;

public class ClientExample {

    public static void main(String[] args) {
        SocketChannel socketChannel = null;

        try {
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(true);

            System.out.println("[연결 요청]");
            socketChannel.connect(new InetSocketAddress("localhost", 5001));
            System.out.println("[연결 성공]");

            ByteBuffer byteBuffer = null;
            Charset charset = Charset.forName("UTF-8");

            byteBuffer = charset.encode("Hello Server");
            socketChannel.write(byteBuffer);
            System.out.println("[데이터 보내기 성공]");

            byteBuffer = ByteBuffer.allocate(100);
            int byteCount = socketChannel.read(byteBuffer);
            byteBuffer.flip();

            String data = charset.decode(byteBuffer).toString();
            System.out.println("[데이터 받기 성공]: " + data);

        } catch (Exception e) {
            e.printStackTrace();
        }

        if (socketChannel.isOpen()) {
            try {
                socketChannel.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}

 

 

* ServerExample.java

package javaStudy;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

public class ServerExample {

    public static void main(String[] args) {
        ServerSocketChannel serverSocketChannel = null;

        try {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(true);
            serverSocketChannel.bind(new InetSocketAddress(5001));

            while (true) {
                System.out.println("[연결 기다림]");
                SocketChannel socketChannel = serverSocketChannel.accept();
                InetSocketAddress isa = (InetSocketAddress) socketChannel.getRemoteAddress();
                System.out.println("[연결 수락함] " + isa.getHostName());

                ByteBuffer byteBuffer = null;
                Charset charset = Charset.forName("UTF-8");

                byteBuffer = ByteBuffer.allocate(100);
                int byteCount = socketChannel.read(byteBuffer);
                byteBuffer.flip();

                String data = charset.decode(byteBuffer).toString();
                System.out.println("[데이터 받기 성공]: " + data);

                byteBuffer = charset.encode("Hello Client");
                socketChannel.write(byteBuffer);
                System.out.println("[데이터 보내기 성공]");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (serverSocketChannel.isOpen()) {
            try {
                serverSocketChannel.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}

ServerExample.java부터 실행 후 ClientExample.java를 실행한 결과

 

Reference : '이것이 자바다' 19장 

반응형

블로그의 정보

슬기로운 개발생활

coco3o

활동하기