본문 바로가기

Computer Science/네트워크

HTTP 요청 전송 및 분석 - 소켓 프로그래밍, TCP 전송의 원리 (Java)

안녕하세요, 저번 포스팅에서 HTTP에 대해서 알아보았습니다. 다시 한번 정리하자면, HTTP는 인터넷에서 데이터를 주고받기 위한 프로토콜 중 하나로, 웹 브라우저와 웹 서버 간의 통신을 담당합니다. HTTP는 클라이언트가 서버에 요청(request)을 보내고, 서버는 클라이언트에 응답(response)을 보내는 구조로 동작합니다. 이러한 구조를 바탕으로 웹 사이트를 방문하거나 파일을 다운로드하게 됩니다. 자세한 내용은 아래 포스팅을 참고바랍니다.

https://porolog.tistory.com/25

 

HTTP 요청 전송 및 분석 with HttpRequest (Java)

이번 포스팅은 브라우저에 URL을 입력했을 때, 일어나는 일 중, HTTP로 요청을 보내고, 그 결과를 서버로부터 받는 http 프로토콜의 작동을 프로토타입으로 구현해보는 것에 대한 내용입니다. 그 전

porolog.tistory.com

 

하지만, HTTP는 요청과 응답에 대한 프로토콜이며, 아직은 어떻게 구체적으로 서버와 클라이언트가 파일을 주고 받으며 통신하는지 불명확하실겁니다. 이러한 이해를 돕기 위해, 케이블에서 0,1의 흐름으로 정보를 전달하는 것처럼 매우 Low-level의 작동원리에서부터, 우리가 웹 브라우저로 확인하는 것과 같은 어플리케이션 단까지의, 통신의 모든 단계에서 일어나는 상호작용들을 계층 별로 분류한 OSI 7계층 모델의 개념을 설명드리겠습니다. 저번에 다루었던 HTTP는 OSI 7계층 중 응사용자와 가장 가까운 계층인 응용계층에 위치하며, 사용자의 요구에 따라 데이터를 처리하고 응용 프로그램과 직접적으로 상호작용합니다. 그리고 OSI 7계층 중 추가로 중요하게 다룰 계층인 TCP(Transmission Control Protocol)는 OSI 7계층에서 전송 계층(Transport Layer)에 위치합니다. 전송 계층은 데이터를 전송하기 위한 계층으로, 네트워크 계층에서 전달받은 데이터를 상위 계층으로 전송하거나, 상위 계층에서 받은 데이터를 하위 계층으로 전송하는 역할을 합니다. 따라서 이번 포스팅에서는 OSI 7계층과 계층 중에서 TCP/UDP 프로토콜에 대해 다룰 것이며, 그리고 이러한 데이터를 송/수신할 수 있는 인터페이스인 소켓에 대해서도 다루겠습니다. 소켓으로 HTTP 전송을 구현해보는 것은, 웹 통신의 작동 원리의 이해에 도움이 될 것입니다.

 

OSI 7 계층


가장 먼저, OSI 7계층이란 무엇인지부터 살펴보겠습니다.

 

OSI(Open Systems Interconnection) 7계층은 네트워크 통신에서 데이터를 주고받는 과정을 일곱 개의 계층으로 나누어서 설명한 네트워크 표준 모델입니다. 이는 국제표준화기구(ISO)에서 개발한 모델로, 다양한 종류의 네트워크에서 상호 연동성과 호환성을 확보하기 위해 만들어졌습니다. 각 계층은 서로 다른 역할을 수행하며, 상위 계층에서는 하위 계층이 제공하는 서비스를 이용합니다. 

피라미드 구조처럼, 송신 시에는 보낼 데이터에 응용 계층에서부터 부가적인 정보의 모음인 헤더를 붙여, 제일 하위 계층인 물리 계층에서는 데이터와 헤더의 모음인 전송 데이터가 됩니다. 반대로, 수신 시에는 역순으로 헤더를 분석해가며 전송된 데이터로부터 최종 데이터를 얻어냅니다. 7계층은 각각 아래와 같습니다.

