본문 바로가기

국비과정

[ANDROID 국비과정] 2023.02.06 - 안드로이드 앱 개발자 과정

Java


Network

인터넷은 어떻게 동작하는가?

 

인터넷은 어떻게 동작하는가? - Web 개발 학습하기 | MDN

이 글에서는 인터넷의 개념과 작동 원리에 대해 설명합니다.

developer.mozilla.org

네트워크란 쉽게 말해 하나의 망 입니다. 그리고 그 망을 이용하여 서로 다른 컴퓨터들을 연결합니다. 라우터에 의하여 컴퓨터들은 네트워크를 형성하며, 이러한 라우터들이 ISP 에 연결되어 더욱 큰 네트워크를 형성합니다. 컴퓨터간의 정보 교환은 아주 방대한 양이며, 무질서할 수 있는데 이를 해결하기 위해 우리는 일종의 약속을 해야합니다. 그러한 약속을 프로토콜(Protocol) 이라 합니다.

 

프로토콜의 종류

1.IP	[ Internet Protocol ]
2.TCP	[ Transmission Control Protocol ]
3.UDP	[ User Datagram Protocol ]
4.SMTP	[ Simple Mail Transfer Protocol ]
5.FTP	[ File Transfer Protocol ]
6.HTTP	[ Hyper Text Transfer Protocol ]

자바에서 프로토콜을 이용하여 데이터를 처리해봅시다. 오늘은 TCP 와 UDP 를 이용하겠습니다.

 


TCP (Tranmission Control Protocol)

TCP 는 마치 전화의 형식과 비슷합니다. 누군가에게 전화를 걸 때 상대방이 받지 않는다면 전화를 연결할 수 없습니다. 상대방이 전화를 받아야 통화를 시작할 수 있는것 입니다. TCP 의 정보전달 방식도 이와 비슷합니다. 정보를 보내는 쪽을 Client 라고하며, 받는쪽을 Server 라고 합니다.

 

클라이언트는 서버의 IP 주소와 포트번호를 알아야 정보를 보낼 수 있습니다. 또한 클라이언트는 정보를 전달하기 위해 소켓을 생성해야 하며, 소켓에서 정보를 내보내야 합니다. 반대로 서버는 서버소켓을 만들고, 그 안쪽에 소켓을 만듭니다. 이 안쪽 소켓으로 클라이언트가 접속하여 정보를 입력해주어야 합니다.

 

TCP 통신을 하기 이전에 용어 2가지만 정리하고 넘어갑시다.

 

용어정리

1. IP주소
그 컴퓨터의 고유한 주소(숫자)로, 약 42억개정도 존재합니다(IPv4) 또한 주소는 문자열로 표현됩니다.

2. port 번호
그 컴퓨터 안에서의 프로그램의 고유번호로 약 6만 5천개 정도 존재합니다. 인터넷을 사용할 수 있는 프로그램은 한가지가 아닙니다. 그렇기 때문에 어떠한 프로그램이 인터넷과 연결할지 정해야 하는데, 그때 사용되는 번호입니다. 단, 0~1024 번까지는 사용 하지 않는게 권장됩니다. 보통 10000번대 이상으로 사용하는것이 좋습니다.

 

IP주소 알아보기

 

TCP/IP 를 통해 통신을 한다면, IP 를 통해 네트워크의 목적지를 정확히 알 수 있으며, TCP 를 통해 데이터의 신뢰성을 확보할 수 있습니다. 즉, 통신을 위해서는 먼저 데이터를 전달하고자 하는곳의 IP 주소를 알아야 합니다. 다음 코드를 봅시다.

 

try {
    System.out.println(InetAddress.getLocalHost().getHostAddress()); 
    -- 1
    System.out.println(InetAddress.getLocalHost().getHostName()); 
    -- 2
    System.out.println(InetAddress.getLocalHost()); 
    System.out.println(InetAddress.getLoopbackAddress().getHostAddress()); 
    -- 3
    System.out.println(InetAddress.getLoopbackAddress().getHostName());
    -- 4
} catch (UnknownHostException e) {
	System.out.println("호스트를 찾을 수 없습니다."); -- 5
}

 

