이벤트 핸들러

- 이벤트 처리를 다루는 객체


코덱

- 인코딩과 디코딩의 합성어, 이벤트 핸들러를 상속받아 구현된 구현체



인코더: 전송할 데이터를 전송 프로토콜에 맞춰 변환, ChannelOutboundHandler 인터페이스 상속

디코더: 수신한 데이터를 전송 프로토콜에 맞춰 변환, ChannelInboundHandler 인터페이스 상속



네티에는 수많은 코덱이 있다.


ex)loggingHandler, Mqtt, Base64, Https 등등..








파이프라인 

- 발생된 이벤트가 이동하는 통로






- 다음은 서버소켓으로부터 이벤트들이 채널로 들어온 상황을 가정한 그림이다.

클라이언트 부트스트랩에 등록한 채널에 이벤트가 들어온다면, 가장 먼저 등록한 핸들러로부터 이벤트를 처리할 것이다.







 

 






Netty Framework는 Multi Thread를 지원하고 Event loop 기반 위에서 수행됩니다. 









특징

- 네티의 이벤트는 채널에서 발생

- 채널에 event handler를 여러개 추가할 수 있다.

- multi core CPU 효율적으로 사용

- 이벤트 루프 객체는 이벤트 큐를 가지고 이벤트가 큐에 쌓임

- 네티의 채널은 하나의 이벤트 루프에 등록됨, 다중 이벤트루프 또한 지원

- 네티의 이벤트는 발생순서와 처리순서가 항상 일치한다.

- 이벤트 처리를 위해 SingleThreadEventExecutor 사용







=> 간단하게 요약하면, 클라이언트와 서버간의 입출력 이벤트는 채널에서 read/write 이벤트 루프객체 전달을 통해 처리된다,   






  


[아래 그림은 클라이언트에서 write 이벤트를 발생시키는 처리 과정이다.]





<Process>


1) 기본적으로 여러개의 쓰레드는 channelcontext 에 write 할 수 있다.


2) InboundHandlerAdapter는 채널파이프라인으로 전달된다. (파이프라인은 다음장에서.)



3) 채널에서 발생된 이벤트는 이벤트루프그룹 큐에 쌓이고 서버와 연결된 socket의 outputstream을 통해 write한다.












참고자료 : https://www.slideshare.net/krisjeong/going-asynchronous-with-netty-soscon-2015

(1) Netty Bootstrap이란?


- 애플리케이션의 동작 및 설정을 지정해주는 헬퍼클래스






(2)  Bootstrap의 종류


- ServerBootstrap

- bootstrap


=> bootstrap은 클라이언트에서 사용하며, 구조적으로 ServerBootstrap과 동일








(3)  Bootstrap의 논리적구조






(4)  Bootstrap의 예제코드


public EchoServer() {


   public static void main(String[] args){

        bossGroup = new NioEventLoopGroup(1);  // 생성자에 1이므로 단일스레드로 동작하는 객체

        workerGroup = new NioEventLoopGroup(); // 생성자 인수가 없으면 cpu 코어 수에 따라 설정된다.


    bootstrap = new ServerBootstrap();

    bootstrap.group(bossGroup, workerGroup) // boss – 연결, worker - 입출력

        .channel(NioServerSocketChannel.class) // 부모쓰레드(boss) 입출력모드 설정 NIO모드

        .childHandler(new ChannelInitializer<SocketChannel>() { //worker쓰레드 자식 채널의 초기화

          @Override

          protected void initChannel(SocketChannel socketChannel) throws Exception {

            ChannelPipeline pipeline = socketChannel.pipeline();

            // 핸들러 추가

            pipeline.addLast(new EchoServerHandler());

          }

        });


     }

 bootstrap.bind(host,port).sync();  //socket 주소와 port binding

  }






(5)  Bootstrap의 구체적인 옵션


group: 이벤트 루프 설정

클라이언트와 달리 이벤트 루프가 2개 필요


channel: 소켓 입출력 모드 설정

LocalServerChannel: 로컬 가상 통신을 위한 소켓 채널

OioServerSocketChannel: Blocking 소켓 채널

NioServerSocketChannel: Nonblocking 소켓 채널

EpollServerSocketChannel: epoll 소켓 채널 (리눅스에서만적용)

OioSctpServerChannel: Blocking sctp 소켓 채널

NioSctpServerChannel: Nonblocking sctp 소켓 채널

SCTP(Stream Control Transmission Protocol): Layer 4 protocol

NioUdtByteAcceptiorChannel: Nonblocking udt 소켓 채널

NioUdtMessageAcceptorChannel: blocking udt 소켓 채널

