'Back-End'에 해당되는 글 287건

  1. 2019.04.26 19.04.25 데이터베이스란?
  2. 2019.04.25 19.04.25 웹 프로그래밍의 이해
  3. 2019.04.25 JSP 책 학습일정
  4. 2019.04.25 19.04.24 NIO 기반 입출력 및 네트워킹-2
  5. 2019.04.24 파일 채널,비동기 파일 채널 3
  6. 2019.04.23 NIO 기반 입출력 및 네트워킹-1
  7. 2019.04.22 19.04.22 IO기반 입출력 및 네트워킹-2
  8. 2019.04.21 mysql 테이블 만들어보기, 참조테이블 만들고, 활용해보기

19.04.25 데이터베이스란?

Back-End/Data Base 2019. 4. 26. 01:05

- 데이터 베이스의 기본 기능 -

 

 

  1. 데이터의 검색과 갱신

 

  2. 동시성 제어

 

  3. 장애 대응

      ㄴ3-1 데이터 다중화 : 데이터를 한 곳이 아니라 복수의 장소에 분산해서 유지

  ㄴ3-2 백업 : 데이터 소실이 발생했을 때 데이터를 복원하는 방법

 

  4. 보안

 

 

 

 

- 데이터베이스의 종류 -

 

 

  계층형 데이터베이스

 

  데이터를 계층 구조로 관리하는 데이터베이스.

  조직도나 전체 구조도를 상상하면 이미지가 쉽게 잡힌다.

 

  관계형 데이터베이스

 

  2차원 표 형식으로 데이터를 관리하는 데이터베이스.

  가장 주류를 이루고 있다.

 

  객체지향 데이터베이스와 XML 데이터베이스

 

  각각 '객체'와 'XML'이라는 형식으로 데이터를 관리하는 데이터베이스.

 

  NoSQL 데이터베이스

 

  관계형 데이터베이스에 있는 기능 일부를 버려서 성능을 높인 데이터베이스.

  대량의 데이터를 고속으로 처리해야 하는 웹 서비스와 잘 맞아서 최근 자주 이용.

 

 

XML :인터넷 웹페이지를 만드는 HTML을 획기적으로 개선하여 만든 언어이다.

  홈페이지 구축기능, 검색기능 등이 향상되었고, 웹 페이지의 추가와 작성이 편리해졌다.

:

19.04.25 웹 프로그래밍의 이해

Back-End/JSP 2019. 4. 25. 15:42


- 용어정리 -



클라이언트 :  네트워크에서 정보를 요구하는 쪽



서버 : 요구받은 정보를 제공하는 쪽의 컴퓨터



요청(request) : 클라이언트에서 서버로 정보를 요구하기 위해 보내는 메시지. GET방식과 POST방식이 있습니다.



응답(response) : HTTP에서 요구된 메시지에 대한 응답, HTML, 이미지 등의 응답이 내용이 됩니다.



동적 페이지 (동적 콘텐츠) : 콘텐츠를 동적으로 생성하는 페이지, 웹페이지들을 적절한 처리를 통해서 이미지를 배치해서 다양한 요구에 부합하도록 만들어진 페이지



WWW : 월드 와이드 웹의 줄임말. HTML로 작성된 홈페이지의 데이터는 인터넷에서 상호 링크하여 복잡한 컴퓨터 네트워크를 형성하고 있다. 마치 거미집 처럼 복잡하게 접속되어 있어서 Worldwide(세계적인) Web(거미집)이라고 불리게 되었다.



게이트 웨이 : 네트워크에서의 게이트웨이는 현재 사용자가 위치한 네트워크(정확히는 세그먼트-segment)에서 다른 네트워크(인터넷 등)로 이동하기 위해반드시 거쳐야 하는 거점을 의미한다.



세그먼트 : 메모리 관리 방식의 하나로, 프로그램이나 데이터를 세그멘트 또는 섹션이라는 가변 크기로 관리하는 방법이다.




CGI (Common GateWay Interface) : 월드 와이드 웹(www) 서버와 백 엔드 프로그램(게이트웨이) 사이에서 정보를 주고받는데 사용되는 인터페이스.CGI의 규약을 준수한다면 어떠한 언어도 사용 가능하다는 것이 CGI의 장점.



프로세스 : 프로그램이 실행중인 상태



스레드 : 컴퓨터 프로그래밍에서 어떤 프로세스 도는 프로그램의 일부분이 되는 프로세스. 은행을 예로 들면 프로세스는 은행이고, 스레드는 은행 안에 있는 각각의 창구



스크립트 언어 : 어떤 일을 수행하는 부분 코드들.



확장 CGI : 전통적인 OGI 방식의 단점들을 보완한 기술들이 확장 CGI로 발전. 확장 CGI는 매번 프로세스를 생성하는 방식이 아니고, 동일한 프로그램에 

              대해서는 하나의 프로세스를 생성한 후 여러 개의 스레드 방식으로 요청이 처리되는 방식이기 때문에 시스템의 부하를 줄일 수 있습니다.



- 확장 CGI의 종류 -


 

  ASP (Acive Server Page) : 비주얼 베이직이라는 언어에서 사용되는 문법들을 사용하여 동적 콘텐츠를 만들어 내기 위한 기술.

                                    하지만 특정 웹 서버와 OS(운영체제)에서만 동작한다는 것이 단점.


  PHP (Personal HomePage Tools, Professional Hypertext Preprocessor) : ASP와는 달리 특정 영역에서만 동작하지 않고, 작은 명령어들로 프로그래                                                                                               밍이 가능하고 편리성이 많다, 하지만 복잡한 요구들에 대한 기능 미약


  Servlet / JSP : Servlet (Server + Applet) : 확장 CGI 방식으로 Java라는 언어를 기반으로 하여 동적인 콘텐츠를 생성하는 기술 제공


  JSP (Java Server Pages) : JSP 또한 Java라는 언어를 기반으로 하여 만들어진 것이지만, ASP, PHP처럼 HTML 태그 사이 중간에 동적인 콘텐츠

                                  생성을 담당할 Java 코드가 들어가 있는 형태




  Servlet 와 JSP의 차이 : JSP는 HTML에 포함되어 프로그램이 만들어지는 것, 서블릿은 자바 프로그램의 외형에 웹 프로그래밍 요소가 포함된 것.

                               그런점으로 볼때 HTML 태그에 포함된 JSP는 브라우저에 표현이 될 부분에 유용하고,

                               서블릿은 브라우저에 표시될 필요가 없지만, 내부적으로 처리되어야 할 부분에 보다 유용하게 사용할 수 있다.



이식 : 특정한 시스템에서 개발한 소프트웨어를 다른 시스템에 설치해서 동작하게 하는 작업



서버 & 클라리언트 측 스크립트 : 서버에서 실행하는것을 서버 스크립트, 사용자의 브라우저에서 실행하는 것을 클라이언트 스크립트라고 한다.



HTML : 웹 문서를 만들기 위하여 사용하는 기본적인 웹 언어의 한 종류이다. 하이퍼텍스트를 작성하기 위해 개발되었다. 



자바빈즈 : 기본적으로 데이터를 저장하기 위한, 멤버변수와, 데이터를 컨트롤하는 setter/getter 메소드를 가지고 있는 클래스를 일컫는 말입니다.




-JSP 서버 처리과정-



:

JSP 책 학습일정

Back-End/JSP 2019. 4. 25. 13:46


JSP 책 학습일정.xlsx



:

19.04.24 NIO 기반 입출력 및 네트워킹-2

Back-End/Java 2019. 4. 25. 01:50

-  서버소켓 채널과 소켓 채널의 용도 -


이 두 채널은 IO의 ServerSocket과 Socket에 대응되는 클래스로, IO가 버퍼를 사용하지 않고

블로킹 입출력 방식만 지원한다면 ServerSocketChannel, SocketChannel은 버퍼를 이용하고 블로킹과 넌블로킹 방식을 모두 지원한다.

사용방법은 ServerSocketChannel은 클라이언트 SocketChannel의 연결 요청을 수락하고 통신용 SocketChannel을 생성한다.


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

ServerSocketChannel은 정적 메소드인 open( )으로 생성하고, 블로킹 방식으로 동작시키기 위해 configureBlocking(true)

메소드를 호출한다.

기본적으로 블로킹 방식으로 동작한다.

포트에 바인딩하기 위해서는 bind( ) 메소드가 호출되어야 한다.

포트 정보를 가진 InetSocketAddress 객체를 매개값으로 주면 된다.

포트 바인딩까지 끝났다면 ServerSocketChannel은 클라이언트 연결 수락을 위해 accept( ) 메소드를 실행해야 한다.

accept( ) 메소드는 클라이언트가 연결 요청을 하기 전까지 블로킹되기 때문에 UI 및 이벤트를 처리하는 스레드에서 accept( ) 메소드를

호출하지 않도록 한다.

클라이언트가 연결 요청을 하면 accept( )는 클라이언트와 통신할 SocketChannel을 만들고 리턴한다.


 

  블로킹이란?

  스레드가 대기 상태가 된다는 뜻이다.


  바인딩이란?

  컴퓨터 프로그래밍에서 각종 값들이 확정되어 더 이상 변경할 수 없는 구속(bind) 상태가 되는 것


  포트란? 

  모뎀과 컴퓨터 사이에 데이터를 주고받을 수 있는 통로 




 

  SocketChannel socketChannel = serverSocketChannel.accept( );




연결된 클라이언트의 IP와 포트 정보를 알고 싶다면 SocketChannel의 getRemoteAddress( ) 메소드를 호출해서

SocketAddress를 얻으면 된다. 실제 리턴되는 것은 InetSocketAddress 인스턴스이므로 타입 변환할 수 있다.


 

  InetSocketAddress socketAddress = (InetSocketAddress) socketChannel.getRemoteAddress( );




InetSocketAddress에는 다음과 같이 IP와 포트 정보를 리턴하는 메소드들이 있다.


리턴 타입

 메소드명(매개 변수)

 설명

 String

 getHostName( )

 클라이언트 IP 리턴

 int

 getPort( )

 클라이언트 포트 번호 리턴

 String

 toString( )

 "IP : 포트번호" 형태의 문자열 리턴



더 이상 클라이언트를 위해 연결 수락이 필요 없다면 ServerSocketChannel의 close( ) 메소드를 호출해서 포트를 언바인딩 시켜야 한다.

그래야 다른 프로그램에서 해당 포트를 재사용할 수 있다.


 

  serverSocketChannel.close( );





- 예제 및 출력 결과 -


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.hs.chap19;
 
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(); // open() 메소드를 사용해 서버 소켓 채널 얻기
            serverSocketChannel.configureBlocking(true); // 블로킹 방식으로 동작시키기 위해 호출한다. 이걸 안써도 기본적으로 블로킹 방식으로 동작하지만 //명시적으로
                                                            // 설정하는 이유는 넌블로킹과 구분하기 위해서이다.
            serverSocketChannel.bind(new InetSocketAddress(5001)); // bind()메소드의 매개값으로 5001번 포트를 줘서 바인딩 시킨다.
            while (true) {
                System.out.println("[연결 기다림]");
                SocketChannel socketChannel = serverSocketChannel.accept(); // 연결 수락을 위해 accept() 메소드를 호출
                InetSocketAddress isa = (InetSocketAddress) socketChannel.getRemoteAddress(); // getRemoteAddress() 메소드를
                                                                                                // 호출해 IP와 포트정보를 얻고,
                                                                                                // 타입변환을한다.
                System.out.println("[연결 수락함]" + isa.getHostName());
            }
        } catch (Exception e) {
    }
        if (serverSocketChannel.isOpen()) { // ServerSocketChannel이 열려있을 경우
            try {
                serverSocketChannel.close(); // ServerSocketChannel 닫기
            } catch (IOException e1) {
            }
        }
    }
}
 
cs




- 소켓 채널 생성과 연결 요청 -