OSI 7계층

1. 물리 계층(Physical Layer)

  • 데이터 전송에 필요한 물리적인 매체(유선, 무선 등)에 대한 규격을 정의합니다.
  • 전기적, 기계적, 기하학적인 특성과 물리적 전송 방식을 결정합니다.

2. 데이터 링크 계층(Data Link Layer)

  • 물리적 매체 상에서 데이터를 전송하기 위한 논리적인 경로를 설정하고 오류를 검출하고 수정하는 기능을 담당합니다.
  • 프레임(Frame) 단위로 데이터를 전송하며, 흐름 제어와 오류 제어 기능을 제공합니다.

3. 네트워크 계층(Network Layer)

  • 여러 개의 논리적인 네트워크를 연결하는 기능을 담당합니다.
  • 패킷(Packet) 단위로 데이터를 전송하며, 라우팅(Routing) 기능을 수행합니다.

4. 전송 계층(Transport Layer)

  • 데이터의 전송을 관리하며, 상위 계층에서 받은 데이터를 하위 계층으로 전송하거나, 하위 계층에서 받은 데이터를 상위 계층으로 전송하는 역할을 합니다.
  • TCP(Transmission Control Protocol), UDP(User Datagram Protocol) 등의 프로토콜을 이용하여 신뢰성 있는 데이터 전송을 보장합니다.

5. 세션 계층(Session Layer)

  • 데이터를 전송하는 두 시스템 간의 세션(Session)을 설정하고 유지하는 기능을 담당합니다.
  • 세션 설정, 유지, 종료, 동기화 등의 기능을 수행합니다.

6. 표현 계층(Presentation Layer)

  • 데이터의 표현 방법을 정의하고, 데이터를 암호화, 압축, 변환 등의 처리를 수행합니다.
  • 다양한 데이터 형식을 표준화된 형식으로 변환하는 역할을 합니다.

7. 응용 계층(Application Layer)

  • 최종 사용자가 네트워크를 통해 수행하는 서비스와 응용 프로그램에서 사용하는 데이터 형식을 정의합니다.
  • HTTP, FTP, SMTP, DNS 등 다양한 프로토콜을 이용하여 응용 프로그램 간의 통신을 지원합니다.

이러한 7계층 모델은 네트워크 통신에서 각 계층이 담당하는 역할을 명확하게 나누어 관리하기 쉽고, 서로 다른 기술의 통신 기능을 연동하여 네트워크를 효율적으로 운영할 수 있도록 도와줍니다. 따라서 이러한 7계층 모델은 네트워크 설계와 관리, 문제 해결 등에서 중요한 개념으로 사용됩니다.


TCP/IP 4계층


OSI 7계층과 비슷한 계층 모델로 TCP/IP 4계층 모델이 있습니다. 다루고 있는 내용은 비슷하지만, TCP/IP 4계층 모델은 OSI 7계층과 달리 실제로 인터넷에서 널리 사용되는 프로토콜입니다. 특히, 인터넷에서 데이터를 전송하기 위해 사용하는 프로토콜인 IP 프로토콜은 TCP/IP 프로토콜 스택에서 동작하며, 인터넷에서 데이터를 안정적으로 전송하기 위해 TCP 프로토콜도 많이 사용됩니다.

 

반면에 OSI 7계층은 네트워크를 설계하고 구현하는데 있어서 참고할 수 있는 개념적인 표준 모델입니다. 이 모델을 기반으로 통신 시스템을 설계하면 다양한 종류의 하드웨어와 소프트웨어 간의 상호 운용성을 확보할 수 있습니다. 따라서 네트워크 설계자나 개발자들은 OSI 7계층 모델을 참고하여 네트워크를 구현하고 통신 시스템을 개발하게 됩니다.

 

