HTTP Persistence Connection

Snoop So·2023년 8월 20일
1

HTTP Persistence Connection이란

HTTP 지속 연결(Persistent Connection)은 여러 HTTP 요청 및 응답을 처리할 때 단일 연결을 사용하므로 통신의 효율성을 향상시키는 메커니즘이다. 이렇게 설명하면 이해하기 어려운데, 결국 header의 Connection 항목의 value가 'keep-alive'인 것을 뜻한다.

그런데 http 1.1은 Connection이 기본적으로 keep-alive로 설정되어 있다. 그런데 대체 keep-alive 상태라는 것은 무엇을 의미할까? 반대로, close 상태라면 어떤 일이 일어날까?

개념 정리: HTTP와 TCP

본격적으로 들어가기 전에 우선 HTTP와 TCP에 대해 알아보자. HTTP는 웹 브라우저와 웹 서버 간 HTML, JS 등과 같은 데이터를 주고 받는 데 사용된다. TCP는 이런 데이터들을 패킷으로 나누어 전송하고, 다시 재조립하는 역할을 한다. TCP의 데이터 패킷의 예시는 다음과 같다.

0000   02 00 00 00 45 00 00 40 00 00 40 00 40 06 00 00   ....E..@..@.@...
0010   7f 00 00 01 7f 00 00 01 d0 88 0b b8 57 4a 03 60   ............WJ.`
0020   00 00 00 00 b0 02 ff ff fe 34 00 00 02 04 3f d8   .........4....?.
0030   01 03 03 06 01 01 08 0a bb f9 72 40 00 00 00 00   ..........r@....
0040   04 02 00 00                                       ....

위와 같은 데이터 패킷을 해독해보면 다음과 같은 데이터들이 들어있다.

Frame 1: 68 bytes on wire (544 bits), 68 bytes captured (544 bits) on interface lo0, id 0
    Section number: 1
    Interface id: 0 (lo0)
    Encapsulation type: NULL/Loopback (15)
    Arrival Time: Aug 21, 2023 01:47:55.602152000 KST
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1692550075.602152000 seconds
    [Time delta from previous captured frame: 0.000000000 seconds]
    [Time delta from previous displayed frame: 0.000000000 seconds]
    [Time since reference or first frame: 0.000000000 seconds]
    Frame Number: 1
    Frame Length: 68 bytes (544 bits)
    Capture Length: 68 bytes (544 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: null:ip:tcp]
    [Coloring Rule Name: TCP SYN/FIN]
    [Coloring Rule String: tcp.flags & 0x02 || tcp.flags.fin == 1]
Null/Loopback
    Family: IP (2)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
    Total Length: 64
    Identification: 0x0000 (0)
    010. .... = Flags: 0x2, Don't fragment
        0... .... = Reserved bit: Not set
        .1.. .... = Don't fragment: Set
        ..0. .... = More fragments: Not set
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 64
    Protocol: TCP (6)
    Header Checksum: 0x0000 [validation disabled]
    [Header checksum status: Unverified]
    Source Address: 127.0.0.1
    Destination Address: 127.0.0.1
Transmission Control Protocol, Src Port: 53384, Dst Port: 3000, Seq: 0, Len: 0
    Source Port: 53384
    Destination Port: 3000
    [Stream index: 0]
    [Conversation completeness: Complete, WITH_DATA (31)]
    [TCP Segment Len: 0]
    Sequence Number: 0    (relative sequence number)
    Sequence Number (raw): 1464468320
    [Next Sequence Number: 1    (relative sequence number)]
    Acknowledgment Number: 0
    Acknowledgment number (raw): 0
    1011 .... = Header Length: 44 bytes (11)
    Flags: 0x002 (SYN)
        000. .... .... = Reserved: Not set
        ...0 .... .... = Accurate ECN: Not set
        .... 0... .... = Congestion Window Reduced: Not set
        .... .0.. .... = ECN-Echo: Not set
        .... ..0. .... = Urgent: Not set
        .... ...0 .... = Acknowledgment: Not set
        .... .... 0... = Push: Not set
        .... .... .0.. = Reset: Not set
        .... .... ..1. = Syn: Set
            [Expert Info (Chat/Sequence): Connection establish request (SYN): server port 3000]
        .... .... ...0 = Fin: Not set
        [TCP Flags: ··········S·]
    Window: 65535
    [Calculated window size: 65535]
    Checksum: 0xfe34 [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    Options: (24 bytes), Maximum segment size, No-Operation (NOP), Window scale, No-Operation (NOP), No-Operation (NOP), Timestamps, SACK permitted, End of Option List (EOL), End of Option List (EOL)
    [Timestamps]

이와 같은 데이터 패킷은 여러 개로 구성되어 Three-hand shaking을 거쳐 전송된다.

코드로 요청 예시 작성하기

먼저, 간단한 Flask 애플리케이션을 통해 Connection close와 지속 연결의 차이를 알아보자.

from flask import Flask, Response

app = Flask(__name__)

@app.route('/close')
def close_connection():
    response = Response('Connection will be closed after this response')
    response.headers['Connection'] = 'close'
    return response

@app.route('/keep')
def keep_connection():
    return 'Connection will be kept alive after this response'

if __name__ == '__main__':
    app.run(port=3000)

이 예제에서는 두 개의 경로가 있다. /close는 연결을 종료하고, /keep은 연결을 유지한다.

패킷 분석하기

그러나 코드만으로는 이 차이를 명확하게 관찰하기 어렵다. 이를 위해서는 네트워크 패킷을 캡처하고 분석해야 한다. Wireshark 같은 패킷 분석 도구를 사용하면 이를 직접 볼 수 있다.

패킷을 참고하기에 앞서 TCP Flag의 종류를 살펴보자.

  • SYN (Synchronize): 연결 설정을 위해 사용. SYN 플래그는 연결 요청을 시작하거나 확인하는 데 사용.
  • ACK (Acknowledgment): 세그먼트 수신을 확인하는 데 사용. 대부분의 세그먼트에 설정되어야 함.
  • FIN (Finish): 연결 종료를 시작하는 데 사용. 발신자가 더 이상 데이터를 보낼 계획이 없음을 나타냄.
  • RST (Reset): 연결을 강제로 재설정하는 데 사용. 오류 발생 시나 예기치 않은 세그먼트 수신 시 사용.
  • PSH (Push): 수신자에게 버퍼링된 데이터를 빨리 응용 프로그램에 전달하도록 지시하는 데 사용.
  • URG (Urgent): 세그먼트에 긴급 데이터가 포함되어 있음을 나타냄. URG 플래그 설정 시, 긴급 데이터의 끝을 가리킴.
  • ECE (Explicit Congestion Notification Echo): 혼잡이 감지되었음을 나타냄. 명시적 혼잡 알림 기능의 일부.
  • CWR (Congestion Window Reduced): 윈도우 크기가 줄어들었음을 나타냄. 혼잡 제어와 관련하여 사용.

아래는 close 상태일 때 보여지는 패킷 목록이다. 해당 패킷 목록을 관찰하면 TCP 연결을 종료하고(RST Flag) 다시 새로운 TCP를 연결하는 모습을 확인할 수 있다.

반대로 아래는 keep-alive 상태일 때 보여지는 패킷 목록이다. SYN - SYN-ACK - SCK 를 거쳐 성공적으로 HTTP GET 요청이 완료된 것을 확인할 수 있다.

TCP의 keep-alive와 HTTP의 keep-alive의 차이

한편 TCP 의 keep-alive와 HTTP의 keep-alive의 차이는 무엇일까? TCP의 keep-alive는 TCP 연결 자체를 유지하도록 하는 매커니즘이고, HTTP의 keep-alive는 하나의 TCP 연결에서 여러 개의 HTTP 요청 및 응답을 처리하는 매커니즘이다.

결론

HTTP Persistence Connection 는 HTTP header에 'Connection' 항목을 'keep-alive'로 둠으로써 통신을 종료시키지 않고 유지시키는 것을 의미한다. 이를 통해 TCP 연결 요청 수를 줄이고 네트워크를 효율적으로 사용할 수 있다.

0개의 댓글