SocketChannel은 정적 메소드인 open( ) 으로 생성하고, 블로킹 방식으로 동작시키기 위해 configureBlocking(true) 메소드를 호출한다.

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

서버 연결 요청은 connect( ) 메소드를 호출하면 되는데, 서버 IP와 포트 정보를 가진 InetSocketAddress 객체를 매개값으로 주면 된다.

connect( ) 메소드는 연결이 완료될 때까지 블로킹되고, 연결이 완료되면 리턴된다.

다음은 로컬 PC의 5001 포트에 바인딩된 서버에 연결을 요청하는 코드이다.


 

  SocketChannel socketChannel = SocketChannel.open( );

  socketChannel.configureBlocking(true);

  socketChannel.connect(new InetSocketAddress("localhost", 5001));





- 예제 및 출력 결과 -


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.hs.chap19;
 
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(); // open() 메소드를 사용해 서버 소켓 채널 얻기
            serverSocketChannel.configureBlocking(true); // 블로킹 방식으로 동작시키기 위해 호출한다. 이걸 안써도 기본적으로 블로킹 방식으로 동작하지만 //명시적으로
                                                            // 설정하는 이유는 넌블로킹과 구분하기 위해서이다.
            serverSocketChannel.bind(new InetSocketAddress(5001)); // bind()메소드의 매개값으로 5001번 포트를 줘서 바인딩 시킨다.
            while (true) {
                System.out.println("[연결 기다림]");
                SocketChannel socketChannel = serverSocketChannel.accept(); // 연결 수락을 위해 accept() 메소드를 호출
                InetSocketAddress isa = (InetSocketAddress) socketChannel.getRemoteAddress(); // getRemoteAddress() 메소드를
                                                                                                // 호출해 IP와 포트정보를 얻고,
                                                                                                // 타입변환을한다.
                System.out.println("[연결 수락함]" + isa.getHostName());
            }
        } catch (Exception e) {
        }
        if (serverSocketChannel.isOpen()) { // ServerSocketChannel이 열려있을 경우
            try {
                serverSocketChannel.close(); // ServerSocketChannel 닫기
            } catch (IOException e1) {
            }
        }
    }
}
 
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.hs.chap19;
 
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을 open()메소드를 사용해 생성
            socketChannel.configureBlocking(true); // 블로킹 방식으로 동작시키기 위해 configureBlocking 메소드를 호출
            System.out.println("[연결 요청]");
            socketChannel.connect(new InetSocketAddress("localhost"5001)); // localhost 5001포트로 연결요청을 한다.
            System.out.println("연결 성공");
        } catch (Exception e) {
        }
        if (socketChannel.isOpen()) {
            try {
                socketChannel.close();
            } catch (IOException e1) {
            }
        }
    }
}
 
 
cs



- 소켓 채널 데이터 통신 -


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

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

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


 

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

  ByteBuffer byteBuffer = charset.encode("Hello Server");

  socketChannel.write(byteBuffer);




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


 

  ByteBuffer byteBuffer = ByteBuffer.allocate(100);

  int byteCount = socketChannel.read(byteBuffer);

  byteBuffer.flip( );

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

  String message = charset.decode(byteBuffer).toString( );



- 예제 및 출력 결과 -

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.hs.chap19;
 
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// 소켓채널에 null값을 준다. 값을 담아야하기 때문
        try {
            serverSocketChannel = ServerSocketChannel.open(); // open() 메소드를 사용해 SocketChannel객체를 얻는다.
            serverSocketChannel.configureBlocking(true); // 블록킹방식으로 동작하기 위해 configureBlocking(true)호출
            serverSocketChannel.bind(new InetSocketAddress(5001)); // 새로운 5001번 로컬호스트를 연결 시킨다.
            while (true) {
                System.out.println("[연결 기다림]");
                SocketChannel socketChannel = serverSocketChannel.accept(); // 클라이언트 연결 수락을 위해 accept() 메소드 실행
                InetSocketAddress isa = (InetSocketAddress) socketChannel.getRemoteAddress(); // socketChannel의 IP주소와 포트
                                                                                                // 정보를 isa에 저장한다.
                System.out.println("[연결 수락함]" + isa.getHostName());
 
                ByteBuffer byteBuffer = null// 바이트버퍼에 null값을 준다. 값을 담아야하기때문
                Charset charset = Charset.forName("UTF-8"); // 매개값으로 주어진 문자셋을 담아놓는다. (문자열을 전송하기위해)
 
                byteBuffer = ByteBuffer.allocate(100); // 바이트버퍼에 100개의 공간을 새로 만들어 저장.
                int byteCount = socketChannel.read(byteBuffer); // 바이트버퍼의 수만큼 읽어서 카운트 변수에 저장
                byteBuffer.flip(); // position의 위치를 0번인덱스로 설정, (0이어야 ByteBuffer의 첫 바이트 부터 저장되기 때문)
                String message = charset.decode(byteBuffer).toString(); // 바이트버퍼를 디코딩해서 string타입으로 변환한다
                System.out.println("[데이터 받기 성공]: " + message);
 
                byteBuffer = charset.encode("Hello Client");
                socketChannel.write(byteBuffer);
                System.out.println("[데이터 보내기 성공]");
            }
        } catch (Exception e) {
        }
 
        if (serverSocketChannel.isOpen()) {
            try {
                serverSocketChannel.close();
            } catch (IOException e1) {
            }
        }
 
    }
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.hs.chap19;
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
 
public class ClientExample {
    public static void main(String[] args) {
        SocketChannel socketChannel = null// 소켓채널에 null값을 준다. 값을 담아야하기 때문
        try {
            socketChannel = SocketChannel.open(); // open() 메소드를 사용해 SocketChannel객체를 얻는다.
            socketChannel.configureBlocking(true); // 블록킹방식으로 동작하기 위해 configureBlocking(true)호출
            System.out.println("[연결 요청]");
            socketChannel.connect(new InetSocketAddress("localhost"5001)); // 새로운 5001번 로컬호스트를 연결 시킨다.
            System.out.println("[연결 성공]");
 
            ByteBuffer byteBuffer = null// 바이트버퍼에 null값을 준다. 값을 담아야하기때문
            Charset charset = Charset.forName("UTF-8"); // 매개값으로 주어진 문자셋을 담아놓는다. (문자열을 전송하기위해)
 
            byteBuffer = charset.encode("Hello Server"); // 위에서 담은 문자셋으로 Hello Server을 변환한다.
            socketChannel.write(byteBuffer); // 바이트버퍼에 담은 문자열을 보낸다.
            System.out.println("[데이터 보내기 성공]");
 
            byteBuffer = ByteBuffer.allocate(100); // 바이트버퍼에 100개의 공간을 새로 만들어 저장.
            int byteCount = socketChannel.read(byteBuffer); // 바이트버퍼의 수만큼 읽어서 카운트 변수에 저장
            byteBuffer.flip(); // position의 위치를 0번인덱스로 설정, (0이어야 ByteBuffer의 첫 바이트 부터 저장되기 때문)
            String message = charset.decode(byteBuffer).toString(); // 바이트버퍼를 디코딩해서 string타입으로 변환한다
            System.out.println("[데이터 받기 성공]: " + message);
        } catch (Exception e) {
        }
 
        if (socketChannel.isOpen()) {
            try {
                socketChannel.close();
            } catch (IOException e1) {
            }
        }
 
    }
 
}
 
 
cs

 

 

 

 

read( ) 메소드가 블로킹 해제되고 리턴되는 경우

 

 블로킹이 해제되는 경우

 리턴값

 상대방이 데이터를 보냄

 읽은 바이트 수

 상대방이 정상적으로 SocketChannel의 close( )를 호출

 -1

 상대방이 비정상적으로 종료

 IOException 발생

 

 

- 스레드 병렬 처리 -

 

TCP 블로킹 방식은 데이터 입출력이 완료되기 전까지 read( )와 write( ) 메소드가 블로킹 된다.

만약 애플리케이션을 실행시키는 main 스레드가 직접 입출력 작업을 담당하게 되면 입출력이 완료될 때까지 다른 작업을 할 수 없는 상태가 된다.

그렇기 때문에 클라이언트 연결(채널) 하나에 작업 스레드 하나를 할당해서 병렬 처리해야 한다.

 

 

 

- 스레드풀 -

 

클라이언트 폭증으로 인해 서버의 과도한 스레드 생성을 방지하려면 스레드풀을 사용하는 것이 바람직하다.

스레드풀은 스레드 수를 제한해서 사용하기 때문에 갑작스런 클라이언트의 폭증은 작업 큐의 작업량만 증가시킬 뿐

스레드 수에는 변함이 없기 때문에 서버 성능은 완만히 저하된다.

 

:

파일 채널,비동기 파일 채널

Back-End/Java 2019. 4. 24. 01:05

- 파일 채널 -

 

파일 채널을 이용하면 파일 읽기와 쓰기를 할 수 있다.

FileChannel은 동기화 처리가 되어 있기 때문에 멀티 스레드 환경에서 사용해도 안전하다.

FileChannel은 정적 메소드인 open()을 호출해서 얻을수도 있지만, IO의 FileInputStream,

FileOutputStream의 getChannel() 메소드를 호출해서 얻을 수도 있다.

다음은 open() 메소드로 FileChannel을 얻는 방법을 보여준다.

 

 

  FileChannel fileChannel = FileChannel.open(Path path, OpenOption...options);

 

 

첫 번째 path 매개값은 열거나, 생성하고자 하는 파일의 경로를 Path 객체로 생성해서 지정하고,

 

두 번째 options 매개값은 열기 옵션 값인데 StandardOpenOption의 열거 상수를 나열해주면 된다.

 

 열거 상수

설명 

 READ

 읽기용으로 파일을 연다.

 WRITE

쓰기용으로 파일을 연다. 

 CREATE

파일이 없다면 새 파일을 생성한다. 

 CREATE_NEW

새 파일을 만든다. 이미 파일이 있으면 예외와 함께 실패한다. 

 APPEND

파일 끝에 데이터를 추가한다(WRITE 나 CREATE와 함께 사용됨) 

 DELETE_ON_CLOSE

채널을 닫을 때 파일을 삭제한다(임시 파일을 삭제할 때 사용) 

 TRUNCATE_EXISTING

파일을 0바이트로 잘라낸다(WRITE 옵션과 함께 사용됨) 

 

FileChannel을 더이상 이용하지 않을 경우에는 close() 메소드를 호출해서 닫아주어야 한다.

 

 

  fileChannel.close();

 

 

 

 

- 파일 쓰기와 읽기 -

 

파일에 바이트를 쓰려면 FileChannel의 write() 메소드를 호출하면 된다.

매개값으로 ByteBuffer 객체를 주면 되는데, 파일에 쓰여지는 바이트는 ByteBuffer의 position부터 limit까지이다.

position이 0이고 limit가 capacity와 동일하다면 ByteBuffer의 모든 바이트가 파일에 쓰여진다.

write() 메소드의 리턴값은 ByteBuffer에서 파일로 쓰여진 바이트 수이다.

 

 

 

-예제 및 출력 결과-

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.hs.chap19;
 
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
 
public class FileChannelWriteExample {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("C:/Temp/file.txt"); // C:/Temp/file.txt경로를 객체로 얻는다.
        Files.createDirectories(path.getParent()); // path 경로에 있는 부모 디렉터리를 생성 한다.
 
        FileChannel fileChannel = FileChannel.open( // open()메소드를 사용해 파일의 경로를 지정하고, 파일이 없다면 새 파일을 생성하고 쓰기용으로 파일을 연다.
                path, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
 
        String data = "안녕하세요";
        Charset charset = Charset.defaultCharset();
        ByteBuffer byteBuffer = charset.encode(data); // encode()메소드를 사용해 data를 byteBuffer로 변환한다.
 
        int byteCount = fileChannel.write(byteBuffer); // write() 메소드로 byteBuffer를 쓰고, 그 바이트의 수를 count에 저장
        System.out.println("file.txt : " + byteCount + "bytes written");
        fileChannel.close();
    }
}
 