UDT(UDP-Based Data Transfer): Layer 7 protocol


channelFactory: 소켓 입출력 모드 설정

Netty에서 기본 제공하는 channel class보다 더 복잡한 로직이 필요할 때 사용


handler: 서버 소켓 채널의 이벤트 핸들러 설정

부모 쓰레드에서 발생한 이벤트만 처리


childHandler: 클라이언트 소켓 채널의 이벤트 핸들러 설정

자식 쓰레드에서 발생한 이벤트만 처리


option - 서버 소켓 채널의 소켓 옵션 설정

기본적으로 자바에서 설정할 수 있는 모든 소켓 옵션 설정 가능

TCP_NODELAY: Nagle 알고리즘 비활성화 여부 설정

SO_KEEPALIVE: 정해진 시간마다 keepalive packet 전송

SO_SNDBUF: 커널 송신 버퍼 크기

SO_RCVBUF: 커널 수신 버퍼 크기

SO_REUSEADDR: TIME_WAIT 상태의 포트에도 bind 가능해짐

SO_LINGER: 소켓을 닫을 때 송신 버퍼에 남은 데이터 전송 대기 시간

SO_BACKLOG: 동시에 수용 가능한 소켓 연결 요청 수


childOption: 클라이언트 소켓 채널의 소켓 옵션 설정












스탠리 큐브릭

OSI 7 Layer (Open System Interconnection) 

기종이 다른 컴퓨터간의 통신시 네트워크 구조에 상관없이 개방형 통신을 할 수 있도록
국제표준화기구(ISO)에서 개발한 모델.
네트워크를 이루는 구성요소들을 계층적 방법으로 나누고 각 계층의 표준을 정한 것.

OSI 모델의 목적은 기본적인 하드웨어와 소프트웨어의 논리적인 변경없이 시스템간의 통신을 개방하는 것.




TCP/IP 4 layer 가 먼저 나왔으며, 향후 OSI 7 layer로 발전하였다. (프로토콜에서 모델로 발전)

그렇다고 해서 TCP/IP 가 OSI 계층의 기능을 수행하지 못하는 것은 아니다.


해당 층에 수행하는 기능을 각각 비교해보자.



<OSI 7 Layer 와 TCP/IP 4 Layer>










응용계층 


네트워크 접근, 서비스 제공



응용계층


  사용자가 네트워크에 접근할 수 있는 계층.

  telnet, http, ftp, ssh .. 


 표현계층


이해할 수 있는 포맷 변환


  입출력 간 데이터를 하나의 표현 형태로 변환

 jpeg, gif, mpg .. 


 세션계층


네트워크의 질서 제어


  사용자 간의 포트연결이 유효한지 확인한다.

  세션 확인


  SSL, TLS .. protocol.




 전송계층


게이트웨이

  전송계층


 종단 대 종단 간 제어와 에러를 관리한다.


 패킷들의 전송이 유효한지 패킷들을 확인하고, 실패한 패킷을 다시 보내는 신뢰성 있는 전송을 보장

 머리말(header)에는 세그먼트가 포함. 


 tcp/udp protocol



 네트워크계층


장비: 라우터


 네트워크계층


 데이터 주소와 경로의 흐름을 제어한다. (ex - ping)

 패킷을 목적지로 전달할 책임을 갖는다.


 

데이터링크계층


장비 : 스위치,브리지


물리계층


 이더넷, 토큰 링 등 네트워크 사이의 데이터 전송 순서를 정함.

 

 OSI 7layer에서는 물리적 매체 전송순서와 데이터 접근 방법으로 나누어 계층을 구분했다.



 

물리계층


장비: 허브





-> l4  장비로 스위치를 두는 경우도 있는데, 3계층에서 온 트래픽을 분석하여 서비스 종류를 구분하는 역할






허브포트에 전기 신호가 도착하면 신호가 들어온 포트를 제외한 모든 포트에 신호를 전달. 각자 터미널 들이 그 신호를 받아 들일것인지 아닌지 결정.



스위치: 허브와 같이 포트에 들어온 신호를 다른 포트에 전송하는 기능

         허브는 모든 포트에 전송을 하지만 스위치는 프레임을 필터링 함

         MAC(물리주소, 랜카드 하드웨어)주소를 이용, 데이터를 보내고자 하는 포트에만 신호 전달

         충돌 도메인이 나뉘어짐



브리지: 개의 세그먼트를 연결하고 패킷을 전송하는 장비


라우터: Routing protocol을 사용하여 IP Packet이 원하는 목적지까지 원할하게 갈 수 있도록 경로를 정해주는 역할을 하는 장비를 지칭하는 것.

         

