본문 바로가기

Java의 정석 : 3rd Edition

[Java의 정석 - 연습문제] Chapter16. 네트워킹(Networking)

Java의 정석 : 3rd Edition, 2016을 개인 학습용으로 정리한 내용입니다.
"ppt 파일 자료, 연습문제"를 학습 용도로 배포할 수 있음을 고지하셨습니다.
저자님께 감사드립니다.

 

 

 

 

[16-1] ip주소가 192.168.10.100이고 서브넷 마스크(subnet mask)255.255.255.0일 때,

네트워크 주소와 호스트 주소를 계산하여 화면에 출력하는 프로그램을 작성하시오.

단, 비트 연산자를 사용해서 계산해야 한다.

[실행 결과]
네트워크 주소 : 192.168.10.0.
호스트 주소 : 0.0.0.100.

 

답 :

class Exercise16_1 {
    public static void main(String[] args) {
        byte[] ip = {(byte)192, (byte)168, 10, 100};           // ip 주소
        byte[] subnet = {(byte)255, (byte)255, (byte)255, 0};  // 서브넷 마스크
        // byte[] subnet = {-1, -1, -1, 0};

        byte[] nwAddr = new byte[4];    // 네트워크 주소
        byte[] hostAddr = new byte[4];  // 호스트 주소

        System.out.print("네트워크 주소 : ");

        for (int i = 0; i < ip.length; i++) {
            nwAddr[i] = (byte) (ip[i] & subnet[i]);  // & 연산
            System.out.print(nwAddr[i] >= 0 ? nwAddr[i] : nwAddr[i] + 256);  // 부호O -> 부호X
            System.out.print(i != ip.length-1 ? "." : "");  // 마지막에 마침표 출력X
        }

        System.out.println();
        System.out.print("호스트 주소 : ");

        for (int i = 0; i < ip.length; i++) {
            hostAddr[i] = (byte) (ip[i] & ~subnet[i]);  // & 연산
            System.out.print(hostAddr[i] >= 0 ? hostAddr[i] : hostAddr[i] + 256);  // 부호O -> 부호X
            System.out.print(i != ip.length-1 ? "." : "");  // 마지막에 마침표 출력X
        }
    }
}
[실행 결과]
네트워크 주소 : 192.168.10.0
호스트 주소 : 0.0.0.100

1) byte[]에 ip주소와 서브넷 마스크를 저장한다.

    byte(-128~127)의 표현 범위를 초과하는 값인 192를 저장할 수 없으므로 강제 형변환을 통해 값을 저장해야 한다.

    강제 형변환(큼 → 작음)으로 인해 데이터 손실이 발생하지만, 추후에 이 값은 부호 없는 정수의 값으로 바뀌게 된다.

2) 네트워크 주소와 호스트 주소를 담기 위한 byte[]을 선언한다.

3) 네트워크 주소 : 반복문을 이용하여 1byte씩 ip주소와 서브넷 마스크를 &로 연산하면 얻을 수 있다.

    = ip주소와 서브넷 마스크를 비트 연산자 &로 연산하면 ip주소에서 네트워크 주소만을 뽑아낼 수 있다.

    연산 결과를 nwAddr[](네트워크 주소)에 담은 후 부호 있는 정수(-)를 부호 없는 정수(+)로 바꿔 출력한다.

    부호O(-128~127) → 부호X(0~255) : 양수일 때는 그대로 두고 음수일 때만 256을 더하면 된다(예 : -1 + 256 = 255).

    부호X(0~255) → 부호O(-128~127) : 127 보다 큰 값의 경우에만 256을 빼면 된다(예 : 128 - 256 = -128).

IP주소(192.168.10.100)와 서브넷 마스크(255.255.255.0)의 2진법 표기
IP주소(192.168.10.100)와 서브넷 마스크(255.255.255.0)의 2진법 표기
네트워크 주소 : IP주소(192.168.10.100)와 서브넷 마스크(255.255.255.0)의 &연산
네트워크 주소 : IP주소(192.168.10.100)와 서브넷 마스크(255.255.255.0)의 &연산