물론 TCP/IP 프로토콜 스택과 OSI 7계층은 서로 간의 관계가 있으며, TCP/IP 프로토콜 스택도 7계층 모델과 유사한 계층 구조를 가지고 있습니다. 그러나, TCP/IP 프로토콜 스택은 실제로 인터넷에서 사용되는 프로토콜이기 때문에 OSI 7계층 모델에 비해 더 많이 사용되며, 실제로 네트워크를 구현하고 운용하는 데 있어서 더 큰 역할을 합니다. OSI 7계층이 네트워크의 전 계층을 다루기 때문에 조금 더 개념적, TCP/IP는 중요한 계층을 다루기에 실무에 가깝다고 이해하셔도 좋을 것 같습니다.

OSI 7계층 역할 TCP/IP 4계층
7. 응용 계층 응용 프로그램에서 사용하는 프로토콜 4. 응용 계층
6. 표현 계층 데이터 표현과 암호화
5. 세션 계층 세션 관리
4. 전송 계층 데이터 전송의 신뢰성 보장 3. 전송 계층
3. 네트워크 계층 데이터 패킷 라우팅 2. 인터넷 계층
2. 데이터 링크 계층 물리적 매체를 통한 데이터 전송과 오류 검출 1. 네트워크 인터페이스 계층
1. 물리 계층 물리적 매체와 전기적 특성

 

TCP란?


위에서 OSI 7계층을 설명하면서 언급했던 프로토콜 중 TCP에 대해서 알아보겠습니다.

 

TCP(Transmission Control Protocol)는 인터넷 상에서 데이터를 안정적으로 전송하기 위해 사용되는 프로토콜입니다. 아까 HTTP도 통신에 사용되는 프로토콜이라고 했던 것 같고, IP도 전송에 관련된 프로토콜이라고 들었던 것 같은데, 정확히 어떻게 다른걸까요?

차이점은, TCP는 데이터의 안정적 전송을 위한 프로토콜이며, HTTP(Hypertext Transfer Protocol)는 웹 브라우저와 웹 서버 간에 통신하기 위해 사용되는 프로토콜이며, IP(Internet Protocol)는 인터넷에서 데이터를 전송하기 위한 프로토콜입니다. 즉, 프로토콜 간에는 목적이 다르며, 작용하는 계층이 다르다는 점을 인지해야합니다. IP는 가장 Low-level에서 라우팅을 통해 최종 목적지로 데이터의 전송이 가능하도록 하며, TCP는 IP 위에서 작동하며 안정적인 전송을 보장하며, HTTP는 다른 프로토콜들과 협력해 최종적으로 클라이언트와 서버 간 통신이라는 어플리케이션 목적을 달성하게 돕는 것입니다.

 

다시 한 번, 각 프로토콜에 대해 조금 자세히 설명해보겠습니다.

IP는 인터넷에서 데이터를 전송하기 위한 프로토콜로, 데이터를 송신자에서 수신자로 라우팅하는 역할을 합니다. IP는 데이터를 패킷으로 나누어 전송하며, 패킷에는 수신자의 IP 주소와 출발지 IP 주소가 포함됩니다. 이를 통해 데이터를 올바른 수신자에게 전달하며, TCP와 함께 사용되어 안정적인 데이터 전송을 보장합니다.

TCP는 IP 위에서 동작하는 프로토콜로, 데이터의 손실이나 오류를 감지하고 복구하는 기능을 제공합니다. 이를 위해 TCP는 데이터를 패킷(packet)이라는 작은 조각으로 나누어 전송하며, 이 패킷이 제대로 전송되었는지 확인하는 과정을 거칩니다. 만약 어떤 패킷이 전송되지 않거나 손상되었다면, TCP는 해당 패킷을 재전송하여 안정적인 데이터 전송을 보장합니다.