-  스위치와 브릿지, 허브를 통해서 LAN을 구성 후 외부와 연결하기 위해서는 라우터가 필요 

-  한 네트워크에서 다른 네트워크로 패킷을 전달하기 위해 사용

-  3계층이기때문에 IP주소를 사용해서 전달
-  라우터가 다른장비와 틀린점은 충돌도메인과 브로드캐스트 도메인을 분리



게이트웨이 : l4 이상에서만 작동하며, 퓨터 네트워크에서 서로 다른 통신망, 프로토콜을 사용하는 네트워크 간의 통신을 가능하게 하는 컴퓨터나 소프트웨어를 두루 일컫는 용어, 즉 다른 네트워크로 들어가는 입구 역할을 하는 네트워크 포인트이다. 특정 장비가 아니라 네트워크를 할때 노드에서 어디로 전송해야 할지를 나타내는 곳. 라우터나 스위치등의 장비가 될 수 있음







방화벽


- 방화벽은 네트워크 packet 중 network protocol의 tcp/ip layer에서 ip와 port 정보를 갖고 있으며 방어한다.


 · 외부 네트워크(인터넷)로부터 내부 시스템을 보호

  - 문제 있는 트래픽이 들어오는 것을 제한(필터링)하는 일종의 라우터

 · 방화벽의 설치위치: 외부망과 내부망(사내망) 연결통로에 설치

 · 구현방식: 대부분이 라우터로써, 통합 구현



=> 네트워크 상에 흘러다니는 패킷을 검사하고 외부의 접근은 제한한다.




방화벽 역할


 · 침입 차단(Firewall) 역할

  - IP주소 포트에 의거한 침입 차단 기능 수행

 · 침입방지(IPS) 역할

  - 다양한 위협에 대처하기 위해, 많은 다기능 침입방지 기능들이 추가적으로 수행됨

 · 가상사설망(VPN) 역할


  - 정보의 비밀성 무결성에 촛점을 두고 수행








인바운드 / 아웃바운드 규칙


인바운드 

 - 서버 내부로 들어오는 여러 정보 ( IP / port / host / protocol / packet ....)

 - windows 기본 설정 ( 모든 접속 차단)

 - 컴퓨터로 들어오기 시작하는 모든 데이터



아웃바운드 

 - 외부로 송출하려는 정보

 - windows 기본 설정 ( 모든 접속 허용)

 - 컴퓨터에서 나가기 시작하는 모든 데이터


 간단한 채팅 프로그램 만들기


- 다대다 클라이언트 간의 채팅

- 서버는 클라이언트에게 메시지를 뿌려주는 역할




[Server]

package tcp4;


import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.ArrayList;

import java.util.List;


public class Server {


ServerSocket serSock;

List<Socket> sockList;


public Server() throws IOException {

serSock = new ServerSocket(10081);

sockList = new ArrayList<>();

}


public void all_client_write(Socket sock, String msg) {


BufferedOutputStream bos = null;

try {

String info = "[" + sock.getRemoteSocketAddress() + "] : ";

for (int i = 0; i < sockList.size(); i++) {

bos = new BufferedOutputStream(sockList.get(i).getOutputStream());

System.out.println("send to " + sockList.get(i).toString() + ":" + msg);


String inFoMsg = info + msg;

bos.write(inFoMsg.getBytes("UTF-8"));

bos.flush();


}


} catch (IOException e) {

// TODO Auto-generated catch block

try {

bos.close();

sock.close();

removeSock(sock);

} catch (IOException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}


System.out.println("server write exception!");


e.printStackTrace();

}


}


public void stop(Thread thread) {

thread.interrupt();

}

public void removeSock(Socket sock) {

System.out.println("socket is close");

System.out.println("====================================");

System.out.println("현재 접속: " + sockList.size());

for (int i = 0; i < sockList.size(); i++) {

if (sockList.get(i) == sock) {

try {

sockList.get(i).close();

sockList.remove(i);

break;

} catch (IOException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}


}

}

for (int i = 0; i < sockList.size(); i++) {

System.out.println(sockList.get(i).toString());

}

System.out.println("====================================\n\n");


}


public void read(Socket sock) {

Thread thread = null;


thread = new Thread(new Runnable() {


@Override

public void run() {

// TODO Auto-generated method stub

BufferedInputStream bis = null;

boolean flag = true;

try {

while (flag) {


bis = new BufferedInputStream(sock.getInputStream());

byte b[] = new byte[256];

int data = bis.read(b);


if (data != 0) {

String msg = new String(b);

System.out.println("[" + sock.getLocalAddress() + "] : " + msg);


all_client_write(sock, msg);

}


}

} catch (IOException e) {

// TODO Auto-generated catch block

try {

sock.close();

removeSock(sock);

} catch (IOException e2) {

// TODO Auto-generated catch block

e2.printStackTrace();

}

}


}


});


thread.start();


}


public void startServer() {

System.out.println("start Server!");

Thread thread = new Thread(new Runnable() {


@Override

public void run() {

// TODO Auto-generated method stub

try {

while (true) {

Socket sock = null;

sock = serSock.accept();

sockList.add(sock);


System.out.println("====================================");

System.out.println("현재 접속: " + sockList.size());

for (int i = 0; i < sockList.size(); i++) {

System.out.println(sockList.get(i).toString());

}

System.out.println("====================================\n\n");


read(sock);

}


} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

System.out.println("socket accept excpetion");

return;

}

}

});

thread.start();


}