예제에서는 서버 프로그램과 클라이언트 프로그램이 한 프로그램에서 통신하기 때문에 두 프로그램에서 통신하는 컴퓨터는 본 컴퓨터 1대라고 하겠습니다.

 

서버용 프로그램 작성하기

TCP 의 통신을 위해서는 서버와 클라이언트가 필요합니다. 그래서 서버용 프로그램을 먼저 만들어봅시다.

 

try {
    ServerSocket serverSocket = new ServerSocket(10001);
    System.out.println("서버소켓 생성");
    -- 1

    System.out.println("클라이언트의 접속을 기다리는 중 . . .");
    Socket socket = serverSocket.accept();
    -- 2
    System.out.println("클라이언트 접속 완료!");
    -- 3

    InputStream is = socket.getInputStream();
    InputStreamReader isr = new InputStreamReader(is);
    BufferedReader reader = new BufferedReader(isr);
    -- 4

    while(true) {
        String msg = reader.readLine();
        -- 5

        if(msg == null) break;
        System.out.println("받은 메시지 : " + msg);
    }
    System.out.println("모든 접속 종료");
    reader.close();
} catch (IOException e) {
	System.out.println("입출력 오류");
} -- 6

 

  1. ServerSocket 클래스를 이용하여 서버소켓 객체를 생성해줍니다. 객체의 생성자 파라미터로 포트번호를 넘겨주어, 클라이언트가 메시지를 전달할 때 지정한 포트번호로 넘겨주도록 합니다.

  2. 데이터를 저장할 소켓을 하나 만들어주고, accept() 메서드를 통해 클라이언트의 서버접속 유무를 판단하여 정보를 받아올 수 있는 상태가 될 수 있습니다. 클라이언트가 서버에 접속하지 않는다면 접속할때까지 기다립니다.

  3. 만일 이 코드로 넘어왔다면 클라이언트의 접속이 완료되었다는 의미입니다.

  4. 접속이 완료되었으니, 클라이언트의 데이터를 읽어올 수 있는 InputStream 을 열어줍시다. 그리고 열린 바이트스트림을 받아오기 편하게 문자 스트림 InputStreamReader 로 변환해준 뒤, 보조 스트림 BufferedReader 로 다시 변환시켜 줍시다.

  5. 보조스트림의 메서드 readLine() 을 활용하여 데이터를 읽어와 문자열 변수에 저장해줍니다. 단, readLine() 메서드 또한 accept() 와 같이 읽어올 데이터가 없다면 보내줄때까지 기다립니다. 그런데, if 문 안쪽의 조건을 보면 데이터의 값이 없을 때, 즉 null 값을 반환할때 while 문을 빠져나가도록 되어있습니다. 보내줄때까지 기다리는데 언제 null 값을 반환할까요? 그건 바로 스트림이 끊어졌을 때를 의미합니다. 클라이언트에서 데이터를 보내줄때, OutputStream 을 통해 보내주게 되는데, 이 스트림이 닫히게 되면 readLine() 은 null 을 반환하게 됩니다.

  6. 입력이 모두 끝났다면, close() 메서드로 스트림을 닫아줍시다. 또한 try-catch 문을 활용하여 발생할 수 있는 에러의 예외처리도 해줍니다.

 

클라이언트용 프로그램 작성하기

 

String serverIP = "localhost";
-- 1

try {
    Socket socket = new Socket(serverIP,10001);
    System.out.println("서버와 연결이 되었습니다.");
    -- 2

    OutputStream os = socket.getOutputStream();
    PrintWriter writer = new PrintWriter(os);
    System.out.println("스트림 연결 성공! ... 데이터 전송 가능");
    -- 3

    Scanner scan = new Scanner(System.in);
    while(true) {
        System.out.print("보낼 메시지를 입력하세요(exit를 입력하면 종료)");
        String msg = scan.nextLine();
        if(msg.equalsIgnoreCase("exit")) break;
        writer.println(msg);
        writer.flush();
        -- 4
    }
    writer.close();
    
} catch (IOException e) {
	System.out.print("입출력 오류");
} -- 5

 

  1. 클라이언트가 서버에 접속하기 위해서는 서버의 IP 주소와 포트번호가 필요합니다. 이 예제에서는 서버의 IP 주소가 클라이언트의 컴퓨터와 동일하기 때문에 "localhost" 를 사용하였습니다.

  2. 서버와 연결하기 위한 소켓을 만들어줍시다. 소켓을 만들어줄 때는 서버의 IP 주소와 포트번호를 전달해주어야 합니다. 아까 전 서버용 프로그램 예제에서 서버 프로그램을 만들 때, 포트번호를 10001로 지정해주었기 때문에 여기서도 포트번호를 10001로 지정해주었습니다.

  3. 정보를 서버로 전달해주기 위한 OutputStream 을 생성합시다. 또한 보조 스트림인 PrintWriter 도 생성합니다.

  4. 보낼 메시지를 입력하고 스트림을 통해 메시지를 전달 해줍니다.

  5. try-catch 문을 통해 예외처리를 해주고 출력이 끝났다면 스트림을 닫아줍시다.

 

