28장. 다른 서버로 데이터를 보내려면 어떻게 하면 되나요?

공부하는 감자·2023년 12월 22일
0

자바의 신 3판

목록 보기
28/30

들어가기 전

『자바의 신 3판』 을 읽고 내용 정리 및 공부한 내용을 정리한 글입니다.
서적: 자바의 신 3판 구입처

내용 정리

네트워크 프로그래밍

네트워킹

사용자들이 바로 옆에 있는 장비와 데이터를 주고 받는 작업을 보통 네트워킹(networking)이라고 한다.

네트워크의 일반적인 레이어

  • 애플리케이션 레이어
    • HTTP, ftp, telnet 등
  • 트랜스포트 레이어
    • TCP, UDP 등
  • 네트워크 레이어
    • IP 등
  • 링크 레이어
    • device driver 등

자바에서 활용하는 대표적인 레이어들

  • TCP 통신 (Transmission Control Protocol)
    • 가장 대표적인 통신 방법으로, 연결 기반 프로토콜이라고 불린다.
    • HTTP, ftp, telnet 은 모두 TCP 통신을 한다.
    • 상대방이 데이터를 받았는지 확실히 보장할 수 있다.
    • 내부적으로 처리되는 절차가 매우 복잡해 UDP 보다 비싸고 느리며, 무겁다.
  • UDP 통신 (User Datagram Protocol)
    • 다른 장비가 데이터를 제대로 받았는지에 대한 보장을 못한다.
    • 100만 개 중 몇 개는 유실되어도 큰 문제가 되지 않는 시스템에서 사용한다.
  • 포트 (port)
    • 16비트로 구성되어 65,535까지 사용할 수 있다.
    • 일반적으로 웹 애플리케이션에서는 80번을 사용하도록 정해져 있다.
    • SSL이라는 안전한 통신을 하려면 443이라는 포트를 사용해야 한다.
    • 사용하고 싶은 포트 번호를 지정할 수 있다. (웹 주소:8081으로 호출)
    • 0~1023까지는 정해진 포트로, 다른 용도로 사용하지 않는 것이 좋다.

TCP 통신: Socket 클래스

자바에서 TCP 통신을 수행하려면 소켓 클래스를 사용해 소켓 통신을 한다.

  • 데이터를 보내는 쪽(보통 클라이언트)에서 Socket 객체를 생성하여 사용한다.
  • 데이터를 받는 쪽(보통 서버)에서 클라이언트의 요청에 대한 Socket 객체를 생성하여 데이터를 처리한다.
  • 즉, Socket 클래스는 원격에 있는 장비와의 연결 상태를 보관하고 있다.

클래스 선언

생성자

많은 생성자가 있는데, 그 중 일부만 정리했다.

생성자설명
Socket()소켓 객체만 생성
Socket(Proxy proxy)프록시 관련 설정과 함께 소켓 객체만 생성
Socket(SocketImpl impl)사용자가 지정한 SocketImpl 객체를 사용하여 소켓 객체만 생성
Socket(InetAddress address, int port)소켓 객체 생성 후 address와 port를 사용하는 서버에 연결
Socket(String host, int port)소켓 객체 생성 후 host와 port를 사용하는 서버에 연결
  • 가장 편한 것은 Socket(InetAddress address, int port) 이다.

Timeout

소켓 연결에 문제가 있을 경우 일정 시간 이후 끊어주는 것을 말한다.

  • 소켓에 대한 연결 시간 초과
    • 원격 호스트에 대한 연결 설정을 시도할 때, 일정 시간이 지나도 연결에 실패할 경우 ConnectionException을 발생시킨다.
    • connect() 메소드에 Timeout 값을 지정한다.
Socket socket = new Socket(); 
SocketAddress socketAddress = new InetSocketAddress(host, port); 
socket.connect(socketAddress, 30000);
  • 읽기 시간 초과
    • 소켓에서 적어도 하나의 데이터 바이트를 읽을 때까지 대기하다가, 일정 시간이 지나도 아무것도 반환하지 않으면 InterruptedIOException을 발생시킨다.
ServerSocket serverSocket = new new ServerSocket(port);
serverSocket.setSoTimeout(40000);

ServerSocket 클래스

서버에서 데이터를 받을 때 사용하는 클래스로, 클라이언트 요청이 생기면 Socket 객체를 생성해서 전달하는 메소드를 제공한다.

생성자