HTTP는 TCP를 기반으로 동작하며, 웹 브라우저와 웹 서버 간에 데이터를 주고받습니다. HTTP는 웹 브라우저에서 사용자가 요청한 웹 페이지의 정보를 웹 서버에게 전송하고, 이에 대한 응답을 받아 사용자에게 보여줍니다. HTTP는 TCP가 제공하는 안정적인 데이터 전송을 이용하여 웹 페이지의 정보를 전송하며, TCP의 재전송 기능을 통해 데이터의 손실이나 오류를 방지하는 것입니다. 이제 TCP 프로토콜이 어떤 역할을 하는지 이해가 되셨을 것 같습니다. 그럼 TCP 프로토콜의 특징에 대해서 알아보고, TCP 프로토콜과 안정적 전송의 측면에서 비슷한 역할을 수행할 수 있는, UDP 프로토콜에 대해서도 알아보겠습니다.

 

TCP의 특징


1. 연결 지향 (Connection-Oriented)

TCP는 연결 지향 프로토콜로, 데이터를 전송하기 전에 먼저 연결 설정 단계를 거치며, 연결이 설정되면 안정적인 데이터 전송이 가능해집니다. 이때, TCP는 3-way handshake라는 과정을 거쳐 연결을 설정합니다.

예를 들어, A 컴퓨터에서 B 컴퓨터로 데이터를 전송하려면, A 컴퓨터가 먼저 B 컴퓨터에게 SYN(Synchronize) 메시지를 보내 연결을 요청합니다. B 컴퓨터는 SYN에 대한 ACK(Acknowledgment)와 SYN 메시지를 보내 응답합니다. 이후, A 컴퓨터는 ACK 메시지를 보내 연결이 설정됩니다.

 

연결을 확인하는 3-way handshake

반대로, 연결을 종료할 때는, 4-way handshake 과정을 통해 연결을 해제합니다.

  1. 연결 종료 요청 (FIN) : 연결 종료 요청을 보내는 주체는 일반적으로 클라이언트입니다. 클라이언트는 서버에게 연결 종료를 요청하기 위해 FIN 플래그를 전송합니다.
  2. 연결 종료 응답 (ACK) : 서버는 클라이언트의 연결 종료 요청을 받고, 응답으로 ACK 플래그를 전송합니다. 이때, 서버는 아직 클라이언트에게 보내야 할 데이터가 남아있다면 이를 전송합니다. 예를 들면, 서버에서 A라는 작업을 하고 있는 중에, 클라이언트가 통신을 종료할 것이라는 신호를 받게된다면, 클라이언트에게 A라는 작업만 종료하고 알려줄지, 혹은 바로 종료할지 선택할 수 있겠죠? 어떤 경우든 서버가 정말로 통신을 종료할 준비가 되었다는 FIN 신호를 보낼 때까지, 클라이언트는 Time-wait 상태에 들어갑니다.
  3. 데이터 전송 완료 (FIN) : 서버가 클라이언트에게 보내야 할 데이터를 모두 전송하고, 연결을 종료하기 위해 FIN 플래그를 전송합니다.
  4. 연결 종료 확인 (ACK) : 클라이언트는 서버의 연결 종료 요청을 받고, 응답으로 ACK 플래그를 전송합니다. 이때, 클라이언트가 아직 서버에게 보내야 할 데이터가 남아있다면 이를 전송합니다. 이로써 연결 종료가 완료됩니다. 서버는 클라이언트의 마지막 ACK를 받고 종료되며, 클라이언트는 서버가 마지막으로 보낸 패킷이 유실되거나 늦게 도착하는 경우에 대비해 일정 시간을 대기한 후 종료됩니다.

안전한 연결 해제를 위한 4-way handshake

 

2. 신뢰성 (Reliable)