cs

 

 

 

 

- 예제 및 출력 결과 -

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.hs.chap19;
 
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
 
public class FileChannelReadExample {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("C:/Temp/file.txt"); // get()메소드로 파일이 저장될 경로를 얻는다.
 
        FileChannel fileChannel = FileChannel.open( // open()메소드를 사용해 path경로를 열고, 읽기용으로 파일을 연다.
                path, StandardOpenOption.READ);
 
        ByteBuffer byteBuffer = ByteBuffer.allocate(100); // allocate() 메소드를 이용, 100개의 바이트를 저장할수 있는 넌다이렉트 버퍼를 생성한다.
 
        Charset charset = Charset.defaultCharset();
        String data = "";
        int byteCount;
 
        while (true) {
            byteCount = fileChannel.read(byteBuffer); // read()메소드를 사용해 byteBuffer 파일로부터 데이터를 읽음
            if (byteCount == -1// 더이상 읽을 바이트가 없으면.
                break;
            byteBuffer.flip(); // position의 위치를 0번인덱스로 설정, (0이어야 ByteBuffer의 첫 바이트 부터 저장되기 대문)
            data += charset.decode(byteBuffer).toString(); // byteBuffer을 String로 변환해 하나씩 data에 담는다.
            byteBuffer.clear(); // 모든 위치 속성을 초기화
        }
 
        fileChannel.close();
        System.out.println("file.txt : " + data);
    }
 
}
 
cs

 




- 콜백 메소드 -


콜백이란 애플리케이션이 스레드에게 작업 처리를 요청한 후, 스레드가 작업을 완료하면 특정 메소드를 자동 실행하는 기법을 말한다.

이때 자동 실행되는 메소드를 콜백 메소드라고 한다.


블로킹 방식은 작업 처리를 요청한 후 작업이 완료될 때까지 블로킹되지만, 콜백 방식은 작업 처리를 요청한 후 결과를 기다릴 필요없이 다른 기능을 수행할 수 있다.

그 이유는 작업 처리가 완료되면 자동적으로 콜백 메소드가 실행되어 결과를 알 수 있기 때문이다.


콜백 메소드를 만들때는 직접 정의하거나 java.nio.channels.CompletionHandler를 이용해도 좋다.

CompletionHandler를 사용하면 콜백 객체를 만들수 있다.

다음은 CompletionHandler 객체를 생성하는 코드이다.


 

  CompletionHandler<V,A> callback = new CompletionHander<V,A>( ) {   //CompletionHander의 V 타입 파라미터는 결과값의 타입이고 

  @Override                                                                                     // A는 첨부값의 타입이다. 

  public void completed(V result, A attachment){                                   // 첨부값은 콜백 메소드에 결과값 이외에 추가적으로 전달하는 객체이다.

  }                                                                                                  // 첨부값이 필요 없다면 A는 Void로 지정해주면 된다.

  @Override

  public void failed(Throwable exc, A attachment){

  }

  }; 



- 콜백 메소드 종류 -


completed( ) : 작업을 정상 처리 완료했을 때 호출되는 콜백 메소드


failed( ) : 작업 처리 도중 예외가 발생했을 때 호출되는 콜백 메소드




- 파일 비동기 채널 -


FileChannel의 read( )와 write( ) 메소드는 파일 입출력 작업 동안 블로킹 된다.

만약 UI 및 이벤트를 처리하는 스레드에서 이 메소드들을 호출하면 블로킹되는 동안에 UI 갱신이나 이벤트 처리를 할 수 없다.

따라서 별도의 작업 스레드를 생성해서 이 메소드들을 호출해야 하는데, 파일의 수가 많으면 스레드의 수도 많아지기 때문에 문제가 될 수 있다.

그렇기 때문에 불특정 다수의 파일 및 대용량 파일의 입출력 작업을 위해 비동기 파일 채널을 제공하고 있다.


 

  - 파일 비동기 채널 파일 입출력 순서 -


  1. 파일의 데이터 입출력을 위해 read( )와  write( ) 메소드를 호출


  2. 스레드풀에게 작업 처리를 요청하고 이 메소드들을 즉시 리턴시킨다.


  3. 비동기채널에서 스레드풀로 작업처리 요청을 한다.


  4. 스레드풀의 작업큐에서 비동기채널로 부터 받은 작업을 실행대기 시킨다.


  5. 각 스레드는 스레드풀의 큐에서 작업을 가져와 입출력을 시킨다.


  6. 작업 스레드가 파일 입출력을 완료하게 되면 콜백 메소드가 자동 호출된다.




 




- AsynchronousFileChannel 생성과 닫기 -

AsynchronousFileChannel는 두가지 정적 메소드인 open( )을 호출해서 얻을 수 있다.
첫번째 open( ) 메소드는 파일의 Path 객체와 열기 옵션 값을 매개값으로 받는다.


  AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open (

     Path file,

     OpenOption...options

  )';



생성된 AsynchronousFileChannel는 내부적으로 생성되는 기본 스레드풀을 이용해서 스레드를 관리한다.

기본 스레드풀의 최대 스레드 개수는 개발자가 지정할 수 없기 때문에 두번째 open( ) 메소드로 AsynchronousFileChannel 를 만들수도 있다.


 

  AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(

        Path file,

        Set<? extends OpenOption> options,

        ExecutorService executor,

        FileAttribute<?>...attrs

  ); 





- 파일 읽기와 쓰기 -

AsynchronousFileChannel이 생성되었다면 read( ), write( ) 메소드를 이용해서 입출력할 수 있다.


  read(ByteBuffer dst, long position, A attachment, CompletionHandler<Integer, A> handler);


  write(ByteBuffer src, long position, A attachment, CompletionHandler<Integer, A> handler);



이 메소드들을 호출하면 즉시 리턴되고, 스레드풀의 스레드가 입출력 작업을 진행한다.


dst와 src 매개값 : 읽거나 쓰기 위한 ByteBuffer

position 매개값 : 파일에서 읽을 위치이거나 쓸 위치이다. 

                       파일의 첫 번째 바이트부터 읽거나 첫 번째 위치에 바이트를 쓰고 싶다면 position을 0으로 주면 된다.

attachment 매개값 : 콜백 메소드로 전달할 첨부 객체.

첨부 객체 : 콜백 메소드에서 결과값 이외에 추가적인 정보를 얻고자 할 때 사용하는 객체이고 필요없다면 null을 대입해도 된다.
handler 매개값 : CompletionHandler<Integer, A> 구현 객체를 지정한다.
Integer : 입출력 작업의 결과 타입으로, read( ) 와 write( ) 가 읽거나 쓴 바이트 수이다.

A는 첨부 객체 타입으로 개발자가 CompletionHandler 구현 객체를 작성할 때 임의로 지정이 가능하다. 만약 첨부 객체가 필요 없다면 A는 Void가 된다.

CompletionHandler<Integer, A> 구현 객체는 비동기 작업이 정상적으로 완료된 경우와 예외 발생으로 실패된 경우에 자동 콜백되는 다음 두 가지
메소드를 가지고 있어야 한다.

리턴타입 

 메소드명 (매개 변수)

 설명

void

 completed(Integer result, A attachment)

 작업이 정상적으로 완료된 경우 콜백

void

 failed(Throwable exc, A attachment)

 예외 때문에 작업이 실패된 경우 콜백




- 예제 및 출력 결과 (파일 생성 및 쓰기) -



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package com.hs.chap19;
 
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class AsynchronousFileChannelWriteExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool( // cpu코어의 수 만큼 스레드풀을 생성한다.
                Runtime.getRuntime().availableProcessors() // cpu코어의 수를 리턴
        );
 
        for (int i = 0; i < 10; i++) {
            Path path = Paths.get("C:/Temp/file" + i + ".txt");
            Files.createDirectories(path.getParent()); // 해당 디렉터리의 경로에 디렉터리(폴더)를 생성한다.
 
            // 비동기 파일 채널 생성
            AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open( // 정적 메소드인 open()을 비동기채널을 만든다.
                    path, // 작업해야될 파일의 경로
                    EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE), // 열기옵션(생성하고 쓸수있게)들을 담아서 리턴한다.
                    executorService // 위에서 만든 스레드풀
            );
 
            Charset charset = Charset.defaultCharset();
            ByteBuffer byteBuffer = charset.encode("안녕하세요."); // encode()메소드를 사용해 문자열을 인코딩해 Buffer에 넣는다.
 
            // 첨부 객체 생성 (콜백 메소드에서 결과값 이외에 추가적으로 보여주고싶은 값)
            class Attachment {
                Path path; // 파일의 경로를 저장
                AsynchronousFileChannel fileChannel; // 비동기 파일 채널을 저장
            }
            Attachment attachment = new Attachment();
            attachment.path = path;
            attachment.fileChannel = fileChannel;
 
            CompletionHandler<Integer, Attachment> completionHander = new CompletionHandler<Integer, Attachment>() { // 콜백
                                                                                                                        // 메소드를
 
                @Override
                public void completed(Integer result, Attachment attachment) { // 작업을 정상 처리 완료했을때 호출되는 콜백 메소드.
                    System.out.println(attachment.path.getFileName() + ":" + result + "bytes written :"
                            + Thread.currentThread().getName());
                    // 첨부객체의 파일의 이름을 출력하고, 결과값, 현재 실행중인 스레드의 이름을 출력한다.
                    try {
                        attachment.fileChannel.close();
                    } catch (IOException e) {
                    }
                }
 
                @Override
                public void failed(Throwable exc, Attachment attachment) {
                    exc.printStackTrace();
                    try {
                        attachment.fileChannel.close();
                    } catch (IOException e) {
                    }
 
                }
 
            };
 
            // 파일 읽기
            fileChannel.write(byteBuffer, 0, attachment, completionHander);
            // (읽은데이터를 저장할 버퍼 객체, 버퍼인덱스, 첨부 객체, 콜백 메소드를 가진 객체)
 
            // 스레드풀 종료
            Thread.sleep(1000);
            executorService.isShutdown();
        }
    }
}
 
cs





- 예제 및 출력 결과 (파일 읽기) -



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
ppackage com.hs.chap19;
 
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class AsynchronousFileChannelWriteExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool( // cpu코어의 수 만큼 스레드풀을 생성한다.
                Runtime.getRuntime().availableProcessors() // cpu코어의 수를 리턴
        );
 
        for (int i = 0; i < 10; i++) {
            Path path = Paths.get("C:/Temp/file" + i + ".txt"); // 해당 경로에있는 파일을 읽는다.
        
 
            // 비동기 파일 채널 생성
            AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open( // 정적 메소드인 open()을 비동기채널을 만든다.
                    path, // 작업해야될 파일의 경로
                    EnumSet.of(StandardOpenOption.READ), // 열기옵션(파일을 읽을수 있게)을 담아서 리턴한다.
                    executorService // 위에서 만든 스레드풀
            );
            
            //파일의 크기와 동일한 capacity를 갖는 버퍼 생성
            ByteBuffer byteBuffer = ByteBuffer.allocate((int)fileChannel.size());
            
 
            // 첨부 객체 생성 (콜백 메소드에서 결과값 이외에 추가적으로 보여주고싶은 값)
            class Attachment {
                Path path; // 파일의 경로를 저장
                AsynchronousFileChannel fileChannel; // 비동기 파일 채널을 저장
                ByteBuffer byteBuffer;
            }
            Attachment attachment = new Attachment();
            attachment.path = path;
            attachment.fileChannel = fileChannel;
            attachment.byteBuffer = byteBuffer;
            
            //CompletionHandler 객체 생성
            CompletionHandler<Integer, Attachment> completionHanderlernew = new CompletionHandler<Integer, Attachment>() { // 콜백
                                                                                                                        // 메소드를
 
                @Override
                public void completed(Integer result, Attachment attachment) { 
                    attachment.byteBuffer.flip(); // 작업을 정상 처리 완료했을때 호출되는 콜백 메소드.
                    Charset charset = Charset.defaultCharset();
                    String data = charset.decode(attachment.byteBuffer).toString();
                    
                    System.out.println(attachment.path.getFileName() + ":" + data + ":"
                            + Thread.currentThread().getName());
                    // 첨부객체의 파일의 이름을 출력하고, 결과값, 현재 실행중인 스레드의 이름을 출력한다.
                    try {
                        fileChannel.close();
                    } catch (IOException e) {
                    }
                }
 
                @Override
                public void failed(Throwable exc, Attachment attachment) {
                    exc.printStackTrace();
                    try {
                        fileChannel.close();
                    } catch (IOException e) {
                    }
 
                }
 
            };
 
            // 파일 읽기
            fileChannel.read(byteBuffer, 0, attachment, completionHanderlernew);
            // (읽은데이터를 저장할 버퍼 객체, 버퍼인덱스, 첨부 객체, 콜백 메소드를 가진 객체)
 
            // 스레드풀 종료
            executorService.isShutdown();
        }
    }
}
 