4) 호스트 주소 : 반복문을 이용하여 1byte씩 서브넷 마스크에 ~(not) 연산을 취한 다음 & 연산을 취하면 얻을 수 있다.

    = ip주소와 서브넷 마스크에 비트 연산자 ~로 연산한 다음 &로 연산하면 ip주소에서 호스트 주소만을 뽑아낼 수 있다.

    ~subnet : (10진수 : 0.0.0.255), (2진수 : 00000000000000000000000011111111)

    연산 결과를 hostAddr[](호스트 주소)에 담은 후 부호 있는 정수(-)를 부호 없는 정수(+)로 바꿔 출력한다.

    부호O(-128~127) → 부호X(0~255) : 양수일 때는 그대로 두고 음수일 때만 256을 더하면 된다(예 : -1 + 256 = 255).

    부호X(0~255) → 부호O(-128~127) : 127 보다 큰 값의 경우에만 256을 빼면 된다(예 : 128 - 256 = -128).

IP주소(192.168.10.100)와 &#39;~서브넷 마스크(0.0.0.255)&#39;의 2진법 표기
IP주소(192.168.10.100)와 '~서브넷 마스크(0.0.0.255)'의 2진법 표기
호스트 주소 : IP주소(192.168.10.100)와 &#39;~서브넷 마스크(0.0.0.255)&#39;의 &연산
호스트 주소 : IP주소(192.168.10.100)와 '~서브넷 마스크(0.0.0.255)'의 &연산

5) 끝에 .을 출력하지 않기 위해 배열의 마지막 인덱스(길이보다 -1 작음)에는 빈 문자열을 넣는다.

IP주소 : 컴퓨터(host)를 구별하는데 사용되는 고유한 값. 인터넷에 연결된 모든 컴퓨터는 IP주소를 갖는다.
         : IP주소는 네트워크 주소와 호스트 주소로 나뉜다.
서브넷 마스크(subnet mask) : IP주소의 네트워크 부분과 호스트 부분을 구분하는 역할
네트워크 주소 : 네트워크를 구분하는데 사용되는 고유한 값.
                        : 두 호스트의 네트워크 주소가 동일하다면 같은 네트워크에 포함되어 있다는 의미이다(통신 가능).
호스트 주소 : 해당 네트워크 주소에 속한 호스트를 구분하는데 사용되는 고유한 값.
~ : 비트 전환 연산자(1의 보수 연산자). 피 연산자를 2진수로 표현할 때 0 → 1, 1 → 0으로 바꾼다.
& : 비트 연산자. 양쪽 모두 1이어야 1을 결과로 얻으며, 그 외에는 0을 얻는다.
비트 연산자 : 피 연산자를 이진수로 표현한 후 각 자리를 규칙에 따라 비트 단위로 논리 연산한다.
모든 bit의 값이 1인 1byte(8bit)
모든 bit의 값이 1인 1byte(8bit)

모든 bit의 값이 1인 1byte의 데이터가 있을 때
(왼쪽) 첫 번째 비트를 부호로 인식X : 부호 없는 1byte(범위 : 0~255)
(왼쪽) 첫 번째 비트를 부호로 인식O : 부호 있는 1byte(범위 : -128~127 = -1)
같은 데이터이지만 자바의 자료형인 byte의 범위가 부호 있는 1byte 정수의 범위인 -128~127이라 -1로 인식한다.
(이 말은 사실 이해가 잘 되지 않는다. 학습 후 수정하겠습니다.)

byte의 표현 범위와 출력문(print, println, printf)의 자동 형변환
byte의 표현 범위는 -128~127인데 어떻게 이 범위를 넘는 값(192, 168)을 출력할 수 있었을까?
출력문 내 자료형에 대해 오버플로우 or 언더플로우가 발생하면 어떤 변수에 저장한 게 아니기 때문에
정수형의 기본 자료형인 int 타입으로 자동 형변환되어 출력된다.