TCP는 데이터 전송의 신뢰성을 보장합니다. 데이터를 전송할 때, 패킷 단위로 나누어 전송하며, 각 패킷이 정상적으로 전송되었는지 확인합니다. 이때, 이전 패킷이 제대로 전송되지 않으면 다음 패킷을 전송하지 않고 재전송을 요청합니다. 이를 통해 데이터의 손실이나 오류를 최소화하며 안정적인 데이터 전송을 보장합니다. 이후에 TCP 헤더에 대해서 다시 설명하겠지만, TCP 헤더에는 전송의 순서를 알 수 있는 Sequence Number에 대한 정보가 포함되어 있습니다. 이를 통해서 패킷이 순서대로 들어오지 않은 경우 패킷이 유실되었다고 판단해서 재전송을 요청할 수 있게 됩니다. 그리고, 이러한 Sequence Number에 대해서 클라이언트, 서버 간 전달이 hand-shake 과정 중에 일어납니다.

 

3. 흐름 제어 (Flow Control)

TCP는 수신자의 처리 속도를 고려하여 데이터의 흐름을 조절하는 흐름 제어 기능을 제공합니다. 수신자는 데이터를 처리할 수 있는 속도보다 더 많은 데이터를 수신하면 데이터의 손실이나 오류가 발생할 수 있습니다. 이를 방지하기 위해 TCP는 수신자가 처리할 수 있는 데이터의 양을 파악하여 데이터의 전송 속도를 조절합니다.

 

4. 혼잡 제어 (Congestion Control)

TCP는 인터넷 상에서 데이터 전송이 혼잡해지는 경우, 네트워크에 불필요한 부하를 줄이기 위해 혼잡 제어 기능을 제공합니다. 혼잡 제어 기능은 데이터 전송 속도를 동적으로 조절하여 네트워크 혼잡을 방지합니다. 예를 들어, 여러 사용자가 동시에 대용량 파일을 다운로드하면 네트워크 혼잡이 발생할 수 있습니다. 이때, TCP는 데이터 전송 속도를 감소시켜 네트워크 망에 발생하는 부하를 일시적으로 줄일 수 있습니다. 그리고 혼잡이 어느정도 제어가 된 상태에서 다시 전송 속도를 증가시킬 수 있겠죠. 이러한 동적 제어를 TCP에서 제공합니다.

 

다음으로, TCP 프로토콜의 헤더 정보에 대해서 살펴보겠습니다.


TCP 헤더 구조


아래 그림은 TCP 헤더의 구조에 대한 간략한 그림입니다. 신뢰성 있는 데이터 전달을 위해 여러 가지 헤더 데이터가 많이 포함되는 것을 알 수 있습니다. 아래에서 UDP의 헤더와 비교해서 보면 각 프로토콜의 특징을 좀 더 이해할 수 있습니다.

 0                   1                   2                   3   
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |         |E|U|A|P|R|S|F|                               |
| Offset|Reserved |C|R|C|S|S|Y|I|            Window             |
|       |         |E|G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

  • Source Port : 송신측의 포트 번호
  • Destination Port : 수신측의 포트 번호
  • Sequence Number : 전송된 데이터의 순서 번호 네트워크가 불안하여 패킷을 분실하거나 지연으로 순서가 어긋날 수 있기 때문에 sequence number로 데이터를 재배열할 수 있습니다.
  • Acknowledgment Number : 수신한 데이터의 순서 번호를 확인하기 위한 응답 번호 다음 세그먼트를 수신할 준비가 되었다는 사실을 알립니다. 모든 데이터가 수신되었다는 것을 나타내는 묵시적 확인 메시지 역할을 합니다.
  • Data Offset : 헤더의 길이를 나타냅니다.
  • Reserved : 예약 필드로, 0으로 설정됩니다.
  • Flags : TCP의 특성을 지정하는 6개의 플래그(bit)로, SYN, ACK, FIN 등이 포함됩니다.
    • ACK(Acknowledgment) : 확인 응답 메시지
    • SYN(Synchronize) : 가상 회선이 처음 개설될 때 두 시스템의 TCP 소프트웨어는 의미 있는 확인 메시지를 전송하기 위해 일련 번호를 서로 동기화해야 한다.
    • FIN(Finish) : 작업이 끝나고 가상 회선을 종결하고자 할 때 사용
  • Window : 송신측이 전송할 수 있는 수신 버퍼의 크기를 나타낸다.
  • Checksum : 데이터의 무결성을 검사하는 체크섬 값
  • Urgent Pointer : 긴급한 데이터가 전송될 경우 지정되는 필드입니다.
  • Options : TCP 헤더의 옵션을 설정할 수 있는 필드입니다.
  • Padding : 옵션 필드의 크기가 일정 크기보다 작을 경우 채워집니다.