생성자설명
ServerSocket()서버 소켓 객체만 생성한다.
ServerSocket(int port)지정된 포트를 사용하는 서버 소켓을 생성한다.
ServerSocket(int port, int backlog)지정된 포트와 backlog 개수를 가지는 소켓을 생성한다.
ServerSocket(int port, int backlog, InetAddress bindAddr)지정된 포트와 backlog 개수를 가지는 소켓을 생성하며, bindAddr에 있는 주소에서의 접근만을 허용한다.
  • backlog: 연결 요청을 처리하지 못하고 대기 시킬 때의 최대 대기 개수 (Queue의 개수)
    • default: 50개
    • 애플리케이션의 접속이 원활하지 않는다면 이 개수를 적절히 증가시킨다.
  • InetAddress bindAddr
    • 특정 주소에서만 접근이 가능하도록 지정할 때 사용한다.
  • 매개 변수가 없는 생성자를 제외한 나머지는 객체가 생성되자 마자 연결을 대기할 수 있다.

메소드

리턴 타입메소드설명
Socketaccept()새로운 소켓 연결을 기다리고, 연결이 되면 Socket 객체를 리턴
voidclose()소켓 연결을 종료
  • close 메소드 처리를 하지 않고 JVM이 계속 동작 중이라면, 해당 포트는 동작하는 서버나 PC에서 다른 프로그램이 사용할 수 없다.

서버에서 소켓 연결

public void startServer() {
    ServerSocket server=null;
    Socket client=null;
    try {

        // 9999 포트로 소켓 객체 생성
        server = new ServerSocket(9999);

        while(true) {
            
            // 다른 원격 호출 대기
            client = server.accept();

            // InputStream 객체를 받아 데이터 읽기
            InputStream stream = client.getInputStream();
            BufferedReader in = new BufferedReader(
                new InputStreamReader(stream)
            );
            String data=null;
            StringBuilder receivedData = new StringBuilder();
            while( (data=in.readLine()) != null ) {
                receivedData.append(data);
            }
            System.out.println("Received data:" + receivedData);

            // 종료
            in.close();
            stream.close();
            client.close();

            // 넘어오는 데이터가 "EXIT" 이라면 종료
            if(receivedData != null && "EXIT".equals(receivedData.toString())) {
                break;
            }
        }
    } catch (Exception e) {
        ...
    } finally {
        // server.close()
    }
}

클라이언트에서 소켓 연결

public void sendSocketData(String data) {
    Socket socket=null;
    try {

        // 같은 장비이므로 127.0.0.1 선언, 9999 포트로 연결
        socket = new Socket("127.0.0.1",9999);
        Thread.sleep(1000);

        // OutputStream으로 데이터 전송
        OutputStream stream = socket.getOutputStream();
        BufferedOutputStream out = new BufferedOutputStream(stream);
        byte[] bytes = data.getBytes();
        out.write(bytes);

        // 종료
        out.close();
    } catch (Exception e) {
        ...
    } finally {
        // socket.close();
    }
}

TCP 통신 수행 순서

  1. 서버에서 new ServerSocket(9999)
    1. bind() 내부 호출
  2. 클라이언트에서 new Socket(”127.0.0.1”, 9999)
    1. connect() 내부 호출
  3. 클라이언트에서 OutputStream으로 데이터 송신 (전송)
  4. 서버에서 InputStream으로 데이터 송신 (수신)
  5. 서버와 클라이언트에서 각각 Stream 객체 close()
  6. 서버와 클라이언트에서 Socket 객체를 close()

💡 양방향 통신 가능
클라이언트에서 서버로 데이터를 전송할 수도 있고, 반대로 받을 수도 있다.

UDP 통신: Datagram 관련 클래스

UDP는 TCP와 달리 데이터가 제대로 전달되었다는 보장을 하지 않으므로, 데이터의 유실이 있어도 문제가 없을 때 사용한다.

  • 데이터를 서버에 붙지 않더라도 아무런 이상 없이 프로그램 수행 및 종료된다.

DatagramSocket

TCP와 다르게 클래스 하나에서 보내는 역할과 받는 역할을 모두 수행 가능하다.

생성자

생성자설명
DatagramSocket()소켓 객체 생성 후 사용 가능한 포트로 대기
DatagramSocket(DatagramSocketImpl impl)사용자가 지정한 SocketImpl 객체를 사용하여 소켓 객체만 생성
DatagramSocket(int port)소켓 객체 생성 후 지정된 포트로 대기
DatagramSocket(int port, InetAddress address)소켓 객체 생성 후 address와 port를 사용하는 서버에 연결
DatagramSocket(SocketAddress address)소켓 객체 생성 후 address에 지정된 서버로 연결