즉, nwAddr[i]에 256를 더한 값 그대로가 nwAddr배열에 저장된 게 아닌 연산(+) 결과가 int 타입으로 출력된 것이다.
nwAddr배열에 저장된 값을 출력해보면 [-64, -88, 10, 0] 임을 알 수 있다.
for (int i = 0; i < ip.length; i++) {
    nwAddr[i] = (byte) (ip[i] & subnet[i]);  // & 연산
    System.out.print(nwAddr[i] >= 0 ? nwAddr[i] : nwAddr[i] + 256);  // 부호O -> 부호X
    System.out.print(i != ip.length-1 ? "." : "");  // 마지막에 마침표 출력X
}​
System.out.println(Arrays.toString(ip));  // [-64, -88, 10, 100]
System.out.println(Arrays.toString(nwAddr));  // [-64, -88, 10, 0]​

 

 

 

 

[16-2] 다음 중 TCP의 특징이 아닌 것은?

a. 전화와 같은 1:1 연결 기반의 프로토콜이다.

b. 데이터의 전송 순서가 보장된다.

c. UDP보다 전송 속도가 빠르다.

d. 데이터의 수신 여부를 확인한다.

 

답 : c

TCP은 연결 후 통신해야 하기 때문에 연결이 필요없는 UDP보다 전송 속도가 느리다.

TCP와 UDP의 비교
TCP와 UDP의 비교

 

 

 

 

※ 자바의 정석 기초편에는 AWT를 다루지만 자바의 정석 3판에서는 관련 내용이 빠졌습니다.
[16-3], [16-4] AWT 관련된 개념은 저자님 Github에 업로드된 자바의 정석 기초편 ppt를 참고하였습니다.

[16-3] TextField에 URL을 입력하고 Enter키를 누르면 해당 페이지의 소스를 보여주는 'Source Viewer' 프로그램이다.

예제16-4를 참고해서 (1)에 알맞은 코드를 넣어 완성하시오.

import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

class SourceViewer extends Frame {
    TextField tf = new TextField();
    TextArea ta = new TextArea();

    SourceViewer(String title) {
        super(title);
        
        add(tf, "North");
        add(ta, "Center");

        tf.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                displaySource();
            }
        });

        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                System.exit(0);
            }
        });

        setBounds(500, 200, 500, 300);
        setVisible(true);
    }

    void displaySource() {
        URL url = null;
        BufferedReader input = null;
        String address = tf.getText().trim();
        String line = "";

        ta.setText("");

        try {
            if(!address.startsWith("http://")) {
                address = "http://" + address;
            }

            // (1) 알맞은 코드를 넣어 완성하시오.
            
            input.close();
        } catch(Exception e) {
            ta.setText("유효하지 않은 URL 입니다.");
        }
    } // displaySource()

    public static void main(String[] args) {
        SourceViewer mainWin = new SourceViewer("Source Viewer");
    }
}
[실행 결과]
[16-3] 실행 결과
[16-3] 실행 결과
예제 16-4 : https://github.com/castello/javajungsuk3/blob/master/workspace/ch16/src/NetworkEx4.java

 

답 :

url = new URL(address);
input = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));

while ((line = input.readLine()) != null) {
    ta.append(line);
    ta.append("\n");
}

1) URL(String spec) : 지정된 문자열(주소) 정보를 통해

 

URL객체를 생성한다.

2) 읽으려는 데이터가 HTML페이지(텍스트)이므로 BufferedReader(문자 기반 보조 스트림)를 이용하여 데이터를 읽는다.

    InputStreamReader를 이용하여 입력 스트림(openStream())을 BufferedReader으로 변환한다.

    InputStreamReader를 이용하여 URL의 텍스트 데이터를 읽어올 때는 인코딩을 지정해줘야 한다.

      > 인코딩을 지정하지 않으면 OS에서 사용하는 인코딩(예 : XP-MS949)으로 지정된다.

      > 지정한 인코딩과 읽어올 URL 텍스트의 인코딩이 일치하지 않으면 내용이 바르게 보이지 않을 수 있다.

      > HTML페이지의 인코딩을 알아낸 다음에 화면에 보여주면 페이지의 내용을 온전히 보여줄 수 있다.

    openStream() : URL에 연결하여 InputStream(입력 스트림)을 얻는다.

    BufferedInputStream : 바이너리 파일(이미지 등)을 읽을 때 사용하는 바이트 기반 보조 스트림

                                        (InputStreamReader 없이 단독으로 사용하면 된다.)

