TCP Channel (Non-Blocking)

Non-Blocking Socket

๋ธ”๋กœํ‚น ๋ฐฉ์‹์€ Client๊ฐ€ ์—ฐ๊ฒฐ ์š”์ฒญ์„ ํ•  ๋•Œ๋งˆ๋‹ค accept()์—์„œ ๋ธ”๋กœํ‚น์ด ๋ฉ๋‹ˆ๋‹ค.

์ด์™€ ๋ฐ˜๋Œ€๋กœ Non-blocking ๋ฐฉ์‹์€ connect() accept() read() write()์—์„œ ๋ธ”๋กœํ‚น์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ Blocking์ด ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฌดํ•œ์ • read,write๋ฅผ ์‹คํ–‰ํ•˜์—ฌ CPU ์‚ฌ์šฉ๋ฅ ์„ ์ฆ๊ฐ€ ์‹œํ‚ฌ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, event Listener์ธ Selector๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค.

Drawing
Selector Architecture

Selector๋Š” ์ผ์ข…์˜ ๋ฉ€ํ‹ฐ ํ”Œ๋ ‰์„œ ์ž…๋‹ˆ๋‹ค.

๋ฉ€ํ‹ฐํ”Œ๋ ‰์„œ ? ์—ฌ๋Ÿฌ ์ž…๋ ฅ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒ

์ฑ„๋„์ด Selector์— ๋“ฑ๋ก ์š”์ฒญ์„ ํ• ๋•Œ, ์ž‘์—…์„ ํ‚ค๋กœ ์„ค์ •ํ•œ ๋’ค ๊ด€์‹ฌ ํ‚ค์…‹(interest key)์— ์ €์žฅ ์‹œํ‚ต๋‹ˆ๋‹ค

๊ด€์‹ฌ ํ‚ค์…‹์— ๋“ฑ๋ก๋œ ํ‚ค ์ค‘ ์ž‘์—… ์ค€๋น„๊ฐ€ ๋œ ํ‚ค๋Š” key Set(selected keySet)์— ์ €์žฅํ•œ ๋’ค, ํ•˜๋‚˜์”ฉ ๊บผ๋‚ด์–ด ์ฑ„๋„์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋„Œ๋ธ”๋กœํ‚น ๋ฐฉ์‹์€ ์Šค๋ ˆ๋“œํ’€์„ ์ด์šฉํ•˜์—ฌ ์ ์€์–‘์˜ ์Šค๋ ˆ๋“œ๋กœ๋„ ๋งŽ์€ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Create Selector

์ฒซ ๋‹จ๊ณ„๋กœ Channel์„ ์ƒ์„ฑํ•ด ์ค๋‹ˆ๋‹ค.

server) ServerSocetChannel

ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.bind(new InetSocketAddress("localhost", 8080));

client) SocketChannel

SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);

๋‘๋ฒˆ์งธ ๋‹จ๊ณ„๊ณ„๋กœ register()๋กœ Selector์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

Selector selector = Selector.open();
SelectionKey selectionKey = open.register(selector, {OPTION});
Option
Description

OP_ACCEPT

ServerSocketChannel์˜ ์—ฐ๊ฒฐ ์ˆ˜๋ฝ

OP_CONNECT

SocketChannel์˜ ์„œ๋ฒ„ ์—ฐ๊ฒฐ

OP_READ

SocketChannel์˜ ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ

OP_WRITE

SocketChannel์˜ ๋ฐ์ดํ„ฐ ์“ฐ๊ธฐ

๋™์ผํ•œ ์†Œ์ผ“ ์ฑ„๋„์€ ํ•œ๊ฐ€์ง€์˜ ์ž‘์—… ์œ ํ˜•(option)๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฝ๊ธฐ๋ฅผ ๋“ฑ๋กํ•œ ๋’ค ์“ฐ๊ธฐ๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด์„  ๊ธฐ์กด selectionKey๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

์—ฐ๊ฒฐ ํ›„ ์˜ต์…˜ ์ˆ˜์ •
SelectionKey selectionKey = client.register(clientSelector, SelectionKey.OP_CONNECT);
selectionKey.interestOps(SelectionKey.OP_READ);
selectionKey.interestOps(SelectionKey.OP_WRITE);

์„ธ๋ฒˆ์งธ ๋‹จ๊ณ„๋กœ Select๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

๋“ฑ๋ก๋œ SelectionKey์—์„œ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ฑ„๋„์˜ ์ž‘์—… ์ค€๋น„ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.

select()๋Š” blocking์ž…๋‹ˆ๋‹ค
clientSelector.select(); // ์‘๋‹ต ๋Œ€๊ธฐ
clientSelector.select(1000L); // 1์ดˆ ๋Œ€๊ธฐ
clientSelector.selectNow(); // ์ฆ‰์‹œ ํ˜ธ์ถœ ์—†์œผ๋ฉด 0

select()๋Š”์ฑ„๋„์˜ ์ค€๋น„ ์™„๋ฃŒ ์‘๋‹ต, Selector์˜ wakeUp(), Thread Interrupt์— ์˜ํ•ด ๋ฆฌํ„ด๋ฉ๋‹ˆ๋‹ค.

Selector clientSelector = Selector.open();
SelectionKey selectionKey = client.register(clientSelector, SelectionKey.OP_CONNECT);
selectionKey.interestOps(SelectionKey.OP_WRITE);
clientSelector.wakeup();

Channel Process

ํ‚ค์…‹์—์„œ SelectionKey๋“ค์„ ๋ฐ›์•„์™€ ์œ ํ˜•๋ณ„๋กœ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

public class SelectorWorker extends Thread {
    private final Selector selector;

    public SelectorWorker(Selector selector) {
        this.selector = selector;
    }

    @Override
    public void run() {
        while (true) {
                if (isSelectActive()) continue;
                findOption();
        }
    }

    private boolean isSelectActive(){
        try {
            if (selector.select() == 0) {
                return true;
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return false;
    }

    private void findOption() {
        selector.selectedKeys().forEach(selectionKey -> {
            switch (selectionKey.readyOps()) {
                case SelectionKey.OP_ACCEPT -> System.out.println("OPTION = OP_ACCEPT");
                case SelectionKey.OP_CONNECT -> System.out.println("OPTION = OP_CONNECT");
                case SelectionKey.OP_READ -> System.out.println("OPTION = OP_READ");
                case SelectionKey.OP_WRITE -> System.out.println("OPTION = OP_WRITE");
                default -> System.out.println("OPTION = UNKNOWN");
            }
        });
    }
}

์ฑ„๋„ ๊ฐ์ฒด๋Š” selectionKey์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();

์ฑ„๋„ ๊ฐ์ฒด ์™ธ์— ๋‹ค๋ฅธ ๊ฐ์ฒด๋„ selectionKey์— ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๊ฐ์ฒด๋ฅผ ๋“ฑ๋กํ•œ ๋’ค selectionKey์—์„œ ๊บผ๋‚ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

SelectionKey selectionKey = client.register(clientSelector, SelectionKey.OP_CONNECT);
selectionKey.attach(new Attached());

selector.selectedKeys().forEach(selectionKey -> {
Attached attached = (Attached) selectionKey.attachment();
});

Last updated