UDP


데이터를 데이터그램 단위로 처리하는 프로토콜을 의미한다. 데이터그램은 독립적인 관계를 지니는 패킷이라는 의미이다. TCP와 다르게 논리적인 경로가 없습니다. 따라서 각각 다른 경로로 전송될 수 있습니다.

UDP의 특징

  • 비연결형 서비스로, 데이터그램 방식을 제공합니다. 비연결형이라는 의미는 TCP와 같이 연결을 상호 확인하는 handshake 절차가 없다는 의미입니다.
  • 정보를 주고 받을 때 정보를 보내거나 받는다는 신호절차를 거치지 않습니다.
  • 최소한의 오류만 검출합니다.
  • 신뢰성이 낮습니다.
  • TCP보다 속도가 빠릅니다.

UDP 헤더

 0      7 8     15 16    23 24    31  
+--------+--------+--------+--------+ 
|     Source      |   Destination   | 
|      Port       |      Port       | 
+--------+--------+--------+--------+ 
|                 |                 | 
|     Length      |    Checksum     | 
+--------+--------+--------+--------+ 
|                                   | 
|               data                | 
+-----------------------------------+

위에서 살펴보았던 TCP 헤더에 비해서 매우 간략한 것을 확인할 수 있습니다.

TCP vs UDP

위의 내용을 요약해서, TCP와 UDP의 특징을 비교해보겠습니다. 장/단점이 있으므로 서비스에 따라서 적절한 프로토콜을 사용하는 것이 중요합니다.

연결성 연결 지향 (Connection-oriented) 비연결 지향 (Connectionless)
신뢰성 신뢰성 있는 데이터 전송 보장 신뢰성 없는 데이터 전송
흐름 제어 흐름 제어 및 혼잡 제어 기능 지원 흐름 제어 및 혼잡 제어 기능 미지원
속도 느리다 빠르다
패킷 손실 재전송 기능 지원으로 패킷 손실 최소화 패킷 손실 가능성이 높다
전송 순서 전송 순서 보장 전송 순서가 보장되지 않는다.
대역폭 대역폭이 낮아도 안정적으로 데이터 전송 가능 대역폭이 높아야 더욱 빠르게 데이터 전송 가능
사용 예시 파일 전송, 이메일 등 실시간 스트리밍, DNS 등

TCP/IP 소켓 통신


마지막으로, TCP/IP를 기반으로 하는 소켓 프로그래밍으로 HTTP 요청을 해보겠습니다. 자바의 Socket API는 TCP/IP를 기반으로 구현되어 있습니다. TCP를 사용하여 안정적인 데이터 전송을 제공하고, 네트워크 통신의 세부 사항을 추상화하여 개발자가 상위 수준에서 통신을 구현할 수 있도록 도와줍니다. 클라이언트 측에서 TCP 소켓을 만들기 위해서는 Socket 클래스를 사용하면 되고, 서버 측에서 연결을 수신하기 위해서는 ServerSocket 클래스를 사용하면 됩니다.  이번 포스팅에서는 클라이언트의 입장에서 보내는 상황을 가정해서 구현을 진행해보겠습니다. 현업에서는 Socket보다 상위 Level의 API가 많이 제공되기 때문에 좀 더 간편하게 구현을 할 수 있습니다.

 

먼저, 소켓이란 무엇인지부터 알아보겠습니다. 소켓은 프로세스가 네트워크 세계로 데이터를 내보내거나, 데이터를 받기 위한 엔드포인트로서 실제적인 창구 역할을 수행합니다. 데이터를 보내거나 받기 위해서는 소켓을 열어서 소켓에 데이터를 쓰거나, 읽어야한다는 것이죠. 즉, 소켓은 두 호스트를 연결하는 도구로써 인터페이스 역할을 하며 데이터를 주고 받는 구조체로서 통로가 생성됩니다.

 