cs



:

NIO 기반 입출력 및 네트워킹-1

Back-End/Java 2019. 4. 23. 15:33

- NIO(New Input/Output) -


새로운 입출력 방식이라는 뜻에서 java.nio 패키지가 포함되었는데, 자바 7로 버전업하면서 자바 IO와 NIO 사이의 일관성 없는 클래스 설계를 바로잡고,

비 동기 채널 등의 네트워크 지원을 대폭 강화한 NIO.2 API가 추가되었다.





- IO와 NIO의 차이점 -


I0와 NIO는 데이터를 입출력한다는 목적은 동일하지만, 방식에 있어서 크게 차이가 난다.
아래 표는 IO와 NIO의 차이점을 정리한 것이다.


 구분

 IO

NIO 

 입출력 방식

 스트림 방식 (입력과 출력이 구분되어 효율 떨어짐)

 채널방식 (양방향으로 입력과 출력이 가능)

 버퍼 방식

 넌버퍼(non-buffer) (속도 느림)

 버퍼(buffer) (속도 빠름)

 비동기 방식

 지원 안 함

지원

 블로킹 / 넌블로킹 방식

 블로킹 방식만 지원

 블로킹 / 넌블로킹 방식 모두 지원




- IO와 NIO의 선택 -


- NIO -


불특정 다수의 클라이언트 연결 또는 멀티 파일들을 넌블로킹이나 비동기로 처리할 수 있기 때문에 과도한 스레드 생성 방지 및 효과적 재사용 가능

또한 운영체제의 버퍼를 이용한 입출력이 가능하기 때문에 입출력 성능이 향상

NIO는 좀 더 다양한 파일의 속성 정보를 제공해주는 클래스와 인터페이스를  java.nio.file,  java.nio.file.attribute 패키지에서 제공하고 있다.




- IO -


대용량 데이터를 처리할 경우에는 IO가 더 유리.

NIO는 모든 작업에 버퍼를 무조건 사용하기 때문에 복잡하므로 받은즉시 처리하는 IO를 사용.

연결 클라이언트 수가 적고, 전송되는 데이터가 대용량이면서 순차적으로 처리될 필요성이 있을 경우에는 IO로 서버를 구현하는 것이 좋다.

파일 속성 정보를 읽기 위해 File 클래스만 제공




-경로 정의(Path)-


NIO의 API에서 파일의 경로를 지정하기 위해 Path를 사용한다.

Path 구현 객체를 얻기 위해서는 java.nio.file.Paths 클래스의 정적 메소드인 get() 메소드를 호출하면 된다.



  Path path = Paths.get (String first, String...more)


  Path path = Paths.get(URI uri);




-예제 및 출력결과-


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.hs.chap19;
 
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
 
public class PathExample {
 
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("src/sec02/exam01_path/PathExample.java"); // get() 메소드를 사용해 파일을 가져온다.
        System.out.println("[파일명]" + path.getFileName()); // getFileName() 메소드를 사용해 파일의 이름을 가져와 출력한다.
        System.out.println("[부모 디렉터리명]: " + path.getParent().getFileName()); // getParent().getFileName()메소드를 사용해 부모
                                                                                // 디렉터리이름을 출력 한다.
        System.out.println("중첩 경로수 : " + path.getNameCount()); // 파일의 이름의 개수(파일의 이름의 개수는 경로의 수)를 출력한다.
 
        System.out.println();
        for (int i = 0; i < path.getNameCount(); i++) {
            System.out.println(path.getName(i)); // 파일의 경로를 하나씩 출력한다.
        }
 
        System.out.println();
        Iterator<Path> iterator = path.iterator(); // 반복자 메소드를 사용
        while (iterator.hasNext()) { // hasNext()메소드를 사용해 다음 디렉터리가 없을때 까지 출력
            Path temp = iterator.next();
            System.out.println(temp.getFileName()); //getFileName() 메소드를 사용해 얻은 디렉터리의 이름을 출력한다.
        }
    }
 
}
 
cs




- 파일 시스템 정보 (FileSystem) -


운영체제의 파일 시스템은 FileSystem 인터페이스를 통해서 접근할 수 있다.

FileSystem 구현 객체는 FileSystem의 정적 메소드인 getDefault( )로 얻을 수 있다.



  FileSystem fileSystem = FileSystems.getDefault( );

 


리턴 타입 

 매소드(매개 변수)

 설명

 Iterable<FileStore>

 getFileStores( )

 드라이버 정보를 가진 FileStore 객체들을 리턴

 Iterable<Path>

 getRootDirectories( )

 루트 디렉토리 정보를 가진 Path 객체들을 리턴

 String

 getSeparator( )

 디렉토리 구분자 리턴





-FileStore-


드라이버(컴퓨터 드라이버, ex- 그래픽드라이버 같은것) 를 표현한 객체.



리턴 타입

 메소드 (매개 변수)

 설명

 long

 getTotalSpace( )

 드라이버 전체 공간 크기 (바이트 단위) 리

 long

 getUnallocatedSpace( )

 할당되지 않은 공간 크기 (바이트 단위) 리턴

 long

 getUsableSpace( )

 사용 가능한 공간 크기,getUnallocatedSpace( ) 동일

 boolean

 isReadOnly( )

 읽기 전용 여부

 String

 name( )

 드라이버명 리턴

 String

 type( )

 파일 시스템 종류




-속성 이란?-


파일이나 디렉토리가 숨김인지, 디렉토리인지, 크기가 어떻게 되는지, 소유자가 누구인지에 대한 정보를 말한다.

다음은 java.nio.file.Files 클래스가 제공하는 정적 메소드들이다. (너무 많아서 전부다는 안쓰고 헷갈리는것들만 적음)



리턴 타입

 메소드(매개 변수)

설명 

Path

createDirectories( ) 

모든 부모 디렉토리 생성

Path 

createDirectory( ) 

경로의 마지막 디렉토리만 생성

FileStore 

getFileStore( ) 

파일이 위치한 FileStore(드라이브) 리턴

BufferedReader

newBufferedReader( ) 

텍스트 파일을 읽는 BufferedReader 리턴 

BufferedWriter 

newBufferedWriter( ) 

텍스트 파일에 쓰는 BufferedWriter 리턴 

boolean 

notExists( ) 

존재하지 않는지 여부 

String 

probeContentType( ) 

파일의 MIME 타입을 리턴 

byte[ ] 

readAllBytes( ) 

파일의 모든 바이트를 읽고 배열로 리턴 

List<String> 

readAllLines( )

텍스트 파일의 모든 라인을 읽고 리턴






-예제 및 출력결과-


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.hs.chap19;
 
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
 
public class DirectoryExample {
 
    public static void main(String[] args) throws Exception {
        Path path1 = Paths.get("C:/Temp/dir/subdir"); // 디렉토리 주소를 얻는다.
        Path path2 = Paths.get("C:/Temp/file.txt"); // 파일 주소를 얻는다.
 
        if (Files.notExists(path1)) // notExists 메소드를 사용해 path1 경로에 디렉토리가 존재하지 않는지 확인
        {
            Files.createDirectories(path1); // 디렉토리의 경로가 존재하지 않으면 createDirectories() 메소드를 사용해 path1 경로에 디렉토리를 생성한다.
        }
 
        if (Files.notExists(path2)) // notExists 메소드를 사용해 path2 경로에 파일이 존재하지 않는지 확인
        {
            Files.createFile(path2); // 디렉토리의 경로가 존재하지 않으면 createDirectories() 메소드를 사용해 path2 경로에 파일을 생성한다.
        }
 
        Path path3 = Paths.get("C:/Temp"); // 폴더의 주소를 얻는다.
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path3); // newDirectoryStream() 메소들 사용해서 Temp
                                                                                    // 디렉토리 내용을 스트림으로 얻는다.
        for (Path path : directoryStream) // 얻는 스트림을 path에 하나씩 집어넣는다.
        {
            if (Files.isDirectory(path)) {
                System.out.println("[디렉토리]" + path.getFileName()); // 디렉토리의 이름을 순차적으로 출력한다.
            } else {
                System.out.println("[파일]" + path.getFileName() + "(크기: " + Files.size(path) + ")"); // 디렉토리의 이름을 출력한 후에
                                                                                                    // 파일의 크기를 출력한다.
            }
        }
    }
}
 
cs





- 와치 서비스(WatchService) -


자바7에서 처음 소개된 것으로 디렉토리 내부에서 파일 생성, 삭제, 수정 등의 내용 변화를 감시하는데 사용된다.

와치 서비스의 적용 예는 에디터에서 파일을 편집하고 있을 때, 에디터 바깥에서 파일 내용을 수정하게 되면 파일 내용이 변경됐으니

다시 불러올 것인지 묻는 대화상자를 띄우는 것이다.

WatchService를 생성하려면 FileSystem의 newWatchService( ) 메소드를 호출하면 된다.



  WatchService watchService = FileSystems.getDefault( ).newWatchService( );

 




 

  * 와치 서비스 사용법 *



 1. FileSystem의 newWatchService( ) 메소드를 호출해서 WatchService 생성.


 2. 감시가 필요한 디렉토리의 path객체에서 register( ) 메소드로 WatchService 를 등록.


 3. 어떤 변화(생성, 삭제, 수정)를 감시할 것인지를 Standard WatchService 상수로 저장.


 4. 디렉토리에 WatchService 를 등록한 순간부터 WatchEvent가 발생하고, WatchService는 해당 이벤트 정보를 가진 와치키(WatchKey)를

생성하여 큐에 저장


 5. 프로그램이 무한 루프를 돌면서 WatchService의 take() 메소드를 호출하여 WatchKey가 큐에 들어올 때까지 대기하고 있다가 WatchKey가

    큐에 들어오면 WatchKey를 얻는다.


 6. WatchKey를 얻고나서 pollEvents( ) 메소드를 호출해서 WatchEvent 리스트를 얻어낸다.


 7. 프로그램은 WatchEvent 리스트에서 WatchEvent를 하나씩 꺼내어 이벤트의 종류와 Path 객체를 얻어낸 다음 적절히 처리하면 된다.




- JNI(Java Native Interface) -


자바 코드에서 C함수를 호출할 수 있도록 해주는 API이다.




- 버퍼 -


Buffer 종류

Buffer은 저장되는 데이터 타입에 따라 분류될 수 있고, 어떤 메모리를 사용하느냐에 따라 다이렉트(Direct)와 넌다이렉트(NonDirect)로 분류할 수도 있다.


Buffer 클래스 allocate( ), wrap( )  메소드 = 넌다이렉트 버퍼 생성