메소드

리턴 타입메소드설명
voidreceive(DatagramPacket packet)메소드 호출 시 요청을 대기하고, 만약 데이터를 받았을 때에는 packet 객체에 데이터를 저장
voidsend(DatagramPacket packet)packet 객체에 있는 데이터 전송
voidclose()소켓 연결을 종료

DatagramPacket

TCP에서는 스트림 객체를 얻어 데이터를 주고받았다면, UDP에서는 DatagramPacket 객체로 데이터를 주고받는다.

생성자

데이터를 받기 위한 생성자는 단 하나다.

생성자설명
DatagramPacket(Byte[] buf, int length)length의 크기를 갖는 데이터를 받기 위한 객체 생성
DatagramPacket(Byte[] buf, int length, InetAddress address, int port)지정된 address와 port로 데이터를 전송하기 위한 객체 생성
DatagramPacket(Byte[] buf, int length, int length, int offset)버퍼의 offset이 할당되어 있는 데이터를 전송하기 위한 객체 생성
DatagramPacket(Byte[] buf, int offset, int length, InetAddress address, int port)버퍼의 offset이 할당되어 있고, 지정된 address와 port로 데이터를 전송하기 위한 객체 생성
DatagramPacket(Byte[] buf, int offset, int length, SocketAddress address)버퍼의 offset이 할당되어 있고, 지정된 소켓 address로 데이터를 전송하기 위한 객체 생성
DatagramPacket(Byte[] buf, int length, SocketAddress address)지정된 소켓 address로 데이터를 전송하기 위한 객체 생성

메소드

리턴 타입메소드설명
byte[]getData()전송받은 데이터를 리턴
intgetLength()전송받은 데이터의 길이를 리턴

서버에서 소켓 연결

public void startServer() {
    DatagramSocket server = null;
    try {
        // 9999 포트로 소켓 생성
        server=new DatagramSocket(9999);

        // 데이터를 받기 위한 DatagramPacket 객체 생성
        int bufferLength = 256;
        byte[] buffer = new byte[bufferLength];
        DatagramPacket packet = new DatagramPacket(buffer, bufferLength);

        while(true){
            // 데이터를 받기 위해 대기
            server.receive(packet);

            // byte[]로 넘어온 데이터를 String으로 변환 후 출력
            int dataLength = packet.getLength();
            String data = new String(packet.getData(), 0, dataLength);
            System.out.println("Received data:" + data);

            // 넘어온 문자가 "EXIT" 면 종료
            if(data.equals("EXIT")) {
                break;
            }
        }
    } catch (Exception e) {
        ...
    } finally {
        // server.close();
    }
}

클라이언트에서 소켓 연결

public void sendDatagramData(String data) {
    try {
        // DatagramSocket 객체 생성
        DatagramSocket client = new DatagramSocket();

        // 데이터를 받을 서버 IP 설정
        InetAddress address = InetAddress.getByName("127.0.0.1");

        // 전송할 데이터를 byte[]로 변환
        byte[] buffer = data.getBytes();
        
        // 데이터를 전송하기 위한 DatagramPacket 객체 생성 및 전송
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length, address, 9999);
        client.send(packet);

        // 종료
        client.close();
        Thread.sleep(1000);
    } catch(Exception e) {
        e.printStackTrace();
    }
}

웹 페이지 요청

자바 API에서 제공하는 URL 클래스로 간단한 웹 페이지 요청을 할 수 있다. 하지만, 이 클래스는 연결에 대한 상세한 설정을 할 수 없기 때문에 권장하지 않는다.

일반적으로 Apache의 Http Components를 많이 사용한다.

💡 Http Compoents 홈페이지: http://hc.apache.org/

정리해 봅시다.

Q. TCP 통신과 UDP 통신의 차이는 무엇인가요?

Me: TCP 통신은 데이터를 받았는지를 확인하고, UDP는 데이터가 제대로 전달되었는지 확인하지 않는다

Q. 네트워크 통신시 포트 번호를 지정하는 이유는 무엇인가요?

Me: 특정 프로그램에 접속하기 위해 사용한다.

Q. ServerSocket 클래스의 용도는 무엇인가요?

Me: TCP 통신일 때, 서버에서 데이터를 주고 받기 위해 사용한다.

