[JAVA]Java NIOベースのI/Oとネットワーク2


TCPブロックチャネル


転送制御プロトコル(TCP):接続向けプロトコル
ブロック:スレッドが待機中

サーバスロットチャネルとスロットチャネルの用途


ServerSocketChannel

  • クライアントSocketChannelからの接続要求
  • を受け付ける.
  • 通信用のSocketChannel
  • を作成

    SocketChannel

  • 接続クライアント、サーバ、および通信
  • 🤔 IOとの違い:バッファを使用し、ブロックとブロックをサポートする

    サーバスロットチャネルを作成し、接続を受け入れる

    // ServerSocketChannel 객체 얻기 → 정적메서드 open() 사용
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
    // 블로킹으로 설정하기(기본 설정이지만 명시적으로 설정하여 논블로킹과 구분)
    ServerSocketChannel.configureBlocking(true);
    
    // 포트에 바인딩시키기(포트 정보를 가진 InetSocketAddress 객체 매개값)
    ServerSocketChannel.bind(new InetSocketAddress(5001));
    
    // accep() 메서드로 클라이언트 연결 수락(클라이언트가 연결 요청하기 전까지 블로킹)
    SocketChannel socketChannel = SeverSocketChannel.accept();
    
    // 연결된 클라이언트의 IP와 포트 정보 얻기
    // getHostName() : 클라이언트 IP 리턴
    // getPort() : 클라이언트 포트 번호 리턴
    // toString() : "IP : 포트번호" 형태의 문자열 리턴
    InetSocketAdress socketAddress = (InetSocketAdress)socketChannel
    				.getRemoteAddress();
    
    // 포트 언바인딩
    serverSocketChannel.close();

    コンセントチャネルの作成と接続の要求

    // SocketChannel 객체 얻기 → 정적메서드 open() 사용
    SocketChannel socketChannel = SocketChannel.open();
    
    // 블로킹으로 설정하기(기본 설정이지만 명시적으로 설정하여 논블로킹과 구분)
    SocketChannel.configureBlocking(true);
    
    // 서버 연결 요청(서버 IP와 포트 정보를 가진 InetSocketAddress 객체 매개값)
    // connect() 메서드는 연결이 완료될 때까지 블로킹, 완료되면 리턴
    SocketChannel.connect(new InetSocketAddress("localhost",5001));
    
    // 연결 끊기
    socketChannel.close();

    ソケットチャネルデータつうしん


    write()メソッドを使用して文字列を送信する

    // 정적 메서드 forName()으로 Charset 인스턴스 생성
    // Charset : 인스턴스의 캐릭터셋과 유니코드 사이의 변환을 처리하는 클래스
    Charset charset = Charset.forName("UTF-8");
    
    // encode() 메서드를 통해 유니코드로 변환하고 ByteBuffer에 담음
    ByteBuffer byteBuffer = charset.encode("Hello Server");
    
    socketChannel.write(byteBuffer);

    read()メソッドを使用して文字列を取得する

    // 100개의 byte를 저장하는 ByteBuffer 생성
    ByteBuffer byteBuffer = ByteBuffer.allocate();
    
    // socketChannel에서 읽은 byte들을 buteBuffer에 저장하고 읽은 바이트 수 리턴
    // 상대방이 SocketChannel의 close()를 호출하면 -1, 비정상적으로 종료되면 예외 발생
    int byteCount = socketChannel.read(byteBuffer);
    
    // 데이터 읽기 위해 위치 속성값 변경
    byteBuffer.flip();
    
    // 정적 메서드 forName()으로 Charset 인스턴스 생성
    Charset charset = Charset.forName("UTF-8");
    
    // buteBuffer안에 바이트들을 UTF-8로 변환하고 문자열로 다시 변환
    String message = charset.decode(byteBuffer).toString();

    スレッド並列処理


    TCPブロッキングは、データの入出力が完了する前に、読み取り()および書き込み()メソッドをブロックする
    👉 クライアント接続にワークスレッドを割り当てることで並列処理
    🚨 パラレル処理では、複数のクライアントが同時に接続されると、サーバに複数のスレッドが作成され、サーバのパフォーマンスが低下します.
    👉 スレッドプールの使用

    TCP非ブロックチャネル


    クローズドフィーチャー

  • connt()、accept()、read()、write()メソッドはブロックされていません.
  • accept():クライアントの接続要求がない場合null
  • を返します.
  • read():クライアントがデータを送信していない場合は、0
  • を返します.
    🚨 ブロックされずに直接戻るため、accept()を繰り返し文として使用すると、コードが実行され続け、CPUが過剰に消費されます.
    👉 イベントリスナーとして機能するセレクタの使用(Selector)

    セレクタ

  • クライアントの接続要求は、データがある場合、チャネルがセレクタに通知する.
  • セレクタは、通知されたチャネルを選択し、作業スレッドにaccept()またはread()メソッドを実行させてタスクを処理させる.
  • マルチチャネルタスクを単一スレッドで処理できる複数のコントローラ.
  • コレクタ運動原理


  • チャネルは、鍵を用いて独自のタスクタイプを生成し、コレクタの関心鍵セットに鍵を登録する.
  • クライアントが処理を要求すると、コレクタは「関心」キーセットからタスクを処理する準備ができているキーを選択し、選択したキーセットに個別に格納します.
  • オペレーションスレッドは、選択された鍵セットから鍵を1つずつ取り出し、関連するチャネルオペレーションを処理する.
  • コレクタの作成と登録

    // Selector 생성
    try{
        Selector selector = Selector.open();
    }catch(IOException e){}
    
    // 넌블로킹 설정
    // Selector에는 SelectableChannel 하위 채널만 등록 가능
    SocketChannel socketChannel = SocketChannel.open(); // 예시 
    SocketChannel.configureBlocking(false);
    
    // Selector에 등록
    // 첫번째 매개값은 Selector, 두번째 매개값은 채널의 작업 
    SelectionKey selectionKey = socketChannel.register(selector
    						 , SelectionKey.OP_READ);
    2番目のパラメータとして使用できる特定の操作タイプの選択キー定数

    🚨 同じSocketChannelを使用して2つ以上の操作タイプを登録することはできません.
  • レジスタが返す選択キーは、タスクタイプの変更、添付オブジェクトの保存、チャネル登録のキャンセルなどに使用される.
  • チャンネルでSelectorを登録すると、keyFor()メソッドでSelectionKeyを取得できます.
  • 選択したキーセット

  • select()は、タスク処理準備完了の通知を受信するまで、関心鍵セットに格納された選択鍵からブロックされる.
  • の戻り値は、通知を発行する選択鍵の数である.
  • select()が戻る場合

  • 少なくとも1つのチャネルが準備完了であることを通知するとき、
  • セレクタのwakeup()メソッドを呼び出すとき
  • 呼び出し
  • select()のスレッド中断時は
  • である.

    チャネルの操作タイプを変更します。

    // selectionKey 작업 유형 변경
    selectionKey.interstOps(SelectionKey.OP_WRITE);
    
    // 블로킹되어 있던 select() 리턴
    selector.wakeup();
    
    // select() 다시 실행
    int keyCount = selector.select();
    
    // 값이 1 이상일 경우 selectedKeys() 작업 처리 준비된 Set<SelectionKey>를 얻음
    if(keyCount > 0){
    	Set<SelectionKey> selectedKey = selector.selectedKeys();
    }

    チャネルタスクの処理


    タスクを処理するには、ワークスレッドが選択キーがどのタイプのタスクであるかを決定する必要があります.
    // 채널 객체를 얻으려면 SelectionKey의 channel() 메소드를 사용
    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
    
    // 채널 객체 이외의 객체 첨부하기
    Client client = new Client(socketChannel);
    SelectionKey selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);
    selectionKey.attach(client);
    
    // 첨부된 객체 얻기
    if(selectionKey.isReadable()){
    	Client client = (Client)selectionKey.attachment();
    }

    TCP非同期チャネル


    ひどうきチャネルとくせい

  • connect()、accept()、read()、write()を呼び出してすぐに戻ります.
  • 実際のI/Oタスクはスレッドプールのスレッドが担当し、スレッドがタスクを完了したときにコールバックメソッドを呼び出す
  • read()メソッドの例


  • アプリケーション呼び出しread()メソッドは、直ちに
  • に戻る.
  • の内部で、スレッドプールの作業スレッドはread()方法で実際に
  • を実行する.
  • 操作スレッドがすべてのread()メソッドを実行すると、()コールバック
  • が完了する.
  • 完了()メソッド実行スレッドは、スレッドプールの動作スレッド
  • である.

    非同期チャネルグループ


    同じスレッドプールを共有する非同期チャネルグループ

    非同期チャネルグループの作成

  • 非同期チャネルを作成するとき、チャネルグループが指定されていない場合、デフォルトの非同期チャネルグループ
  • 基本非同期チャネルグループ内部で作成されたスレッドプール
  • を使用

    非同期チャネルグループを終了

  • shutdown()
  • 非同期チャネルグループを脱退する意向のみを伝える
  • .
  • 非同期チャネルグループ
  • を直ちに終了する.
  • 非同期チャネルグループ内のすべての非同期チャネルが閉じると
  • を終了する.
    新しい非同期チャネルを含めるには、ShutdownChannel Group Exception
  • を使用します.
  • shutdownNow()
  • 非同期チャネルグループ内のすべての非同期チャネルを強制的に閉じる
    ベースチャネルグループ
  • を終了する
  • コールバック実行中のスレッド終了または中断X
  • を完了する.

    UDPチャネル

  • NIO上のUDPチャネルはDataramChannelである.
  • 同期(ブロッキング)および非ブロッキング(非ブロッキング)は、いずれも
  • を用いることができる.

    送信者の作成

    // DatagramChannel 생성을 위해 open() 호출
    // open()은 ProtocolFamilydml 인터페이스 타입의 매개값을 가짐
    // 구현 객체로 StandardProtocolFamily 열거 상수 사용
    // IPv4와 IPv6을 구분하는 역할
    DatagramChannel datagramChannel = DatagramChannel.open(StandardProtocolFamily.INET);
    
    // send() 메소드를 이용해 데이터 보내기
    // 첫번째 매개값은 보낼 데이터, 두번째는 수신자 정보를 가진 SocketAddress(추상 클래스)
    // 하위 클래스인 InetSocketAddress 생성해서 대입
    int byteeCount = datagramChannel.send(byteBuffer, new InetSocketAddress("localhost", 5001));
    
    // 닫기
    datagramChannel.close();

    受信者の作成

    // DatagramChannel 생성을 위해 open() 호출
    // bind()를 호출해서 포트와 바인딩
    DatagramChannel datagramChannel = DatagramChannel.open(StandardProtocolFamily.INET);
    datagramChannel.bind(new InetSocketAddress(5001));
    
    // receive() 메소드를 이용해 데이터 받기
    // 데이터 받기 전까지는 블로킹 받으면 리턴
    SocketAddress socketAddress = datagramChannel.receive(ByteBuffer dst);
    
    // 작업 스레드 종료
    // 작업 스레드의 interrup() 호출로 예외 발생
    // datagramChannel에 close()을 호출시켜 예외 발생
    // 예외 처리코드에서 작업 스레드를 종료 시킴
    datagramChannel.close();