3) 반복문을 이용하여 읽어온 데이터를 append(String str)를 통해 TextArea의 내용 맨 마지막에 덧붙인다.

    setText()는 초기화 용도로 사용하고, 생성된 영역에 내용을 추가하는 경우에는 append()를 사용하는 것 같다.

    void setText(String t) : 지정된 문자열을 TextArea 영역에 표시될 내용으로 설정한다.

    void append(String str) : 지정된 문자열을 TextArea에 표시되는 내용 맨 마지막에 덧붙인다.

HTML페이지의 인코딩을 확인하는 방법
<meta>content 속성을 보면 인코딩(charset)을 알 수 있다. charset=utf-8
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
...​

 

 

 

 

 

[16-4] 다음의 코드는 TCP 통신을 하는 예제16-6, 16-7을 결합하여 GUI 채팅 프로그램을 작성한 것이다.

(1)~(4)에 알맞은 코드를 넣어 프로그램을 완성하시오.

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

class ChatServer extends Frame {
    String nickname = "";

    DataOutputStream out;
    DataInputStream in;

    Panel p = new Panel();
    TextArea ta = new TextArea();
    TextField tf = new TextField();

    ChatServer(String nickname) {
        super("Chatting");
        this.nickname = nickname;

        p.setLayout(new BorderLayout());
        p.add(tf, "Center");

        add(ta, "Center");
        add(p, "South");

        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        EventHandler handler = new EventHandler();
        ta.addFocusListener(handler);
        tf.addFocusListener(handler);
        tf.addActionListener(handler);

        ta.setEditable(false);
        setBounds(200, 200, 300, 200);
        setVisible(true);
        tf.requestFocus();
    }

    void startServer() {
        ServerSocket serverSocket = null;
        Socket socket = null;

        try {
            /*
                (1) 아래의 로직에 맞게 코드를 작성하시오.
                    1. 서버 소켓을 생성하여 7777번 포트와 결합시킨다.
                    2. ta에 "서버가 준비되었습니다."라고 보여준다.
                    3. 상대방의 연결을 기다린다.
                    4. ta에 "상대방과 연결되었습니다."라고 보여준다.
                       ta.append("\r\n" + "상대방과 연결되었습니다.");
                    5. 연결된 상대방 소켓의 입력 스트림과 출력 스트림을 얻어온다.
                    6. 반복문을 이용해서 입력 스트림이 null이 아닌 동안
                       입력 스트림으로부터 데이터를 읽어 변수 msg에 저장한다.
                       ta.append("\r\n" + msg);
            */
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("USAGE : java ChatServer NICKNAME");
            System.exit(0);
        }

        ChatServer chatWin = new ChatServer(args[0]);
        chatWin.startServer();
    } // main

    class EventHandler extends FocusAdapter implements ActionListener {
        public void actionPerformed(ActionEvent ae) {
            String msg = tf.getText();

            if ("".equals(msg)) return;

            // (2) 알맞은 코드를 넣어 완성하시오.

            ta.append("\r\n" + nickname + ">" + msg);
            tf.setText("");
        }

        public void focusGained(FocusEvent e) {
            tf.requestFocus();
        }
    } // class EventHandler
} // class
import java.awt.*;
import java.awt.event.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;

class ChatClient extends Frame {
    String nickname = "";
    String serverIp = "";
    int serverPort = 0;

    DataOutputStream out;
    DataInputStream in;

    Panel p = new Panel();
    TextArea ta = new TextArea();
    TextField tf = new TextField();

    ChatClient(String nickname, String serverIp, String serverPort) {
        super("Chatting with " + serverIp + ":" + serverPort);
        this.nickname = nickname;
        this.serverIp = serverIp;
        this.serverPort = Integer.parseInt(serverPort);

        setBounds(600, 200, 300, 200);

        p.setLayout(new BorderLayout());
        p.add(tf, "Center");

        add(ta, "Center");
        add(p, "South");

        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        EventHandler handler = new EventHandler();
        ta.addFocusListener(handler);
        tf.addFocusListener(handler);
        tf.addActionListener(handler);

        ta.setEditable(false);

        setVisible(true);
        tf.requestFocus();
    }