Q. ServerSocket 클래스의 accept() 메소드의 용도는 무엇인가요?

Me: 클라이언트가 연결될 때까지 대기하다가, 연결 되면 새로운 Socket 객체를 리턴한다.

Q. Socket 클래스의 용도는 무엇인가요?

Me: 데이터를 받거나 전송하기 위해 사용하는 클래스이다.

Q. DatagramSocket 클래스의 용도는 무엇인가요?

Me: UDP 통신에서 데이터를 주고받는 역할이다.

Q. DatagramPacket 클래스의 용도는 무엇인가요?

Me: UDP 통신에서 데이터를 주거나 받기 위해 사용한다.

질문

💡 책에 있는 내용이 아닙니다.

책을 읽으며 설명이 더 필요하거나, 추가로 궁금한 점에 대해 질문 형식으로 작성 후, 답을 구해보고 있습니다.
참고한 사이트나 영상은 [출처]로 달아두었으며, 오류 지적은 언제나 환영합니다.

질문

Q. 소켓이란 무엇인가?

Network socket

네트워크 상에서 데이터를 주고받기 위한 프로세스 간 통신의 엔드 포인트이다. OSI 7 계층에서 응용 계층에 속하는 프로세스들은 데이터 송수신을 위해 반드시 소켓을 거쳐 전송 계층으로 데이터를 전달해야 한다. 즉, 소켓은 전송 계층과 응용 프로그램 사이의 인터페이스 역할을 하며 떨어져 있는 두 호스트를 연결해준다.

특징

  • 소켓은 고유한 IP 주소와 포트 번호를 가지며, 이를 통해 특정 프로세스와 통신할 수 있다.
  • 주로 TCP/IP 도는 UDP/IP와 같은 프로토콜 기반으로 통신
    • TCP 소켓: 연결 지향적이고 신뢰성 높은 통신 제공
    • UDP 소켓: 연결이 없고 비연결성인 통신 제공
  • 서버 소켓과 클라이언트 소켓으로 나뉜다.
    • 서버 소켓: 클라이언트의 연결 요청을 수락
    • 클라이언트 소켓: 서버에 연결을 요청
  • 양방향 통신 지원

HTTP 통신과 소켓 통신의 차이

  • HTTP 통신은 애플리케이션 레벨의 프로토콜로, 웹 브라우저와 웹 서버 간 텍스트 기반의 데이터를 주고 받는데 사용한다.
  • HTTP는 주로 클라이언트가 서버에 요청을 보내면 서버가 그에 대한 응답을 반환하는 요청-응답 모델을 따른다.
  • HTTP는 상태 없는 프로토콜로, 각 요청은 이전 요청과 독립적이다.
  • 소켓 통신은 더 낮은 레벨인 네트워크 레벨에서 동작하며, TCP/IP 또는 UDP/IP와 같은 프로토콜을 기반으로 한다.
  • 소켓은 양방향 통신을 지원하며 데이터를 바이트 스트림으로 주고 받을 수 있다.
  • 소켓은 연결 기반의 프로토콜이므로, 연결이 유지되는 동안 상태를 유지할 수 있다.

HTTP 통신은 데이터 전달이 필요한 경우에만 요청을 보내는 상황에 유리하고, 소켓 통신은 실시간 동영상 스트리밍이나 온라인 게임 등 실시간 연결이 필요할 때 사용한다.

Q. 소켓에서 큐를 사용하는 이유는?

이건 개인적으로 낸 결론인데, 자바의 신 자료구조 설명에서 서버에 접속 중인 사용자들이, 가장 먼저 들어왔는데 순번이 밀려 뒤에 들어온 사람이 먼저 나가면 안되지 않겠냐는 이야기를 했었다.

그래서 먼저 들어온 게 먼저 나가는 큐를 사용하는 거 같다.

Q. 프록시란 무엇인가?

💡 아래 참고 사이트에서 더 자세할 설명과 예제를 볼 수 있다.

Proxy Pattern

사전적인 의미는 “대리인”이라는 뜻으로, 어떠한 대상 객체에 대한 인터페이스 역할을 하는 디자인 패턴을 말한다.

즉, 클라이언트가 직접 대상 객체에 접근하는 것이 아니라 중간에 프록시라는 대리인을 거쳐 간접적으로 접근하게 하는 패턴이다.

객체지향 설계의 원칙 중 하나인 DIP(의존성 역전 원칙)을 따르며, 코드의 유연성과 확장성을 향상시키는데 기여한다.