public static void main(String[] args) {

// TODO Auto-generated method stub

try {

new Server().startServer();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

System.out.println("server start exception");

}

}


}

[Client]

package tcp4;


import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.Socket;

import java.net.UnknownHostException;

import java.util.Scanner;


public class Client {


Socket sock;

String host;

int port;


public void connect() throws UnknownHostException, IOException {

host = "localhost";

port = 10081;

sock = new Socket();

sock.connect(new InetSocketAddress(host, port));

}


public void read() {

Thread thread = new Thread(new Runnable() {


@Override

public void run() {

// TODO Auto-generated method stub

BufferedInputStream bis = null;

try {

while (true) {


bis = new BufferedInputStream(sock.getInputStream());

byte[] b = new byte[256];

int data = bis.read(b);


if (data != 0) {

System.out.println("\n");

System.out.println(new String(b));

}

}

} catch (IOException e) {

// TODO Auto-generated catch block


System.out.println("client read exception!");

e.printStackTrace();


try {

bis.close();

sock.close();

} catch (IOException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

}

}

});

thread.start();

}


public void chatStart() {

Scanner scan = new Scanner(System.in);

BufferedOutputStream bos=null;

while (true) {

System.out.print("입력>>");

String msg = scan.nextLine();


try {

bos = new BufferedOutputStream(sock.getOutputStream());

bos.write(msg.getBytes("UTF-8"));

bos.flush();

} catch (IOException e) {

// TODO Auto-generated catch block


System.out.println("client write exception!");

try {

bos.close();

sock.close();

break;

} catch (IOException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

e.printStackTrace();

}

}

}


public static void main(String[] args) {

// TODO Auto-generated method stub

Client client = new Client();

try {

client.connect();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

System.out.println("connect exception");

}

client.read();

client.chatStart();

}


}










====> 결과







소켓이 close() 됐을 때의 예외처리는 무엇보다 중요하다.

반드시 예외를 잡고 처리해야한다. 

시간이 없어서 빈약하게 처리했지만, 로직을 작성하는 것보다 메모리 누수 혹은 정확한 처리가 더 중요한 것 같다.




다음은 selector 와  asyncrhronousServerSocket에 대해 마지막으로 알아보자.




https://docs.oracle.com/javase/7/docs/api/java/nio/channels/AsynchronousServerSocketChannel.html














client / server



server 기반 모델   vs   p2p 모델




server 기반 모델


- 전용 서버를 두는 모델

- 안정적인 서비스 제공

- 공유 데이터의 관리와 보안이 용이





P2P 모델


- 전용 서버없이 각 클라이언트가 서버역할까지 동시에 수행

- 자원 활용의 극대화

- 보안이 취약하고 자원관리의 어려움







TCP / UDP


소켓 : 프로세스간의 통신에 사용되는 end point ( 소켓간의 커넥션이 되면 point to point)


TCP/UDP 는  OSI 7 계층의 전송 계층에 해당( tcp/ip protocol 에 포함 , 4layer)



 항목

tcp 

udp 

 연결방식

 연결기반

- 연결 후 통신

- 1:1 통신

비 연결기반

- 1:1 , 1:n, n:n 통신

- 연결없이 통신 ( 소포를 예를 듬) 

 특징

 - 신뢰성 있는 데이터 전송

 - 데이터의 전송 순서 보장

 - 데이터의 수신 여부 확인

 - 패킷 관리 할 필요 없다.

 - udp 보다 느림

- 비신뢰성 전송

- 데이터의 전송 순서가 바뀔 수 있다.

- 수신 여부 확인하지 않음

- 패킷 관리해야함.

 

 관련클래스

  serversocket, socket

 datagramsocket, datagrampacket, multicastsocket












+ Recent posts