    void startClient() {
        try {
            /*
                (3) 아래의 로직에 맞게 코드를 작성하시오.
                    1. 소켓을 생성하여 serverIp의 serverPort에 연결한다.
                    2. ta에 "상대방과 연결되었습니다."라고 보여준다.
                       ta.setText("상대방과 연결되었습니다.");
                    3. 연결된 상대방 소켓의 입력 스트림과 출력 스트림을 얻어온다.
                    4. 반복문을 이용해서 입력 스트림이 null이 아닌 동안
                       입력 스트림으로부터 데이터를 읽어 변수 msg에 저장한다.
            */
        } catch (ConnectException ce) {
            ta.setText("상대방과 연결할 수 없습니다.");
            ce.printStackTrace();
        } catch (IOException ie) {
            ie.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        if (args.length != 3) {
            System.out.println("USAGE : java ChatClient NICKNAME SERVER_IP SERVER_PORT");
            System.exit(0);
        }

        ChatClient chatWin = new ChatClient(args[0], args[1], args[2]);
        chatWin.startClient();
    } // main

    class EventHandler extends FocusAdapter implements ActionListener {
        public void actionPerformed(ActionEvent ae) {
            String msg = tf.getText();

            if ("".equals(msg)) return;

            // (4) 알맞은 코드를 넣어 완성하시오.

            ta.append("\r\n" + nickname + ">" + msg);
            tf.setText("");
        }

        public void focusGained(FocusEvent e) {
            tf.requestFocus();
        }
    } // class EventHandler
} // class
[실행 결과]
[16-4] 실행 결과
[16-4] 실행 결과

 

답 :

// ChatServer
void startServer() {
    ServerSocket serverSocket = null;
    Socket socket = null;

    try {
        // 1. 서버 소켓을 생성하여 7777번 포트와 결합시킨다.
        serverSocket = new ServerSocket(7777);
        // 2. ta에 "서버가 준비되었습니다."라고 보여준다.
        ta.setText("서버가 준비되었습니다.");

        // 3. 상대방의 연결을 기다린다.
        socket = serverSocket.accept();
        // 4. ta에 "상대방과 연결되었습니다."라고 보여준다.
        ta.append("\r\n" + "상대방과 연결되었습니다.");

        // 5. 연결된 상대방 소켓의 입력 스트림과 출력 스트림을 얻어온다.
        in = new DataInputStream(socket.getInputStream());     // 입력 스트림
        out = new DataOutputStream(socket.getOutputStream());  // 출력 스트림

        // 6. 반복문을 이용해서 입력 스트림이 null이 아닌 동안
        //    입력 스트림으로부터 데이터를 읽어 변수 msg에 저장한다.
        while (in != null) {
            try {
                String msg = in.readUTF();
                ta.append("\r\n" + msg);  // "\r\n" : 개행 처리
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
class EventHandler extends FocusAdapter implements ActionListener {
    public void actionPerformed(ActionEvent ae) {
        String msg = tf.getText();

        if ("".equals(msg)) return;

        // (2) 알맞은 코드를 넣어 완성하시오.
        if (out != null) {
            try {
                out.writeUTF(nickname + ">" + msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        ta.append("\r\n" + nickname + ">" + msg);
        tf.setText("");
    }
} // class EventHandler​

1) serverSocket 생성 : 7777번 포트에서 클라이언트 프로그램의 연결 요청을 기다린다.

2) void setText(String t) : 지정된 문자열을 TextArea 영역에 표시될 내용으로 설정한다.

3) serverSocket 클라이언트 프로그램의 연결 요청을 처리할 수 있도록 대기 상태로 만든다.

    클라이언트 프로그램의 연결 요청이 오면 새로운 socket을 생성해서 클라이언트 프로그램의 socket과 연결한다.

4) void append(String str) : 지정된 문자열을 TextArea에 표시되는 내용 맨 마지막에 덧붙인다.

5) InputStream getInputStream() : socketInputStream(입력 스트림)을 반환한다.

    OutputStream getOutputStream() : socket의 OutputStream(출력 스트림)을 반환한다.

6) String readUTF() : UTF-8 형식으로 코딩된 문자열을 읽는다.

                                : 더 이상 읽을 값이 없으면 EOFException가 발생한다(예외 처리 필요).

    void append(String str) : 지정된 문자열을 TextArea에 표시되는 내용 맨 마지막에 덧붙인다.

    \r\n : 시스템(OS) 마다 사용하는 개행 문자가 다르다(Window : \r\n, max : \r, unix : \n).

7) Event Handler(이벤트 처리기) : 이벤트가 발생했을 때 실행될 코드를 구현해 놓은 클래스

    입력한 내용을 상대 socket에 출력하기 위함.

    void writeUTF(String str) : UTF-8 형식으로 문자열을 출력한다.

    UTF-8 : 하나의 문자를 1~4byte의 가변 크기로 표현한다(영문, 숫자 : 1byte, 한글 : 3byte).

소켓(socket)
: 프로세스간의 통신에 사용되는 양쪽 끝단(endpoint)
: 두 개의 스트림(InputStream(입력 스트림), OutputStream(출력 스트림))을 가진다.
: 두 스트림을 통해 프로세스간의 통신(입출력)이 이뤄진다.

 

// ChatClient
void startClient() {
    try {
        // 1. 소켓을 생성하여 serverIp의 serverPort에 연결한다.
        Socket socket = new Socket(serverIp, serverPort);
        // 2. ta에 "상대방과 연결되었습니다."라고 보여준다.
        ta.setText("상대방과 연결되었습니다.");

        // 3. 연결된 상대방 소켓의 입력 스트림과 출력 스트림을 얻어온다.
        in = new DataInputStream(socket.getInputStream());     // 입력 스트림
        out = new DataOutputStream(socket.getOutputStream());  // 출력 스트림

        // 4. 반복문을 이용해서 입력 스트림이 null이 아닌 동안
        //    입력 스트림으로부터 데이터를 읽어 변수 msg에 저장한다.
        while (in != null) {
            try {
                String msg = in.readUTF();
                ta.append("\r\n" + msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } catch (ConnectException ce) {
        ta.setText("상대방과 연결할 수 없습니다.");
        ce.printStackTrace();
    } catch (IOException ie) {
        ie.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
class EventHandler extends FocusAdapter implements ActionListener {
        public void actionPerformed(ActionEvent ae) {
            String msg = tf.getText();

            if ("".equals(msg)) return;

            // (4) 알맞은 코드를 넣어 완성하시오.
            if (out != null) {
                try {
                    out.writeUTF(nickname + ">" + msg);
                } catch (IOException e) {}
            }

            ta.append("\r\n" + nickname + ">" + msg);
            tf.setText("");
        }

        public void focusGained(FocusEvent e) {
            tf.requestFocus();
        }
    } // class EventHandler
} // class

1) socket을 생성(서버 IP, 포트 번호)하면 서버에 자동으로 연결 요청을 하게 된다.

2) void setText(String t) : 지정된 문자열을 TextArea 영역에 표시될 내용으로 설정한다.

3) InputStream getInputStream() : socket InputStream(입력 스트림)을 반환한다.

    OutputStream getOutputStream() : socket의 OutputStream(출력 스트림)을 반환한다.

4) String readUTF() : UTF-8 형식으로 코딩된 문자열을 읽는다.

                                : 더 이상 읽을 값이 없으면 EOFException가 발생한다(예외 처리 필요).

    void append(String str) : 지정된 문자열을 TextArea에 표시되는 내용 맨 마지막에 덧붙인다.

    \r\n : 시스템(OS) 마다 사용하는 개행 문자가 다르다(Window : \r\n, max : \r, unix : \n).

5) Event Handler(이벤트 처리기) : 이벤트가 발생했을 때 실행될 코드를 구현해 놓은 클래스

    입력한 내용을 상대 socket에 출력하기 위함.

    void writeUTF(String str) : UTF-8 형식으로 문자열을 출력한다.

    UTF-8 : 하나의 문자를 1~4byte의 가변 크기로 표현한다(영문, 숫자 : 1byte, 한글 : 3byte).

 

책 한 권만 바라보고 완독하는 데에 오랜 세월이 흘러버렸다. 게으름에 반성하며 앞으로도 완독해나갈 책들이 기대된다.