이는 대상 클래스가 민감한 정보를 가지고 있거나, 인스턴스화 하기에 무겁거나, 원본 객체를 수정할 수 없는 상황일 때를 극복하기 위해 사용한다.

Spring Framework의 AOP와도 밀접한 관련이 있다.

Proxy Object

클라이언트와 실제 대상 객체 사이에 위치하는 중간 객체를 프록시 객체라고 부른다.

프록시 패턴의 효과

  • 보안 제어
    • 클라이언트의 요청을 검사하여 권한이 있는 경우에만 실제 객체에 접근하도록 허용한다.
  • 지연 로딩 (lazy loading)
    • 대상 객체의 생성 비용이 비쌀 경우, 실제로 필요한 시점까지 객체의 생성 또는 초기화를 지연시킬 수 있다.
  • 캐싱
    • 프록시는 실제 객체에 접근하여 결과를 캐시하고, 동일한 요청이 여러 번 발생했을 때 캐시된 결과를 반환하여 성능을 향상시킬 수 있다.
  • 모니터링 및 로깅
    • 클라이언트의 요청과 실제 객체의 동작을 모니터링하거나 로깅하기 위해 사용될 수 있다.
    • 이를 통해 디버깅이나 성능 측정 등을 수행할 수 있다.

Q. 호스트와 어드레스의 차이는 무엇인가?

어드레스

  • 주소를 의미하며, 네트워크에서 어떤 대상을 식별하기 위한 값을 나타낸다.
  • 대표적인 예로 IP 주소가 있다.

호스트

  • 컴퓨터, 서버, 라우터 등과 같은 실제적인 네트워크 디바이스를 의미한다.
  • 각 호스트는 IP 주소 또는 호스트 명을 통해 식별된다.
  • “www.naver.com”은 호스트 명이며, 이 호스트는 특정 IP 주소에 매핑되어 네트워크에서 식별된다.
  • 호스트는 여러 어드레스를 가질 수 있다.

요약

호스트(Host)는 네트워크에 연결된 실제 장치를 나타내는 용어이며, 어드레스(Address)는 해당 장치를 식별하기 위한 값을 나타내는 용어이다.

Q. SSL 통신의 기본 포트

SSL 통신의 간략한 설명

SSL(Secure Socket Layer)은 컴퓨터 네트워크 상에서 정보를 안전하게 전송하기 위한 보안 프로토콜 중 하나이다. 데이터를 암호화하고, 데이터의 기밀성과 무결성을 보장하며, 클라이언트와 서버 간의 안전한 통신을 보장한다.

기본 포트

SSL는 과거에 사용되던 보안 프로토콜로, HTTPS의 기본 포트로 443을 사용한다.

SSL의 후속으로 개발된 TLS(Transport Layer Security)도 기본 포트로 443을 사용한다.

물론, 이 기본 포트는 웹 서버의 설정을 수정하거나 프록시 서버를 통해 특정 포트로 전달하는 방식 등으로 변경할 수 있다.

Q. URL 클래스와 Http Components

💡 무언가 더 알아야 하는 내용이 있을까 싶어 찾아봤지만 본문에 나온 내용과 별반 다를 바 없다.

URL 클래스

URL 클래스는 URL을 파싱하고 간단한 웹 리소스에 대한 연결을 만드는 데 사용한다.

기본적인 URL 관리 및 접속 기능을 제공하지만, HTTP에 특화된 고급 기능은 제공하지 않는다.

Http Components

Apache 에서 제공하는 HTTP 통신을 위한 라이브러리이다. 주요 구성 요소로는 HttpClient와 HttpServer가 있으며, 다양한 기능을 지원한다.

참고 사이트

네트워크 소켓

소켓이란? 종류, 통신 흐름, HTTP통신과의 차이

Java 소켓에 대한 연결 시간 초과 VS 읽기 시간 초과

💠 프록시(Proxy) 패턴 - 완벽 마스터하기

[Java] 프록시패턴 (Proxy Pattern)

SSL과 TLS 비교 - 통신 프로토콜 간의 차이점 - AWS

SSL인증서는 443포트만 사용해야 하나요? > 자주묻는질문 | SSL 인증서 써트코리아- DigiCert 공식 제휴사

profile
책을 읽거나 강의를 들으며 공부한 내용을 정리합니다. 가끔 개발하는데 있었던 이슈도 올립니다.

0개의 댓글