ByteBuffer의 allocateDirect( ) 메소드 = 다이렉트 버퍼 생성 


구분

넌다이렉트 버퍼

다이렉트 버퍼 

사용하는 메모리 공간 

JVM의 힙 메모리 

 운영체제의 메모리

버퍼 생성 시간

버퍼의 생성이 빠르다.

 버퍼 생성이 느리다.

버퍼의 크기

 작다.

 크다.(큰 데이터를 처리할 때 유리)

입출력 성능

 낮다.

 높다. (입출력이 빈번할 때 유리)



- allocate( ) 메소드 -


JVM 힙 메모리에 넌다이렉트 버퍼를 생성하는 메소드.

다음은 최대 100개의 바이트를 저장하는 ByteBuffer를 생성하고, 최대 100개의 문자를 저장하는 CharBuffer를 생성하는 코드이다.


  ByteBuffer byteBuffer = ByteBuffer.allocate(100);


  CharBuffer charBuffer = CharBuffer.allocate(100);




- wrap( ) 메소드 -


이미 생성되어 있는 자바 배열을 래핑해서 Buffer 객체를 생성한다.

자바 배열은 JVM 힙 메모리에 생성되므로 wrap( )은 넌다이렉트 버퍼를 생성한다.

다음은 길이가 100인 byte[]를 이용해서 ByteBuffer를 생성하고, 길이가 100인 char[]를 이용해서 CharBuffer를 생성한다.




  byte[] byteArray = new byte[100];


  ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);


  char[] charArray = new char[100];


  CharBuffer charBuffer = CharBuffer.wrap(charArray);





- allocateDirect( ) 메소드 -


JVM 힙 메모리 바깥쪽, 즉 운영체제가 관리하는 메모리에 다이렉트 버퍼를 생성한다.

Buffer 클래스에는 없고, ByteBuffer에서만 제공된다.


다음은 100개의 바이트(byte) 를 저장하는 다이렉트 ByteBuffer과 50개의 문자(char)를 저장하는 다이렉트 CharBuffer, 25개의 정수(int)를 저장하는

다이렉트 IntBuffer를 생성하는 코드이다. char은 2바이트 크기를 가지고, int는 4바이트 크기를 가지기 때문에 초기 다이렉트 ByteBuffer 생성 크기에

따라 저장 용량이 결정된다.



 

  ByteBuffer byteBuffer = ByteBuffer.allocateDriect(100);    //100개의 byte값 저장 (char은 2바이트 크기, int는 4바이트 크기를 가짐)


  CharBuffer charBuffer = ByteBuffer.allocateDirect(100).asCharBuffer( );   //50개의 char값 저장 (100개의 char값을 변환한 값)


  IntBuffer intBuffer = ByteBuffer.allocateDirect(100).asIntBuffer( );         //25개의 int값 저장 (50개의 char값을 변환한 값)





-예제 및 출력결과-


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.hs.chap19;
 
import java.nio.ByteBuffer;
 
public class BufferSizeExample {
    public static void main(String[] args) {
        ByteBuffer directBuffer = ByteBuffer.allocateDirect(200 * 1024 * 1024); // allocateDirect(버퍼크기 설정)버퍼 크기 설정해서
                                                                                // 다이렉트 버퍼 생성
        System.out.println(directBuffer);
        System.out.println("다이렉트 버퍼가 생성되었습니다.");
 
        ByteBuffer nonDirectBuffer = ByteBuffer.allocate(200 * 1024 * 1024); // allocate(버퍼크기 설정)버퍼 크기 설정해서 넌다이렉트 버퍼 생성
        System.out.println(nonDirectBuffer);
        System.out.println("넌다이렉트 버퍼가 생성되었습니다.");
 
    }
}
 
 
cs




- byte 해석 순서(ByteOrder) -


데이터를 처리할 때 바이트 처리 순서는 운영체제마다 차이가 있다. 이러한 차이는 데이터를 다른 운영체제로 보내거나 받을 때

영향을 미치기 때문에 데이터를 다루는 버퍼도 이를 고려해야 한다.

앞쪽 바이트부터 먼저 처리하는 것을 Big endian 이라고 하고, 뒤쪽 바이트부터 먼저 처리하는 것을 Little endian이라고 한다.




- Buffer의 위치 속성 -

-속성 크기 관계-


  0 <= mark <= position <= limit <= capacity 



속성 

설명 

 position

현재 읽거나 쓰는 위치값이다. 인덱스 값이기 때문에 0부터 시작하며, limit 보다 큰 값을 가질 수 없다.

만약 position과 limit의 값이 같아진다면 더 이상 데이터를 쓰거나 읽을 수 없다는 뜻이 된다.

 limit

버퍼에서 읽거나 쓸 수 있는 위치의 한계를 나타낸다. 이 값은 capacity보다 작거나 같은 값을 가진다.

최초에 버퍼를 만들었을 때는 capacity와 같은 값을 가진다,

 capacity

버퍼의 최대 데이터 개수(메모리 크기)를 나타낸다. 인덱스 값이 아니라 수량임을 주의하자.

 mark

reset( ) 메소드를 실행했을 때에 돌아오는 위치를 지정하는 인덱스로서 mark( ) 메소드로 지정할 수 있다.

주의할 점은 반드시 position 이하의 값으로 지정해주어야 한다.

position이나 limit의 값이 mark 값보다 작은 경우, mark는 자동 제거된다.

mark가 없는 상태에서 reset( ) 메소드를 호출하면 InvalidMarkException이 발생한다.


 

 

  flip( ) 메소드 : 현재 position의 위치를 limit로 설정하고, position을 0번 인덱스로 설정한다. 


  mark( ) 메소드 : 현재 position의 위치를 기억시켜 놓는다.


  reset( ) 메소드 : position을 mark가 있는 인덱스로 이동시킨다. (mark가 없는 상태에서 호출하면 예외발생-기억한 위치가 없기 때문)


  rewind( ) 메소드 : limit는 변하지 않지만 position은 0번 인덱스로 다시 설정된다.


  clear( ) 메소드 : Buffer의 세가지 속성이 초기화 된다. (limit는 capacity (버퍼의 최대 메모리값)으로, position은 0으로 설정되고, mark는 없어짐)

                       하지만 데이터는 삭제되지 않는다.


  compact( ) 메소드 : 현재 position에서 limit 사이의 데이터가 0번 인덱스로 복사 되고 현재 position은 복사된 데이터 다음 위치로 이동한다.

                            (예를들어 flip() 메소드 호출 후 세 개의 데이터를 읽고 다음과 같이 position이 3번 인덱스 위치에 있을 때 compact( )가

                             호출되면 3번 인덱스와 4번 인덱스의 데이터는 0번 인덱스와 1번 인덱스로 복사되고 position 2번 인덱스로 이동한다.)

                             주의할 점은 0번과 1번 인덱스를 제외한 나머지 인덱스의 데이터는 삭제되지 않고 남아있다.

                             compact( ) 메소드를 호출하는 이유는 읽지 않은 데이터 뒤에 새로운 데이터를 저장하기 위해서이다.                             



 

 

- 데이터를 읽고 저장하는 메소드 -

 

 

  put( ) 메소드 : 버퍼에 데이터를 저장하는 메소드

 

  get( ) 메소드 : 버퍼에 있는 데이터를 읽는 메소드

 

 

이 두가지 메소드는 상대적과 절대적으로 구분된다.

 

버퍼 내의 현재 위치 속성인 position에서 데이터를 읽고, 저장할 경우는 상대적이고, position과 상관없이 주어진 인덱스에서

데이터를 읽고, 저장할 경우는 절대적이다.

 

상대적 get( )과 put( ) 메소드를 호출하면 position의 값은 증가하지만, 절대적 get( )와 put( ) 메소드를 호출하면

position의 값은 증가되지 않는다.

 

만약 position 값이 limit 값까지 증가했는데도 상대적 get( )을 사용하면 BufferUnderflowException 예외가 발생하고,

put( ) 메소드를 사용하면 BufferOverflowException 예외가 발생한다.

 

상대적 메소드와 절대적 메소드를 쉽게 구분하는 방법은 index 매개 변수가 없으면 상대적이고, index 매개 변수가 있으면 절대적이다.

 

 

 

- 버퍼 예외의 종류 -

 

주로 버퍼가 다 찼을 때 데이터를 저장하려는 경우와 버퍼에서 더 이상 읽어올 데이터가 없을 때 데이터를 읽으려는 경우에 예외가 발생

 

 예외

 설명

 BufferOverflowException

 position이 limit에 도달했을 때 put( )을 호출하면 발생

 BufferUnderflowException

 position이 limit에 도달했을 때 get( )을 호출하면 발생

 InvalidMarkException

 mark가 없는 상태에서 reset( ) 메소드를 호출하면 발생

 ReadOnlyBufferException

읽기 전용 버퍼에서 put( ) 또는 compact( ) 메소드를 호출하면 발생

 

 

-예제 및 출력 결과-

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.hs.chap19;
 
import java.nio.ByteBuffer;
 