클라이언트와 서버

 


UDP (User Datagram Protocol)

앞서 TCP 가 마치 전화의 형식과 비슷하다고 했습니다. 반면에 UDP 는 택배 보내는 형식과 비슷합니다. 택배는 보내는 사람이 받는 사람의 사는지역이 어딘지, 어느 아파트 몇호에 사는지만 알면 택배를 보낼 수 있습니다. 또한 받는 사람은 일련의 과정 없이 보내는 사람이 택배를 보내면 그냥 받을 수 있습니다.

 

UDP 의 방식도 이와 비슷합니다. 보내는쪽을 Sender, 받는쪽을 receiver 라고 합니다.택배를 보내기 위해 택배상자를 싸는것처럼, 센더쪽에서는 Datagram Packet 을 이용하여 데이터를 감싸줍니다. 그리고 택배를 보내기 위해 주소를 알아야하듯, 센더는 리시버의 IP 주소와 포트번호를 알고있어야 합니다. 센더와 리시버는 우체통과 같은 역할을 하는 Datagram Socket 을 만들어주어야 하며, 이 우체통을 통해 데이터를 주고 받습니다.

 

Sender 용 프로그램 작성하기

 

try {
    DatagramSocket socket = new DatagramSocket();
    -- 1

    Scanner scan = new Scanner(System.in);
    while(true) {
        System.out.print("보낼 데이터 (exit를 입력하면 종료) : ");
        String msg = scan.nextLine();
        if(msg.equalsIgnoreCase("exit")) break;
        -- 2

        byte[] bytes = msg.getBytes();
        InetAddress addr = InetAddress.getByName("localhost");
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, addr, 10003);
        -- 3

        socket.send(packet);
        -- 4

        System.out.println("전송 완료");
	}
} catch (SocketException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} catch (UnknownHostException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} catch (IOException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}

 

  1. 우편을 보낼 우체통을 만든는 작업과 같습니다. DatagramSocket 을 이용하여 만들어 줍니다.
  2. 보낼 데이터를 입력하고, while 문을 빠져나갈 조건을 만들어 줍시다.

  3. 택배상자를 싸듯, DatagramPacket 클래스를 이용하여, 객체의 생성자 파라미터로 보낼 데이터, 데이터의 길이, 보내고자 하는 주소(IP), 포트번호를 넘겨줍시다.

  4. 택배를 우체통을 통해 보내듯 정보가 담긴 packet을 소켓을 통해 리시버에게로 보냅니다.

 

Receiver 용 프로그램 작성하기

 

try {
    DatagramSocket socket = new DatagramSocket(10003);
    -- 1

    System.out.println("메시지 기다리는 중 . . .");
    while(true) {
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        -- 2

        socket.receive(packet); 
        -- 3

        String msg = new String(buf);
        System.out.println(msg);
        -- 4
    }
} catch (SocketException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} catch (IOException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}

 

  1. 먼저 센더쪽에서 보낸 데이터를 받을 Datagram Socket 을 준비합시다. 여기에는 포트번호를 파라미터로 전달해주어야 합니다.

  2. 불행하게도 센더에서 보낸 데이터들을 받을 때 다시 packet 으로 받아주어야 합니다. 그래서 충분한 크기의 빈 packet 을 준비해줍시다.

  3. 우체통 즉, Socket 에 담겨진 정보들을 receive() 메서드를 통해 가져옵시다.

  4. buf 에 담겨진 문자열 데이터를 출력해줍시다.