여기서 엔드포인트가 되기 위해서는 어떤 정보가 필요할까요? 어디에서 어디로 가는지가 가장 중요하겠죠? 그 정보가 바로 IP 주소와 포트 번호의 조합입니다. 전 세계 컴퓨터에 부여된 고유 식별 주소인 IP 주소와 호스트 내부적으로 어떤 프로세스인지 결정하는 포트 번호로 고유한 주소 정보가 형성된다고 볼 수 있습니다. 아주 간단히 말하면, IP 주소로 어느 컴퓨터인지 찾고, Port 번호로 컴퓨터 내 어느 프로그램에 위치하고 있는지 알 수 있다는 것입니다.

 

그래서 소켓과 HTTP 요청이 무슨 상관이 있다는 걸까요? 

바로, HTTP는 일종의 소켓 통신이라는 점입니다. HTTP 통신은 IP와 Port 번호를 활용하는 TCP 레이어 위에 올라간 소켓 통신 방식위에서 구현됩니다. HTTP는 내부 구현으로는 소켓을 이용하지만 다른 점은 HTTP는 요청 응답 모델로서, 한쪽에서만 요청에 대한 응답을 하는 프로토콜로 구분되기 때문에 별도의 프로토콜로 구분됩니다.

 

소켓 프로그래밍 방법

소켓 프로그래밍은 아래 과정을 따릅니다. 아래 과정은 Java에서는 Socket API로 간단하게 구현할 수 있습니다.

  1. 서버 소켓 생성: 서버는 소켓을 열어 클라이언트의 연결 요청을 기다립니다. 
  2. 바인딩: 서버 소켓은 IP 주소와 포트 번호를 바인딩합니다. 이를 통해 클라이언트가 서버에 접근할 수 있습니다.
  3. 연결 대기: 서버 소켓은 클라이언트의 연결 요청을 대기합니다.
  4. 클라이언트 연결 수락: 서버 소켓이 연결 요청을 수락하면, 새로운 소켓을 생성합니다. 이 소켓은 클라이언트와 서버 간의 통신에 사용됩니다.
  5. 데이터 송수신: 클라이언트와 서버는 소켓을 통해 데이터를 주고받습니다. 이를 위해 소켓은 스트림 형식으로 데이터를 송수신합니다. 송신은 OutputStream에 데이터를 write하는 방식으로, 수신은 소켓의 InputStream으로부터 데이터를 read함으로써 가능합니다.
  6. 연결 종료: 클라이언트 또는 서버가 소켓을 닫으면 연결이 종료됩니다.

소켓 클라이언트 측 예제 코드입니다.

// 소켓 클라이언트 예제 코드
public class Client {
    public static void main(String[] args) {
        try {
            // 서버에 연결
            Socket socket = new Socket("localhost", 5000);

            // 메시지 전송을 위한 스트림 생성
            OutputStream os = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(os);
            BufferedWriter bw = new BufferedWriter(osw);

            // 메시지 작성 및 전송
            String message = "Hello, Server!";
            bw.write(message);
            bw.flush();
            System.out.println("클라이언트가 메시지를 보냈습니다: " + message);

            // 서버로부터의 응답 받기
            InputStream is = socket.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String response = br.readLine();
            System.out.println("서버로부터의 응답 받음: " + response);

            // 소켓과 스트림 닫기
            bw.close();
            osw.close();
            os.close();
            br.close();
            isr.close();
            is.close();
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

접근 방법

따라서, 아래의 클래스 다이어그램의 구조를 작성하고 구현하였습니다. 흐름은 아래와 같습니다.

1. 사용자로부터 요청을 보낼 URI를 읽어들인다.

2. URI의 도메인 주소로부터 DNS 서버에서 조회한 IP 주소와 포트 번호를 확인해서 서버 소켓과 바인딩된 클라이언트 소켓을 생성한다.

3. 소켓에 데이터를 Request 양식에 맞게 기록해서 서버로 요청을 전송한다.

4. 서버에서 받은 응답을 소켓으로부터 읽어서 parse 한 후 Response 객체 형태로 데이터를 저장한다.

5. 사용자에게 Response 객체에 저장된 데이터를 적절한 형식으로 출력한다.

 

public static void main(String[] args) throws IOException {
        // URL 입력 및 생성
        URI uri = View.readUri();

        // DNS IP, Port 조회
        InetAddress address = DnsFetcher.fetchFirstIpAddress(uri);
        int port = DnsFetcher.fetchPortNumberOrDefault(uri);

        // Socket Open
        Socket socket = new Socket(address, port);

        // 데이터 쓰기
        writeToSocket(socket, uri);

        // 데이터 읽기
        readFromSocket(socket);

        // Socket Close
        socket.close();
}

 

구현 결과

아래는 최종적으로 제 블로그로 요청을 보낸 결과입니다.
> URL을 입력하세요.
https://porolog.tistory.com/25
(DNS Lookup...)
IP 조회 : 211.249.222.33

TCP Connection : 211.249.222.33
포트 번호가 지정되어 있지 않습니다. 80번 포트를 사용합니다.

> HTTP 요청 메시지
GET /25 HTTP/1.1
Accept: text/html
User-Agent: Mozilla/5.0
Connection: close
Host: porolog.tistory.com


> HTTP 응답 메시지
HTTP/1.1 200 
Date: Thu, 16 Feb 2023 15:48:10 GMT
Content-Type: text/html;charset=UTF-8
Content-Length: 68759
Connection: close
Vary: Accept-Encoding
T_USERID: febb5ddd8748cb3cf80c7857378364938552fb15
Set-Cookie: REACTION_GUEST=a37040142ff6bdbfe5c5ab10c8de7a0f2f08378a
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0

> HTTP 메시지 Body
<!doctype html>
<html lang="ko">
.
.
.

WireShark 분석

응답 메시지를 분석하는 것 외에도, 네트워크 요청을 분석할 수 있는 툴이 많습니다. 그 중, wireShark라는 네트워크 모니터링 툴을 사용해서 HTTP 요청이 정상적으로 이루어지는 것인지 확인했습니다.

3 way handshake - HTTP 요청/응답 - 4 way handshake 순으로 통신

위 캡처에서 확인 할 수 있듯이, 가장 먼저 3-way handshake가 TCP 계층에서 발생했으며, 그 이후 HTTP 요청 송신, 수신이 차례로 일어났습니다. (위 경우에는 URL에 따른 Bad Request 발생) 그리고 통신을 마친 후, TCP에서 4-way handshake를 통해서 소켓이 정상 종료됨을 확인할 수 있었습니다.

 

HTTP 전송을 구현하는 코드를 작성해보면서 OSI 7계층, TCP, UDP, 소켓 등을 다뤄보았습니다. OSI 7계층은 인터넷을 구성하는 핵심 기술 중 하나로, 계층적인 구조로 인터넷의 통신을 관리합니다. TCP는 OSI 7계층에서 전송 계층에 위치하며, 신뢰성 있는 데이터 전송을 보장합니다. UDP는 TCP와 달리 신뢰성이 떨어지지만, 빠른 속도로 데이터를 전송할 수 있습니다. 소켓은 TCP, UDP와 같은 전송 프로토콜을 사용하여 데이터를 송수신할 수 있는 인터페이스입니다. 이러한 내용을 바탕으로 소켓으로 HTTP 요청 송/수신을 구현해보았습니다. 저는 개인적으로 인터넷이 어떻게 동작하는지 이해하는 데 큰 도움이 되었는데, 여러분들도 한 번 해보시길 적극 권장드립니다.