public class BufferExample {
    public static void main(String[] args) {
        System.out.println("[7바이트 크기로 버퍼 생성]");
        ByteBuffer buffer = ByteBuffer.allocateDirect(7); // allocateDirect()메소드로 7바이트 크기로 다이렉트 버퍼 생성
        printState(buffer); // 버퍼에 인덱스를 매개값으로 받는 메소드 선언
 
        buffer.put((byte10); // put()메소드를 사용해 버퍼에 10바이트를 저장, 0번 인덱스에 값 저장
        buffer.put((byte11); // put()메소드를 사용해 버퍼에 11바이트를 저장, 1번 인덱스에 값 저장
        System.out.println("[2바이트 저장후]");
        printState(buffer); // 인덱스 매개변수가 있으므로 상대적 저장임
 
        buffer.put((byte12); // put() 메소드를 사용해 버퍼에 12바이트를 저장, 2번 인덱스에 값 저장
        buffer.put((byte13); // put() 메소드를 사용해 버퍼에 13바이트를 저장, 3번 인덱스에 값 저장
        buffer.put((byte14); // put() 메소드를 사용해 버퍼에 14바이트를 저장, 4번 인덱스에 값 저장
        System.out.println("[3바이트 저장후]");
        printState(buffer); // 인덱스 매개변수가 있으므로 상대적 저장
 
        buffer.flip(); // flip()메소드를 사용해 현재 position의 위치를 limit로 설정하고, position을 0번 인덱스로 설정
        System.out.println("[flip() 실행후]");
        printState(buffer); // 출력값 position : 0, limit: 5 으로 예상됨
 
        buffer.get(new byte[3]); // get() 메소드를 사용해 3바이트를 읽음
        System.out.println("[3바이트를 읽은 후]");
        printState(buffer); // 상대적 읽기
 
        buffer.mark(); // 현재 position위치를 저장해놓음 (현재 3번 인덱스)
        System.out.println("--------[현재 위치를 마크 해놓음]---------");
 
        buffer.get(new byte[2]); // get()메소드를 사용해 2바이트를 읽음 (현재위치 5번 인덱스)
        System.out.println("[2바이트 읽은후]");
        printState(buffer);
 
        buffer.reset(); // reset() 메소드를 사용해 mark위치로 position을 옮김 (현재 3번 인덱스)
        System.out.println("-----[position을 마크 위치로 옮김]");
        printState(buffer);
 
        buffer.rewind(); // rewind()메소드를 사용하여 position을 0번으로 이동 (현재 0번 인덱스)
        System.out.println("[rewind() 실행후]");
        printState(buffer);
 
        buffer.clear(); // clear() 메소드를 사용해 위치 값을 초기화 (position,limit,capacity 즉 처음 버퍼 만들었을 때로 돌아감)
        System.out.println("[clear() 실행후]");
        printState(buffer);
    }
 
    private static void printState(ByteBuffer buffer) { // 버퍼의 인덱스를 매개값으로 받아 position, limit, capacity를 출력하는 메소드
        System.out.println("position: " + buffer.position() + ", ");
        System.out.println("limit: " + buffer.limit() + ", ");
        System.out.println("capacity: " + buffer.capacity() + ", ");
    }
}
 
cs

 

 

 

 

- Buffer 변환 -

 

채널이 데이터를 읽고 쓰는 버퍼는 모두 ByteBuffer이다.

그렇기 때문에 채널을 통해 읽은 데이터를 복원하려면 ByteBuffer를 문자열 또는 다른 타입 버퍼( CharBuffer, IntBuffer, LongBuffer 등 )

반대로 문자열 또는 다른 타입의 버퍼의 내용을 채널을 통해 쓰고 싶다면 ByteBuffer로 변환해야 한다.

 

 

  ByteBuffer <-- String 변환

 

  채널을 통해 문자열을 파일이나 네트워크로 전송하려면 특정문자셋(UTF-8, EUC-KR)으로 인코딩해서 ByteBuffer로 변환해야 한다.

  문자셋을 표현하는 java.nio.charset.Charset 객체는 두 가지 방법으로 얻을 수 있다.

 

 

  Charset charset = Charset.forName("UTF-8");   //매개값으로 주어진 문자셋

 

  Charset charset = Charset.defaultCharset( );    //운영체제가 사용하는 디폴트 문자셋

 

 

  Charset를 이용해서 문자열을 ByteBuffer로 변환하려면 다음과 같이 encode( ) 메소드를 호출 하면 된다.

 

  String data = ...;

 

  ByteBuffer byteBuffer = charset.encode(data);

 

 

 

 

  ByteBuffer --> String 변환

 

  반대로 파일이나 네트워크로부터 읽은 ByteBuffer이 특정 문자셋으로 인코딩되어 있을 경우, 해당 문자셋으로 디코딩해야만

  문자열로 복원할 수 있다.

  Charset는 ByteBuffer를 디코딩해서 CharBuffer로 변환시키는 decode( ) 메소드를 제공하고 있기 때문에 쉽게 문자열로 복원할 수 있다.

 

 

  ByteBuffer byteBuffer = ...;

 

  String data = charset.decode(byteBuffer).toString( );

 

 

 

-예제 및 출력결과-

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.hs.chap19;
 
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
 
public class ByteBufferToStringExample {
    public static void main(String[] args) {
        Charset charset = Charset.forName("UTF-8"); // forName() 메소드의 매개값인 문자셋으로 Charset 객체를 얻는다.
 
        // 문자열 -> 인코딩 -> ByteBuffer
        String data = "안녕하세요";
        ByteBuffer byteBuffer = charset.encode(data); // charset의 encode메소드로 data에 있는 문자열을 변환한다.
 
        // ByteBuffer -> 디코딩 -> CharBuffer -> 문자열
        data = charset.decode(byteBuffer).toString();// decode 메소드 사용 byteBuffer을 String문자열로 변환한다.
        System.out.println("문자열 복원: " + data);
 
    }
 
}
 
 
cs

 

 

 

 

 

 

  ByteBuffer <-- IntBuffer 변환

 

  int[ ] 배열을 생성하고 이것을 파일이나 네트워크로 출력하기 위해서는 int[ ] 배열 또는 IntBuffer로 부터

  ByteBuffer을 생성해야 한다.

  int 타입은 4byte 크기를 가지므로 int[] 배열 크기 또는 IntBuffer의 capacity보다 4배큰 capacity를 가진 ByteBuffer을 생성하고, 

  ByteBuffer의 putInt() 메소드로 정수값을 하나씩 저장하면 된다.

  다음은 int[] 배열을 IntBuffer로 래핑하고, 4배 큰 ByteBuffer를 생성한 후 정수값을 저장한다.

  주의할 점은 putInt() 메소드는 position을 이동시키기 때문에 모두 저장한 후에는 position을 0으로 되돌려 놓는 flip() 메소드를 호출해야 한다.

 

  int[ ] data = new int[ ] {10,20};

  IntBuffer intBuffer = IntBuffer.wrap(data);

  ByteBuffer byteBuffer = ByteBuffer.allocate(intBuffer.capacity()*4);

  for(int i = 0; i < intBuffer.capacity( ); i++) {

  byteBuffer.putInt(intBuffer.get(i);

  }

  byteBuffer.flip( );

 

 

 

 

 

  ByteBuffer --> IntBuffer 변환

 

  반대로 파일이나 네트워크로부터 입력된 ByteBuffer에 4바이트씩 연속된 int 데이터가 저장되어 있을 경우,

  int[ ] 배열로 복원이 가능하다.

  ByteBuffer의 asIntBuffer( ) 메소드로 IntBuffer를 얻고, IntBuffer의 capacity와 동일한 크기의 int[ ] 배열을 생성한다.

  그리고 IntBuffer의 get( ) 메소드로 int값들을 배열에 저장하면 된다.

 

  ByteBuffer byteBuffer = ...;

  IntBuffer intBuffer = byteBuffer.asIntBuffer( );

  int[ ] data = new int [intBuffer.capacity( )];

  intBuffer.get(data);

 

 

:

19.04.22 IO기반 입출력 및 네트워킹-2

Back-End/Java 2019. 4. 22. 18:00



-보조 스트림-


다른 스트림과 연결되어 여러 가지 편리한 기능을 제공해주는 스트림을 말한다.
보조 스트림을 필터(filter) 스트림이라고도 하는데, 이는 보조 스트림의 일부가 FilterInputStream,
FilterOutputStream의 하위 클래스이기 때문이다.

보조 스트림은 문자 변환, 입출력 성능 향상, 기본 데이터 타입 입출력,
객체 입출력 등의 기능을 제공한다.

보조 스트림을 생성할 때에는 자신이 연결될 스트림을 다음과 같이 생성자의 매개값으로 받는다.

보조스트림 변수 = new 보조스트림(연결스트림)




-문자 변환 보조 스트림-


소스 스트림이 바이트 기반 스트림(InputStream, OutputStream  등) 이면서 입출력 데이터가 문자라면 

Reader과 Writer로 변환해서 사용하는 것을 고려해야한다.




-InputStreamReader-


바이트 입력 스트림에 연결되어 문자 스트림인 Reader로 변환시키는 보조 스트림이다.


Reader reader = new InputStreamReader(바이트입력스트림);




-예제 및 출력결과-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.hs.chap18;
 
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
 
public class InputStreamReadeExample {
    public static void main(String[] args) throws Exception {
        InputStream is = System.in// 시스템으로 부터 입력을 받음
        Reader reader = new InputStreamReader(is); // 입력받은 값을 InputStreamReader메소드를 사용해 문자입력 스트림인 Reader로 변환
 
        int readCharNo;
        char[] cbuf = new char[100]; // cbuf에 100개의 공간을 만듦
        while ((readCharNo = reader.read(cbuf)) != -1// 아까 선언한 readCharNo의 값이 -1과 같지 않으면 (출력할 값이 남아있으면) 문자를 계속 출력
        {
            String data = new String(cbuf, 0, readCharNo);
            System.out.println(data);
        }
 
        reader.close();
    }
 
}
 
cs



-OutputStreamWriter-


바이트 출력 스트림에 연결되어 문자 출력 스트림인 Writer로 변환시키는 보조 스트림이다.

Writer writer = new OutStreamWriter(바이트출력스트림);




-성능 향상 보조 스트림-


프로그램의 실행 입출력이 가장 늦은 장치를 따라간다.
CPU와 메모리가 아무리 뛰어나도 하드디스크의 입출력이 늦어지면 프로그램의 실행 성능은 하드디스크의
처리 속도에 맞춰진다.
프로그램이 입출력 소스와 직접 작업하지 않고 중간에 메모리 버퍼(buffer)와 작업함으로써 실행 성능을
향상시킬 수 있다.




-BufferdInputStream과 BufferedReader-


BufferedInputStream는 바이트 입력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이고,

BufferedReader는 문자 입력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이다.


BufferedInputStream bis = new BufferedInputStrem(바이트입력스트림);


BufferedReader br = new BufferedReader(문자입력스트림);



-예제 및 출력결과-


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.hs.chap18;
 
import java.io.BufferedInputStream;
import java.io.FileInputStream;
 
public class BufferdInputStreamExample {
 
    public static void main(String[] args) throws Exception {
        long start = 0;
        long end = 0;
 
        FileInputStream fis1 = new FileInputStream("C:/forest.jpg");
        start = System.currentTimeMillis(); // fis1을 실행하는데 걸리는 시간 측정 (시간참조 메소드를 사용)
        while (fis1.read() != -1) {
        } // 더이상 읽을값이 없을때까지 출력 (-1이 될때까지)
        end = System.currentTimeMillis(); // 끝나는 시간 측정
        System.out.println("사용하지 않았을 때: " + (end - start) + "ms"); // 끝난시간에서 시작시간을 빼서 총 걸린 시간 측정.
        fis1.close(); // 파일출력이 끝났으므로 종료한다.
 
        FileInputStream fis2 = new FileInputStream("C:/forest.jpg");
        BufferedInputStream bis = new BufferedInputStream(fis2);
        start = System.currentTimeMillis();
        while (bis.read() != -1) {
        }
        end = System.currentTimeMillis();
        System.out.println("사용했을 때: " + (end - start) + "ms");
        bis.close();
        fis2.close();
    }
}
 
cs




-BufferedOutputStream과 BufferedWriter-


BufferedOutputStream은 바이트 출력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이고,

BufferedWriter은 문자 출력 스트림에 연결되어 버퍼를 제공해주는 보조 스트림이다.


BufferedOutputStream bos = new BufferedOutputStream(바이트출력스트림);


BufferedWriter bw = new Buffered(문자출력스트림);




-기본 타입 입출력 보조 스트림-


바이트 스트림은 바이트 단위로 입출력하기 때문에 자바의 기본 데이터 타입인 boolean, char,
short, int, long, float, double 단위로 입출력할 수 없다.
이 메소스들로 입출력할 때는 데이터 타입의 크기가 모두 다르므로 DataOutputStream으로 출력한 데이터를
다시 DataInputStream으로 읽어올 때는 출력한 순서와 동일한 순서로 읽어야 한다.
예를 들어 출력할 때의 순서와 int -> boolean -> double 이라면 읽을 때의 순서도
int -> boolean -> double 이어야 한다.



-예제 및 출력결과-


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.hs.chap18;
 
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
 
public class DataInputOutputStreamExample {
 
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("C:/forest.txt"); // fos에 C:/forest.txt을 출력하는 객체를 만든다.
        DataOutputStream dos = new DataOutputStream(fos); // dos에 fos의 데이터를 출력하는 객체를 만든다.
 
        dos.writeUTF("홍길동"); // write메소드를 사용해 txt에 값을 입력
        dos.writeDouble(5); // write메소드를 사용해 txt에 값을 입력
        dos.writeInt(1); // write메소드를 사용해 txt에 값을 입력
 
        dos.writeUTF("감자바"); // write메소드를 사용해 txt에 값을 입력
        dos.writeDouble(90.3); // write메소드를 사용해 txt에 값을 입력
        dos.writeInt(2); // write메소드를 사용해 txt에 값을 입력
 
        dos.flush();
        dos.close(); // flush()메소드를 사용해 문자가 다 출력되면 잔여값이 남기지 않도록 한다.
                        // close()모든 출력이 종료되면 종료시킨다.
 
        FileInputStream fis = new FileInputStream("C://forest.txt");
        DataInputStream dis = new DataInputStream(fis);
 
        for (int i = 0; i < 2; i++) {
            String name = dis.readUTF();
            double score = dis.readDouble();
            int order = dis.readInt();
            System.out.println(name + " : " + score + " : " + order);
        }
 
        dis.close();
        fis.close();
    }
}
 
cs



-프린트 보조 스트림-


PrintStrem과 PrintWriter은 프린터와 유사하게 출력하는 print(), println()메소드를 가지고 있는
보조 스트림이다.
콘솔 출력 스트림인 System.out이 바로 PrintStream 타입이기 때문에 print(), println() 메소드를
사용할 수 있었다.
다른 보조 스트림과 마찬가지로 연결할 출력 스트림을 생성자의 매개값으로 받는다.


PrintStream ps = new PrintStream(바이트출력스트림);


PrintWriter pw = new PrintWriter(문자출력스트림);



-예제 및 출력결과-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.hs.chap18;
 
import java.io.FileOutputStream;
import java.io.PrintStream;
 
public class PrintStreamExample {
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("C:/forest.txt");
        PrintStream ps = new PrintStream(fos); // 출력 스트림의 변수를 매개값으로 받아옴
 
        ps.println("[프린터 보조 스트림]");
        ps.print("마치");
        ps.println("프린트가 출력하는 것처럼");
        ps.println("데이터를 출력합니다.");
 
        System.out.println();
 
        ps.flush();
        ps.close();
        fos.close();
 
    }
}
 
cs





-printf 메소드-


첫 번째 매개값으로 형식화된 문자열을 지정하고, 두 번째 매개값부터 형식화된 문자열에 들어갈 값을 나열해주면 된다.





-예제 및 출력결과-


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.hs.chap18;
 
import java.util.Date;
 
public class PrintfExample {
 
    public static void main(String[] args) {
        System.out.printf("상품의 가격 %d원/n"123);
        System.out.printf("상품의 가격 %6d원/n"123);
        System.out.printf("상품의 가격 %-6d원/n"123);
        System.out.printf("상품의 가격 %06d원/n"123);
 
        System.out.printf("반지름이 %d인 원의 넓이:%10.2f/n"10, Math.PI * 10 * 10);
 
        System.out.printf("%6d | %-10s | %10s/n"1"홍길동""도적");
 
        Date now = new Date();
        System.out.printf("오늘은 %tY년 %tm월 %td일 입니다/n", now, now, now);
    }
}
 






-객체 입출력 보조 스트림-


객체는 문자가 아니기 때문에 바이트 기반 스트림으로 출력해야 한다.
객체를 출력하기 위해서는 객체의 데이터(필드값)를 일렬로 늘어선 연속적인 바이트로 변경해야 하는데,
이것을 객체 직렬화(serialization)라고 한다.
입력 스트림으로부터 읽어들인 연속적인 바이트를 객체로 복원하는 것을 역직렬화라고 한다.




-ObjectInputStream, ObjectOutputStream-


ObjectInputStream는 바이트 출력 스트림과 연결되어 객체를 직렬화 하는 역할을 하고,
ObjectOutputStream는 바이트 입력 스트림과 연결되어 객체로 역직렬화 하는 역할을 한다.

두가지 모두다 다른 보조 스트림과 마찬가지로
연결할 바이트 입출력 스트림을 생성자의 매개값으로 받는다.


ObjectInputStream ois = new ObjectInputStream(바이트입력스트림);


ObjectInputStream oos = new ObjectOutputStream(바이트출력스트림);


ObjectOutputStream으로 객체를 직렬화하기 위해서는 writeObject()메소드를 사용한다.


oos.writeObject(객체);


반대로 ObjectInputStream의 readObject() 메소드는 입력 스트림에서 읽은 바이트를 역직렬화
해서 객체로 생성한다. readObject() 메소드의 리턴 타입은 Object 타입이기 때문에 객체 원래의
타입으로 변환해야 한다.


객체타입 변수 = (객체타입) ois.readObject();



-예제 및 출력결과-


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.hs.chap18;
 
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
public class ObjectInputOutputStreamExample {
 
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("C:/forest.txt"); // 파일출력스트림 txt를 fos에 저장
        ObjectOutputStream oos = new ObjectOutputStream(fos); // fos의 객체를 받아 객체출력스트림을 oos에 저장
 
        oos.writeObject(new Integer(10)); // 새로운 객체를 만들어서 oos에 저장
        oos.writeObject(new Double(3.14));
        oos.writeObject(new int[] { 1, 2, 3 });
        oos.writeObject(new String("홍길동"));
 
        oos.flush(); // 남은(필요없는부분)이 없도록 출력
        oos.close();
        fos.close();
 
        FileInputStream fis = new FileInputStream("C:/forest.txt"); // 파일입력스트림 txt를 fis에 저장
        ObjectInputStream ois = new ObjectInputStream(fis);
 
        Integer obj1 = (Integer) ois.readObject(); // ois의 객체를 읽어서 Integer타입으로 타입변환해 obj1에 저장
        Double obj2 = (Double) ois.readObject(); // ois의 객체를 읽어서 Double타입으로 타입변환해 obj2에 저장
        int[] obj3 = (int[]) ois.readObject(); // ois의 객체를 읽어서 Double타입으로 타입변환해 obj3에 저장
        String obj4 = (String) ois.readObject(); // ois의 객체를 읽어서 Double타입으로 타입변환해 obj4에 저장
 
        ois.close();
        fis.close();
 
        System.out.println(obj1);
        System.out.println(obj2);
        System.out.println(obj3[0] + "," + obj3[1] + "," + obj3[2]);
        System.out.println(obj4);
 
    }
}
 
cs



s






-직렬화가 가능한 클래스(Serializable)-


자바가 Seralizable 인터페이스를 구현한 클래스만 직렬화할 수 있도록 제한하고 있다.
Serializable 인터페이스는 필드나 메소드가 없는 빈 인터페이스이지만, 객체를 직렬화할 때
private 필드를 포함한 모든 필드를 바이트로 변환해도 좋다는 표시 역할을 한다.


public class XXX implements Serializable { }


또한 직렬화는 필드만 해당하고, 메소드나 생성자는 해당되지 않는다.
하지만 필드선언에 static 또는 transient가 붙어 있을 경우에는 직렬화가 되지 않는다.



-예제 및 출력결과-


1
2
3
4
5
6
7
8
9
10
11
12
package com.hs.chap18;
 
import java.io.Serializable;
 
public class ClassA implements Serializable { // 필드를 직렬화 하기 위해서 Serializable 인터페이스 구현
    int field1;
    ClassB field2 = new ClassB();
    int field3; // static와 transient는 직렬화에서 제외됨
    int field4;
 
}
 
cs

1
2
3
4
5
6
7
8
package com.hs.chap18;
 
import java.io.Serializable;
 
public class ClassB implements Serializable { // 필드를 직렬화 하기 위해서 Serializable 인터페이스 구현
    int field1;
}
 
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.hs.chap18;
 
import java.io.FileInputStream;
import java.io.ObjectInputStream;
 
public class SerializableReader {
 
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("C:/forest.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ClassA v = (ClassA) ois.readObject(); // ois로 읽은 객체를 classA타입으로 타입변환해 v에 저장
        System.out.println("field1: " + v.field1);
        System.out.println("field2.field1: " + v.field2.field1);
        System.out.println("field3: " + v.field3);
        System.out.println("field4: " + v.field4);
    }
}
 
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.hs.chap18;
 
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
 
public class SerializableWriter {
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("C:/forest.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
 
        ClassA classA = new ClassA(); // classA의 객체 생성
        classA.field1 = 1;
        classA.field2.field1 = 2;
        classA.field3 = 3;
        classA.field4 = 4;
        oos.writeObject(classA); // 직렬화해서 파일에 저장
        oos.flush();
        oos.close();
        fos.close();
    }
}
 
cs




-serialVersion UID 필드-

직렬화된 객체를 역직렬화할 때는 직렬화했을 때와 같은 클래스를 사용해야한다.
예외의 내용은 직렬화할 때와 역직렬화할 때 사용된 클래스의 serialVersionUID가 다르다는 것이다.
클래스의 serialVersionUID가 명시적으로 선언되어 있으면 컴파일 시에 serialVersionUID 필드가
추가되지 않기 때문에 동일한 serialVersionUID 값을 유지할 수 있다.



-writeObject()와 readObject() 메소드-

두 클래스가 상속 관계에 있을때 부모 클래스가 Serializable 인터페이스를 구현하고 있으면
자식 클래스는 Serializabled 인터페이스를 구현하지 않아도 자식 객체를 직렬화 하면
부모 필드 및 자식 필드가 모두 직렬화 된다.
하지만 부모 클래스는 그렇지 않다.
부모 클래스의 필드를 직렬화 하고 싶다면 2가지 방법이 있다.
주의할 점은 접근 제한자가 private가 아니면 자동 호출되지 않기 때문에 반드시 private를 붙여주어야 한다.

1. 부모 클래스가 Serializable 인터페이스를 구현하도록 한다.
2. 자식 클래스에서 writeObject()와 readObject() 메소드를 선언해서 부모 객체의 필드를 직접 출력시킨다.

private void writeObject(ObjectOutputStream out) throws IOException {
out.writeXXX(부모필드);   -> 부모 객체의 필드값을 출력함
 .
 .
 .
 .
out.defaultWriteObject();  ->자식 객체의 필드값을 직렬화
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
부모필드 = in.readXXX();   -> 부모 객체의 필드값을 읽어옴
 .
 .
 .
 .
in.defaultReadObject();  ->자식 객체의 필드값을 역직렬화
}



-예제 및 출력 결과-

1
2
3
4
5
6
package com.hs.chap18;
 
public class Parent {
    public String field1;
}
 
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.hs.chap18;
 
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
 
public class Child extends Parent implements Serializable { // 부모 클래스를 상속받고 Serializable 인터페이스를 구현한다.
    public String field2;
 
    private void writeObject(ObjectOutputStream outthrows IOException {
        out.writeUTF(field1); // 부모 객체의 필드 값을 출력한다.
        out.defaultWriteObject();
    }
 
    private void readObject(ObjectInputStream inthrows IOException, ClassNotFoundException {
        field1 = in.readUTF(); // 부모 객체의 필드값을 읽어옴
        in.defaultReadObject();
    }
 
}
 
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.hs.chap18;
 
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
public class NonSerializableParentExample {
    public static void main(String[] args) throws Exception{
        FileOutputStream fos = new FileOutputStream("C:/Temp/Object.dat");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        Child child = new Child();
        child.field1 = "홍길동";
        child.field2 = "홍삼원";
        oos.writeObject(child);
        oos.flush();
        oos.close();
        fos.close();
        
        FileInputStream fis = new FileInputStream("C:/Temp/Object.dat");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Child v = (Child) ois.readObject();
        System.out.println("field1: "+v.field1);
        System.out.println("field2: "+v.field2);
        ois.close();
        fis.close();
 
    }
 
}
 
cs




-네트워크 기초-


네트워크는 여러 대의 컴퓨터를 통신 회선으로 연결한 것을 말한다.
인터넷은 지역 네트워크를 통신 회선으로 연결한 것을 말한다.




-서버와 클라이언트-


서비스를 제공하는 프로그램을 일반적으로 서버(server)이라고 부르고,
서비스를 받는 프로그램을 클라이언트 (client)라고 부른다.
클라이언트는 서비스를 받기 위해 연결을 요청하고, 서버는 연결을 수락하여 서비스를 제공한다.




-InetAddress로 IP주소 얻기-


InetAddress.getLocalHost() 메소드 사용.


InetAddress ia = InetAddress.getLocalHost();


외부 컴퓨터의 도메인 이름을 알고 있다면 다음 두 개의 메소드를 사용하여 InetAddress 객체를
얻으면 된다.


InetAddress ia = InetAddress.getByName(String host);
InetAddress[ ] iaArr = InetAddress.getAllByName(String host);




-TCP 네트워킹-

TCP는 연결 지향적 프로토콜이다.
연결 지향 프로토콜이란, 클라이언트와 서버가 연결된 상태에서 데이터를 주고받는 프로토콜을 말한다.
TCP의 단점은 데이터를 보내기 전에 반드시 연결이 형성되어야 하고, 고정된 통신 선로가 최단선이
아닐 경우 상대적으로 UDP보다 데이터 전송 속도가 느릴 수 있다.



-TCP 서버의 역할-

1. 클라이언트가 연결 요청을 해오면 연결을 수락하는 것. (java.net.ServerSocket 클래스)
2. 연결된 클라이언트와 통신하는 것. (java.net.Socket 클래스)



-ServerSocket와 Socket의 용도-


서버를 개발하려면 우선ServerSocket 객체를 얻어야 한다.
가장 간단한 방법은 생성자에 바인딩 포트를 대입하고 객체를 생성하는 것이다.
다음은 5001번 포트에 바인딩하는 ServerSocket를 생성한다.


ServerSocket serverSocket = new ServerSocket(5001);


다른 방법은 디폴트 생성자로 객체를 생성하고 포트 바인딩을 위해 bind() 메소드를 호출하는것이다.
bind() 메소드의 매개값은 포트 정보를 가진 InetSocketAddress 이다.


ServerSocket serverSocket = new ServerSocket();

serverSocket.bind(new InetSocketAddress(5001));


포트 바인딩이 끝났다면 ServerSocket는 클라이언트 연결 수락을 위해 accept() 메소드를 실행해야 한다.
accept() 메소드는 클라이언트가 연결 요청하기 전까지 블로킹 되는데, 블로킹이란 스레드가 대기 상태가
된다는 뜻이다.


연결된 클라이언트의 IP와 포트 정보를 알고 싶다면 Socket의 getRemoteSocketAddress() 메소드를
호출해서 SocketAddress를 얻으면 된다.


실제 리턴되는 것은 InetSockAddress 객체 이므로 타입 변환할 수 있다.


InetSocketAddress socketAddress = (InetSocketAddress) socket.getRemoteSocketAddress();



-예제 및 출력 결과-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.hs.chap18;
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
 
public class ServerExample {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(); // 서버소켓 객체를 생성
            serverSocket.bind(new InetSocketAddress("localhost"5001)); // bind 메소드 호출해서 로컬호스트 5001번포트에 바인딩하는 서버소켓을
                                                                            // 생성한다.
            while (true) {
                System.out.println("[연결 기다림]");
                Socket socket = serverSocket.accept(); // 서버소켓의 연결 수락을 위해 accept()메소드 실행
                InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();// 소켓의
                                                                                            // getRemoteSocketAddress()메소드를
                                                                                            // 호출해서 소켓주소를 얻은후
                System.out.println("[연결 수락함]" + isa.getHostName()); // InetSocketAddress 타입으로 타입변환을 한후 isa에 저장한다.
 
            }
 
        } catch (Exception e) {
        }
 
        if (!serverSocket.isClosed()) {
            try {
                serverSocket.close();
            } catch (IOException e1) {
            }
 
        }
 
    }
}
 
cs

 

 
 

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.hs.chap18;
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
 
public class ClientExample {
    public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket(); // 소켓을 생성
            System.out.println("[연결 요청]");
            socket.connect(new InetSocketAddress("localhost"5001)); //커넥트 메소드로 5001포트로 연결
            System.out.println("[연결 성공]");
        } catch (Exception e) {
        }
 
        if (!socket.isClosed()) { //소켓이 닫히지 않으면 예외처리 (연결이 되어있을 경우)
            try {
                socket.close();
            } catch (IOException e1) {
            }
 
        }
 
    }
 
}
 
cs

 

 

 

 

-Socket 데이터 통신-

 

클라이언트가 연결 요청(connect()) 하고 서버가 연결 수락(accept()) 했다면, 양쪽의 Socket
객체로부터 각각 입력 스트림(InputStream)과 출력 스트림(OutputStream)을 얻을 수 있다.

다음은 Socket으로 부터 InputStream과 OutputStream을 얻는 코드이다.

 

 

//입력 스트림 얻기


InputStream is = socket.getInputStream();

 


//출력 스트림 얻기


OutputStream os = socket.getOutputStream();

 

 

 

-상대방에게 데이터 보내기-

 

String data = "보낼 데이터";
byte[ ] byteArr = data.getBytes("UTF-8");
OutputStream outputStream = socket.getOutputStream();
outputStream.write(byteArr);
outputStream.flush();

 

 

 

-상대방에게 데이터 받기-

 

byte[ ] byteArr = new byte[100];
InputStream inputStream = socket.getInputStream();
int readByteCount = inputStream.read(byteArr);
String data = new String(byteArr,0,readByteCount,"UTF-8");

 

 

 

-스레드 병렬 처리-

 

연결 수락을 위해 ServerSocket의 accept()를 실행하거나, 서버 연결 요청을 위해 Socket 생성자 또는
connect()를 실행할 경우에는 해당 작업이 완료되기 전까지 블로킹 된다.
결론적으로 ServerSocket과 Socket는 동기방식으로 구동된다.
만약 서버를 실행시키는 main 스레드가 직접 입출력 작업을 담당하게 되면 입출력이 완료될 때까지
다른 작업을 할 수 없는 상태가 된다.
그렇기 때문에 accept(), connect(), read(), write()는 별도의 작업 스레드를 생성해서 병렬적으로
처리하는 것이 좋다.
그렇기 때문에 과도한 스레드 생성을 방지하려면 스레드풀을 사용하는 것이 바람직하다.

 

 

 

-서버 클래스 구조-


1.startServer() 메소드

 

start 버튼을 클릭하면 실행하는 메소드.
ExecutorService 생성, ServerSocket 생성 및 포트 바인딩, 연결 수락 코드가 있다.


2.stopServer() 메소드

 

stop 버튼을 클릭하면 stopServer() 메소드가 실행되는데, stopServer() 메소드에는 연결된
모든 Socket 닫기, ServerSocket 닫기, ExecutorService 종료 코드가 있다.


 

 


-클라이언트 클래스 구조-


1.startCilent() 메소드

 

start 버튼을 클릭하면 실행되는 메소드.
startClient() 메소드에는 Socket 생성 및 연결 요청 코드가 있다.

 


2.stopClient() 메소드

 

stop 버튼을 클릭하거나 서버 통신이 안 될 경우 실행되는 메소드.
stopClient() 메소드에는 Socket를 닫는 close() 메소드 호출 코드가 있다.

 


3.receive() 메소드

 

receive() 메소드는 서버에서 보낸 데이터를 받는 역할을 한다.
이 메소드는 startClient()에서 생성한 작업 스레드상에서 호출된다.

 


4.send(String data) 메소드

 

send(String data) 메소드는 사용자가 메시지를 입력하고 send 버튼을 클릭하면 메시지를 매개값으로
호출한다. send() 메소드는 서버로 메시지를 보내는 역할을 한다.

 

 

 

-UDP 네트워킹-

 

UDP는 비연결 지향적 프로토콜이다. 비연결 지향적이란?
데이터를 주고받을 때 연결 절차를 거치지 않고, 발신자가 일방적으로 데이터를 발신하는 방식이다.
연결 과정이 없기 때문에 TCP 보다는 빠른 전송을 할 수 있지만 데이터 전달의 신뢰성은 떨어진다.

 

 

 

-예제 및 출력결과-

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.hs.chap18;
 
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
 
public class UdpSendExample {
    public static void main(String[] args) throws Exception {
        DatagramSocket datagramSocket = new DatagramSocket(); // DatagramSocket객체를 생성
 
        System.out.println("[발신 시작]");
 
        for (int i = 1; i < 3; i++) {
            String data = "메시지" + i; // 2번 보냄
            byte[] byteArr = data.getBytes("UTF-8"); // 문자열을 UTF-8로 인코딩하고, 배열을 얻는다.
            DatagramPacket packet = new DatagramPacket(byteArr, byteArr.lengthnew InetSocketAddress("localhost"5001)
            // Datagram Packet 생성
            );
            datagramSocket.send(packet); // 패킷을 전송한다.
            System.out.println("[보낸 바이트 수]: " + byteArr.length + "bytes");
 
        }
        System.out.println("[발신 종료]");
 
        datagramSocket.close();
 
    }
 
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.hs.chap18;
 
import java.net.DatagramPacket;
import java.net.DatagramSocket;
 
public class UdpReceiveExample extends Thread {
 
    public static void main(String[] args) throws Exception {
        DatagramSocket datagramSocket = new DatagramSocket(5001); //5001번 포트에서 수신하는 DatagramSocket 객체 생성
 
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("[수신 시작]");
                try {
                    while (true) {
                        DatagramPacket packet = new DatagramPacket(new byte[100], 100); //Datagram Packet를 담을 배열을 만든다.
                        datagramSocket.receive(packet);
 
                        String data = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
                        System.out.println("[받은 내용: " + packet.getSocketAddress() + "]" + data);
                    }
                } catch (Exception e) {
                    System.out.println("[수신 종료]");
                }
            }
        };
        thread.start();
 
        Thread.sleep(10000);
        datagramSocket.close();
 
    }
}
 
cs

 

 

:

mysql 테이블 만들어보기, 참조테이블 만들고, 활용해보기

Back-End/Data Base 2019. 4. 21. 22:00

 

먼저 데이터베이스에서는 자료의 삽입,삭제가 튜플(릴레이션의 행)단위로 이루어지기때문에 모든 속성의 도메인의 원자값을 유지하기 위해서 테이블을

무손실분해(제1 정규화)를 실시한다.

 

(만약, 원자값이 유지되지 않으면 1행에 있는 자료가 삭제될때, 학습목표인 '자바기초언어문법'도 삭제되어 2장,3장,4장,5장에서 학습목표를 확인할수없게

된다.)

 

자바 기초 언어 문법 -> 자바 기초 언어 문법 1, 자바 기초 언어 문법 2, 자바 기초 언어 문법 3, 자바 기초 언어 문법 4, 자바 기초 언어 문법 5로 분해

 

장별 학습 내용 -> 학습 단원, 학습 내용 으로 분해.

 

튜플을 식별할수 있는 기본키는 '학습 단원' 으로 설정함. (이 테이블에서는 모든 속성에 값이 중복된 값이 없으므로 사실 어떤 속성으로 해도 상관없음.)

'학습단원'이 식별하기 쉽고, 자료를 계속 삽입할때 중복값이 나올 염려가 없어서 '학습단원'으로 선정.

 

 

 

 

 

mysql에서 DELTE문을 사용한 결과 튜플단위로 자료가 삭제된다는것을 확인할 수 있었음.

 

 

---------------------------------------------------------------------------------------------------------------------------------------------------------------

'JAVA학습일정' 테이블을 참조하는 테이블을 만들어보기.

 

참조테이블 생성 하고, 외래키 설정(학습단원으로 설정.... 'JAVA학습일정' 테이블의 기본키이기 때문..)

그리고 기존 테이블의 튜플(릴레이션의 행)이 삭제되면 참조 테이블의 튜플(릴레이션의 행)이 연쇄적으로 삭제되게 설정

 

 

 

 

 

DELETE문 사용해서 'JAVA학습일정' 테이블의 학습단원이 2장인 튜플을 삭제한 결과, 참조 테이블의 튜플도

연쇄적으로 삭제되었다는것을 알수있었다.

 

 

 

 

 

참조테이블을 활용해서 검색해보기.

 

'JAVA학습일정' 테이블에서 '학습시간' (JAVA단원별학습시간 테이블의 속성을 활용) 이 '45분' 인 '학습기간' 검색해보기

 

 

 

 

'Back-End > Data Base' 카테고리의 다른 글

19.04.27 데이터베이스에 얽힌 돈 이야기  (0) 2019.04.27
19.04.26 관계형 데이터베이스란?  (0) 2019.04.26
19.04.25 SQL 예제  (0) 2019.04.26
19.04.25 데이터베이스란?  (0) 2019.04.26
나의 Data Base 학습 플래닛  (0) 2019.04.17
: