Posts 학원 #51일차: HTTP, URL, Base64
Post
Cancel

학원 #51일차: HTTP, URL, Base64

com.eomcs.net.ex05

image

connection-oriented

연결 후 데이터 송수신

  • 연결 후에 데이터를 송수신 하기 때문에 데이터 송수신에 대한 신뢰를 보장한다.
  • TCP 통신 방법의 전형적인 예이다.
  • FTP, Telnet, SMTP, POP3, HTTP

클라이언트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Client0110 {
  public static void  mian(String[] args) {
    Socket socket = new Socket("localhost", 8888);
    System.out.println("서버에 연결됨!");
    
    Scanner in = new Scanner(socket.getInputStream());
    PrintStream out = new PrintStream(socket.getOutputStream());
    
    out.println("Hello!");
    System.out.println("데이터 보냄!");
    
    String str = in.nextLine();
    System.out.println("데이터 받음!");
    System.out.println(str);
    
    in.close();
    out.close();
    socket.close();
    System.out.println("서버와 연결을 끊음!");
  }
}

서버의 주소와 포트번호를 Socket 생성자의 파라미터로 넘겨준다. 해당 서버가 있어 대기열에 들어간다면 리턴될 것이고, 없다면 예외가 발생한다. 소켓에 대해서 IO스트림을 꺼내서 도우미 스트림인 Scanner와 PrintStream에 장착한다. 클라이언트가 out.println()메서드를 호출한다. 데이터를 저장할 만큼의 여유분이 랜카드에 있다면 랜카드의 램의 버퍼로 출력한다. 그러면 그 즉시 리턴된다. 즉 서버가 읽을 때까지 기다리지 않는다. 그러면 랜카드 CPU가 네트워크로 그 데이터를 전송한다.

in.nextLine()메서드는 줄바꿈코드를 만날 때까지 기다리고, 만나면 그 앞에 있는 값만 String 객체로 만들어 리턴한다. 즉 그 줄바꿈 코드를 만나기 전까지는 리턴하지 않고 blocking 상태에 놓인다.

서버

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
public class Server 0110 {
  public static void main(String[] args) throws Exception {
    Scanner keyboardScanner = new Scanner(System.in);
    
    System.out.println("서버 실행중...");
    ServerSocket ss = new ServerSocket(8888);
    
    System.out.println("엔터를 치면 대기열에서 기다리고 있는 클라이언트의 소켓을 생성한다.>");
    keyboardScanner.nextLine();
    
    Socket socket = ss.accept();
    System.out.println("클라이언트와 통신할 소켓을 준비하였다!");
    
    Scanner = new Scanner(socket.getInputStream());
    PrintStream out = new PrintStream(socket.getOutputStream());
    
    String str = in.nextLine();
    System.out.println("데이터 수신 완료!");
    
    out.println(str);
    out.println("데이터 송신 완료!");
    
    in.close();
    out.close();
    socket.close();
    System.out.println("클라이언트 연결 끊기!");
    
    ss.close();
    keyboardScanner.close();
  }
}

ServerSocket 생성자에 포트번호를 넘겨준다. 포트번호를 어떤 프로그램에서도 사용하고 있지 않으면 예외가 발생되지 않고 다음 문장으로 넘어간다. 서버는 대기하고 있다가 클라이언트가 연결을 시도하면 대기열에 넣고, 대기열에 들어가는 순간 Socket은 생성된다(리턴된다). 즉 Socket은 accept()를 호출하는 순간에 생성되는 것이 아니다.

연결을 하면 데이터를 주고받을 수 있다.

com.eomcs.net.ex05

conntectionless

비연결

  • 서버와 연결 없이 데이터를 보내고 받을 수 있다.
  • DatagramSocket, DatagramPacket을 사용하여 처리한다.
  • 예) 편지, ping 등
  • 모니터링 프로그램에서 많이 사용한다.

ping은 IP 네트워크를 통해 특정 호스트가 도달할 수 있는지 여부를 테스트하는 데 쓰이는 컴퓨터 네트워크 도구 중 하나이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PS C:\Users\Monica Kim\git\algorithm> ping www.naver.com

Ping www.naver.com.nheos.com [125.209.222.142] 32바이트 데이터 사용:
요청 시간이 만료되었습니다.
요청 시간이 만료되었습니다.
요청 시간이 만료되었습니다.

125.209.222.142에 대한 Ping 통계:
    패킷: 보냄 = 3, 받음 = 0, 손실 = 3 (100% 손실),
Control-C
PS C:\Users\Monica Kim\git\algorithm> ping www.danawa.com

Ping www.danawa.com [119.205.194.11] 32바이트 데이터 사용:
119.205.194.11의 응답: 바이트=32 시간=5ms TTL=51
119.205.194.11의 응답: 바이트=32 시간=17ms TTL=51
119.205.194.11의 응답: 바이트=32 시간=15ms TTL=51
119.205.194.11의 응답: 바이트=32 시간=19ms TTL=51

119.205.194.11에 대한 Ping 통계:
    패킷: 보냄 = 4, 받음 = 4, 손실 = 0 (0% 손실),
왕복 시간(밀리초):
    최소 = 5ms, 최대 = 19ms, 평균 = 14ms

ping을 사용해 네이버와 다나와 connectionless 서버에 접속하고자 하였다. naver로 보낸 데이터는 손실된 반면, 다나와는 데이터를 받고 응답하였다. 네이버 서버가 죽은 것이 아니라, 보통 ping은 해커들이 이 서버가 살아있는 지 아닌 지 확인하기 위해 사용한다. 이에 응답하기 위해서는 CPU를 사용해야 하는데, 네이버같은 서버는 상대편에게 굳이 내가 살아있는지 아닌지 알려줄 필요가 없기 때문에 ping 프로토콜에 따라 전송된 데이터를 받지 않는다. 즉 ping 서버는 닫혀 있다, 실행하지 않는다. 보낸 데이터는 상대편 회사의 허브나 라우터에 도착한 후 연결된 쪽으로 보내는데 더이상 없어서 버려진다.

반면 다나와는 데이터를 받고 다시 데이터를 보냈다. 이를 통해 상대 서버와 내 컴퓨터의 통신 속도를 알 수 있다. 이처럼 ping이라는 프로그램이 connectionless로 데이터를 보내는 대표적인 방식이다.

클라이언트

image

  • DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName(reciever), port)
    • 여기서 InetAddress의 생성자는 default 접근 제한자가 붙어 있어 이를 호출할 수 없다. 그렇다면 어떻게 InetAddress 객체를 packet의 파라미터로 보낼 수 있을까? 이 경우 객체 생성을 위해 다른 도우미 메서드를 사용해야 한다. 여기서는 static 메서드인 getByName()이 그것이다.
    • 이렇게 생성자가 막혔을 때는 바로! 빨리! static 메서드들을 찾아봐라!

image

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
public class Client0210 {
  public static void main(String[] args) throws Exception {
    // connectionless 방식으로 통신을 수행할 소켓 생성
    DatagramSocket socket = new DatagramSocket();

    // 데이터를 받을 상대편 주소와 포트 번호
    String receiver = "localhost";
    int port = 8888;

    // 보낼 데이터를 바이트 배열로 준비
    byte[] bytes = "Hello".getBytes("UTF-8");

    // 보낼 데이터를 패킷에 담는다.
    // => 패킷 = 데이터 + 데이터 크기 + 받는이의 주소 + 받는이의 포트번호
    DatagramPacket packet = new DatagramPacket(
      bytes, // 데이터가 저장될 바이트 배열
      bytes.length, // 전송할 데이터 개수
      InetAddress.getByName(receiver), // 데이터를 받을 상대편 주소
      port // 포트번호
    );

    // 데이터 전송
    socket.send(packet);
    System.out.println("데이터 전송 완료!");

    // 자원 해제
    socket.close();
  }
}

상대편이 네트워크에 연결되었는지 따지지 않고 무조건 데이터를 보낸다. 만약 상대편이 연결되어 있지 않다면, 보낸 데이터는 그 쪽 네트워크에서 버려진다. 즉, 데이터 송수신을 보장하지 않고, 이것을 데이터 송신에 신뢰가 없다고 말한다. 따라서 데이터를 보내도 상대방이 받을 수 있을지 확신할 수 없다. 반면 connection-oriented에서는 서버를 실행하지 않은 상태에서 데이터를 보낼 수 없다.

서버

  • DatagramSocket socket = new DatagramSocket(8888)
    • 데이터 송수신을 담당할 소켓을 먼저 준비한다.
    • 보내는 쪽이나 받는 쪽이나 같은 소켓 클래스를 사용한다 서버 소켓이 따로 없다.
    • 받는 쪽에서는 소켓을 생성할 때 포트번호를 설정한다.
  • DatagramPacket emptyPacket = new DatagramPacket(buf, buf.length)
    • 빈 패킷을 준비한다.
  • socket.recieve(emptyPacket)
    • 빈 패킷을 사용하여 들어온 데이터를 받는다.
    • 데이터를 받을 때까지 리턴하지 않는다.
  • String message = new String(emptyPacket.getData(), 0, emptyPacket.)
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
public class Server0210 {
  public static void main (String[] args) {
    Scanner keyScan = new Scanner(System.in);
    System.out.println("서버 실행 중...");
    System.out.print("소켓 생성 전 잠깐!> ");
    keyScan.nextLine();
    // 데이터 송수신을 담당할 소켓을 먼저 준비한다.
    // => 보내는 쪽이나 받는 쪽이나 같은 소켓 클래스를 사용한다. 
    DatagramSocket socket = new DatagramSocket(8888);
    
    // 받은 데이터를 저장할 버퍼 준비
    byte[] buf = new byte[8196];
    
    // 빈 패킷 준비
    DatagramPacket emptyPacket = new DatagramPacket(buf, buf.length);
    
    System.out.print("데이터를 읽기 전에 잠깐 멈춤> ");
    keyScan.nextLine();
    
    socket.close();
    keyScan.close();
    
    String message = new String(
      emptyPacket.getData(), 
      0, 
      emptyPacket.getLength(), 
      "UTF-8"
    );
    
    System.out.println(message);
    
  }
}

DatagramSocketServer는 없다. DatagramSocket에서 온 것은 DatagramSocket으로 받으면 된다. 상대편에서 데이터를 전송하기 전에 서버에서 DatagramSocket이 이미 생성되어 존재해야 한다. 그래야 데이터 패킷을 받을 수 있다. 만약 클라이언트가 데이터를 보낸 후 서버에서 DatagramSocket을 생성하고 그 후 읽으려면 읽을 수 없다.

웹서버 접속

eomcs.net.ex06

image

image

HTTP

  • Hyper-text Transfer Protocol

  • 고수준의 문서: 문서(text) + 다른 문서의 연결 정보 + 문서 구조

HTTPS

암호화 해서 중간에 가로채도 볼 수 없고, 변경해도 검증할 수 있다.

조작한 데이터를 검증할 수 있는 프로토콜이 HTTPS프로토콜이다.

서버에도 인증서가 있어야 하고, 클라이언트에도 인증서가 있어야 한다.

프로토콜

  • 클라이언트/서버 간의 통신 규칙
  • 데이터를 주고 받는 규칙

ex02>server0110

일반적인 통신 프로그램을 작성할 때 이 범위 포트 번호를 사용해라. => 1024 ~ 49151

모든 웹사이트는 기본적으로 포트번호가 80번이다.

TCP/UDP의 포트 목록

1
2
3
4
5
while (true) {
  try {
    System.out.println(in.nextLine())
  }
}

카카오 채팅 프로토콜도 있다. 그러나 이걸 공개하지 않았다. 그쪽에선 분명히 자기ㅉ꼬 프로그램이 아니면 접근하지 못하게 막을 것이다. 이렇게 비공개 프로토콜이 아니라면, 공개프로토콜이라면 누구나 요청을 보내고 응답을 할 수 있다. http는 공개된 프로토콜이다. 우리도 웹브라우저를 만들 수 있따.

실습

HTTP 클라이언트 만들기

1
2
3
4
5
6
HTTP 요청 프로토콜
---------------------------------
GET [자원주소] HTTP/1.1 (CRLF)
Host: [서버주소] (CRLF)
(CRLF)
---------------------------------
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
public class HttpClient {
  public static void main(String[] args) {
    Socket socket = new Socket("itempage3.auction.co.kr", 80);
    PrintStream out = new PrintStream(socket.getOutputStream());
    Scanner in = new Scanner(socket.getInputStream());
    
    // HTTP 요청 프로토콜에 따라 서버에 데이터 전송
    out.print("GET /DetailView.aspx?itemno=C204190906 HTTP/1.1\r\n");
    out.print("Host: itempage3.auction.co.kr\r\n");
    out.print("\r\n");
    
    // HTTP 응답 프로토콜에 따라 서버가 보낸 데이터를 수신
    while (true) {
      try {
        System.out.println(in.nextLine());
      } catch (Exception e) {
        break;
      }
    }
    
    out.close();
    in.close();
    socket.close();
  }
}

macOS에서 JVM을 실행할 때 println()은 문자열 뒤에 0a(LF) 코드만 붙인다. 이를 해결하려면 위와 같이 명확하게 CRLF 코드를 붙여야 한다.

HTTP 서버 만들기

1
2
3
4
5
6
7
HTTP 응답 프로토콜
--------------------------------
HTTP/1.1 200 OK(CRLF)
Content-Type: text/html; charset=UTF-8(CRLF)
(CRLF)
보낼 데이터
--------------------------------
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
public class HttpServer {
  public static void main(String[] args) throws Exception {
    ServerSocket ss = new ServerSocket(8888);
    System.out.println("서버 실행!");
    
    while (true) {
      Socket socket = ss.accept();
      Scanner in = new Scanner(socket.getInputStream());
      PrintStream out = new PrintStream(socket.getOutputStream());
      
      // 클라이언트가 보낸 데이터를 HTTP 요청 프로토콜에 맞춰 읽는다.
      while (true) {
        String str = in.nextLine();
        System.out.println(str);
        if (str.equals(""))
          break;
      }
      
      out.print("HTTP/1.1 200 OK\r\n");
      out.print("Content-Type: text/html; charset=UTF-8\r\n");
      out.print("\r\n");
      out.print("<html><body><h1>안녕!-하연</html></body></h1>");
      
      out.close();
      in.close();
      socket.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
서버 실행!
GET / HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7

GET /favicon.ico HTTP/1.1
Host: localhost:8888
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
DNT: 1
Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: http://localhost:8888/
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7

URL

image

URI(Uniform Resource Identifier)은 URL과 URN으로 나뉘어진다. URN은 거의 쓰지 않는 방식이다.

웹 사이트에서 자원의 위치를 표현하는 방법

  • [프로토콜]://서버주소:포트번호/자원의경로?파리미터명=값&파라미터명=값
  • 프로토콜: http(80), https(443), ftp(21/20) 등
  • 서버주소: IP 주소(192.168.0.1), 도메인명(``www.bitcamp.co.kr`)
  • 자원의경로: /index.html, /board/list.jsp 등
  • 서버에 보내는 파라미터(Query String): 파라미터명=값&파라미터명=값

자원

정적 자원(static)

  • 요청할 때마다 결과 콘텐트가 변경되지 않는 고정된 자원, 즉 파일을 가리킨다.
  • 예) HTML, GIF, JPEG, PNG, CSS, JavaScript, TXT 등의 파일

동적 자원(dynamic)

  • 요청할 때마다 결과 콘텐트가 변할 수 있는 자원
  • 메일 조회, 게시물 변경, 주문 등의 웹 프로그램을 가리킨다.
  • 예) index.php, index.jsp 등

URL을 다루는 클래스

  • [프로토콜]://서버주소:포트번호/자원의경로?파리미터명=값&파라미터명=값

  • 포트번호는 생략할 수 있다. 생략하면 80(HTTP), 443(HTTPS)번으로 간주한다. 다만 getPort()의 리턴값은 -1이다.
  • Query String: 서버에 보내는 파라미터를 지정
    • 자원의 경로(예: /index.php) 다음(물음표 ? 다음)에 오는 파라미터
    • 형식: 파라미터명=값&파라미터명=값&파라미터명=값
    • 예: sm=top_hty&fbm=1&ie=utf8&query=비트캠프
1
2
3
4
5
6
7
8
URL url = new URL("https://search.naver.com/search.naver?sm=top_hty&fbm=1&ie=utf8&query=bitcamp");

// URL 분석
System.out.printf("프로토콜: %s\n", url.getProtocol());
System.out.printf("서버주소: %s\n", url.getHost());
System.out.printf("포트번호: %d\n", url.getPort()); // 지정하지 않으면 -1 리턴. 실제 접속할 때는 기본 포트번호 사용.
System.out.printf("자원경로: %s\n", url.getPath());
System.out.printf("서버에 보내는 파라미터: %s\n", url.getQuery());
  • 자원의 내부 위치 표현
    • http://서버주소:포트/자원의경로/../xxx#문서의 내부 위치
    • 자원 경로 다음에 문서의 내부 위치를 지정하면 웹브라우저는 해당 위치로 자동 스크롤 한다.
1
2
3
URL url = new URL("https://tools.ietf.org/html/rfc2616#section-5.1");

System.out.printf("참조경로(내부위치): %s\n", url.getRef());
  • 로컬 자원의 위치를 URL로 표현하는 방법
    • file://자원의 경로
    • 자원의 경로
      • /드라이브명:/디렉토리 또는 파일 경로 (Windows)
      • /루트디렉토리/디렉토리 또는 파일 경로 (Linux/macOS/Unix)

URL 요청하기

URL 클래스를 이용하여 HTTP 요청 수행하기

URL 클래스를 이용하면 HTTP 프로토콜을 신경쓰지 않고 HTTP 요청을 수행할 수 있다. 특히 HTTPS까지도 처리할 수 있다.

그러면 URL클래스로 HTTPS 요청이 가능한 이유는? HTTPS 요청을 하기 위해서는 암호화된 데이터를 복원하기 위해 클라이언트 측에서 인증서를 가지고 있어야 하는데, URL 클래스 내부적으로 이 작업을 대신 다 해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// URL 주소를 검증하고 준비한다.
URL url = new URL("https://www.daum.net");

// 서버와 연결하고 http 요청을 수행한다. 
// 그런 후에 웹서버의 응답 데이터를 읽어들일 도구를 준비한다.
InputStream in = url.openStream();
BufferedReader in2 = new BufferedReader(new InputStreamReader(in));

while (true) {
  String str = in2.readLine();
  if (str == null)
    break;
  System.out.println(str);
}

in2.close();
in.close();

이때 응답헤더는 읽지 않는다. outputstream이 읽는 것은 응답 헤더를 제외한 message body 부분을 inputstream을 통해서 읽을 수 있다.

BufferedReader는 더 이상 읽을 게 없으면 null을 리턴한다.

서버에 요청하고 응답한 데이터 중에서 헤더를 제외한 message body 뽑는 것까지는 URL 객체를 통해서 할 수 있다. 특히 HTTPS까지도 처리할 수 있다.

URLConnection 사용

URL 객체로 URLConnection 객체를 얻은 후, 이것을 사용한다.

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
// URL 주소를 준비한다.
URL url = new URL("http://itempage3.auction.co.kr/DetailView.aspx?itemno=C204190906");

// URL 정보를 가지고 HTTP 요청을 수행할 객체를 얻는다.
URLConnection con = url.openConnection();

// 웹서버와 연결한 후 HTTP 요청한다.
con.connect();

//URL.openStream()을 사용하는 것의 이점
System.out.printf("Content-Type: %s\n", con.getContentType());
System.out.printf("Content-Length: %d\n", con.getContentLength());
System.out.printf("Content-Encoding: %s\n", con.getContentEncoding());

// 직접 헤더 이름을 사용해서 헤더 값을 추출할 수 있다.
System.out.printf("Content-Type: %s\n", con.getHeaderField("Content-Type"));
System.out.printf("Server: %s\n", con.getHeaderField("Server"));
System.out.println();

// => 웹서버의 응답 데이터를 읽어들일 도구를 리턴한다.
InputStream in = con.getInputStream();

// => 서버가 보낸 데이터를 한 줄씩 읽기 위해 데코레이터를 붙인다.
BufferedReader in2 = new BufferedReader(new InputStreamReader(in));

while (true) {
  String str = in2.readLine();
  if (str == null)
    break;
  System.out.println(str);
}

in2.close();
in.close();

Base64 인코딩

base64라는 알고리즘을 통해서 이미지를 텍스트화시켜 바꿔서 텍스트 파일에 삽입할 수 있다.

image

Base64는 바이너리 데이터를 텍스트 데이터에 포함시키기 위해 사용한다. 그렇지 않으면 html을 클라이언트에게 줄 때 html이 가리키는 바이너리 파일도 따로 전달해야 한다. Base64를 이용하여 바이너리 데이터를 텍스트화하면 그것을 텍스트 안에 삽입하여 따로 파일을 전달할 필요가 없다.

image

  • encoding: 원래 데이터를 변환시키는 것 (바이너리 데이터 -> 텍스트 데이터: base64인코딩)
  • decoding: 원래 데이터로 바꾸는 것 (텍스트 데이터 -> 바이너리 데이터)

image

위와 같이 Base64로 인코딩할 수 있다. 처음에 바이트 배열로 변환할 때 8비트를 6비트로 자르니 무조건 영어이다. 영어 대소문자와 숫자. 그래서 나중에 new String()할 때 UTF-8 로 가정하는 것이 아무 소용 없다.

Base64

  • 바이너리 데이터를 문자화시킨다.
  • 바이너리 값을 6비트씩 잘라서(2의 6승) 64진수로 만든 후 Base64 표에 정의된 대로 해당 값을 문자로 변환한다.
  • 보통 바이너리 데이터를 텍스트로 전송하고 싶을 때 많이 사용한다.
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
public class Exam0110 {

  public static void main(String[] args) throws Exception {
    String str = "ABC012가각간";

    byte[] bytes = str.getBytes("UTF-8");
    for (byte b : bytes) {
      System.out.printf("%x ", b);
    }
    System.out.println();

    // Base64 인코딩
    // => "ABC012가간" 문자열
    //    414243303132EAB080EAB081(UTF-8 코드)
    //    4142 ==> 0100000101000010... (2진수)
    //    010000 010100 0010... (6비트씩 자른 것)
    //    6비트로 자른 것을 다시 10진수로 표현하면 ==> 16 20 ...
    //    16을 Base64 코드표에 따라 문자로 바꾸면 ==> Q
    //    20을 Base64 코드표에 따라 문자로 바꾸면 ==> U
    //    ...
    //    이런 식으로 문자열을 Base64로 바꾸면 결과는 다음과 같다.
    //    QUJDMDEy6rCA6rCB6rCE
    Encoder encoder = Base64.getEncoder();
    byte[] base64Bytes = encoder.encode(bytes);
    for (byte b : base64Bytes) {
      System.out.printf("%x ", b);
    }
    System.out.println();

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

    // Base64 디코딩
    // => Base64 코드를 원래의 바이너리 값으로 변환한다.
    //
    Decoder decoder = Base64.getDecoder();
    byte[] bytes2 = decoder.decode(base64Bytes);

    System.out.println(new String(bytes2, "UTF-8"));
  }
}
This post is licensed under CC BY 4.0 by the author.

이산수학 #8강: 수의 표현

학원 #52일차: 계산기 Network App 만들기, 스레드

Loading comments from Disqus ...