💻IT

[HTTP 완벽가이드] TCP 커넥션 관리

gom20 2023. 8. 31. 16:58

1. TCP 커넥션

브라우저가 TCP 커넥션을 통해 웹서버에 요청을 보내는 순서

모든 HTTP 통신은 TCP/IP 를 통해 이루어진다.

1) URL에서 호스트명 추출

2) 호스트명에 대한 IP 주소 추출

3) 포트 번호 추출

4) IP와 Port로 TCP 커넥션 생성

5) HTTP 요청 보냄

6) HTTP 응답 수신

7) 커넥션 끊기

 

- TCP 커넥션은 인터넷을 안정적으로 연결

- HTTP에 신뢰할 수 있는 통신 방식 제공

- 순서에 맞게 정확히 전달

TCP 데이터 전송 방식

세그컨트 단위로 데이터 스트림을 나누고, 

세그컨트를 IP패킷에 담아서 인터넷을 통해 데이터 전달

 

- IP 패킷 구성요소

IP패킷 헤더(IP정보), TCP 세그먼트 헤더(포트번호, 체크섬), TCP 데이터 조각(데이터)

 

유일한 TCP 커넥션

발신지 IP 주소, 발신지 포트, 수신지 IP주소, 수신지 포트

서로 다른 두 개의 TCP 커넥션은 네 가지 값이 모두 같을 수 없음

 

TCP 소켓 프로그래밍

소켓 API를 사용하면 TCP 종단에 데이터 구조를 생성하고, 원격 서버 TCP 종단에 그 종단 데이터 구조를 연결하여 데이터 스트림을 읽고 쓸 수 있다.

 

- Client

socket()

connect() -----> accept()

write, read()

close()

 

- Server

socket()

bind()

listen()

accept()

read, write()

close()

 

Server

import socket

# Create a socket object
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Define the server address and port
server_address = ('127.0.0.1', 12345)

# Bind the socket to the server address and port
server_socket.bind(server_address)

# Listen for incoming connections (maximum number of queued connections: 5)
server_socket.listen(5)
print("Server is listening for connections...")

while True:
    # Accept a new connection
    client_socket, client_address = server_socket.accept()
    print(f"Connection from {client_address} established.")

    # Send data to the client
    message = "Hello, client! Welcome to the server."
    client_socket.send(message.encode())

    # Close the client socket
    client_socket.close()

Client

import socket

# Create a socket object
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Define the server address and port
server_address = ('127.0.0.1', 12345)

# Connect to the server
client_socket.connect(server_address)

# Receive data from the server
data = client_socket.recv(1024).decode()
print(f"Received: {data}")

# Close the client socket
client_socket.close()

 

2. TCP 성능 고려

HTTP는 TCP 바로 위에 있는 계층이므로, TCP 성능에 영향을 많이 받음

HTTP 트랜잭션 지연

- 호스트에 첫 방문일 경우 DNS 인프라로 호스트명을 IP주소로 변환하는데 시간 소요

- TCP 커넥션 요청 - 허가 응답 회신: 커넥션 설정 시간

- HTTP 요청 전송

- HTTP 응답

 

TCP 관련 지연 요소

- TCP 커넥션 핸드셰이크 지연

- TCP의 느린 시작

- 네이글 알고리즘

- TCP의 편승 확인응답

- TIME_WAIT 지연과 포트고갈

 

TCP 커넥션 핸드셰이크 지연

TCP커넥션을 열 때 연속으로 IP패킷 교환

작은 크기의 데이터 전송에 커넥셔이 사용된다면 이러한 패킷 교환은 HTTP성능을 크게 저하

1) 클라이언트가 새로운 TCP 커넥션을 생성하기 위해 작은 TCP패킷을 서버에게 전송(SYN 플래그)

2) 서버가 그 커넥션을 받으면 몇가지 커넥션 매개 변수 산출. 커넥션 요청이 받아들여졌음을 의미하는 SYN, ACK플래그를 포함한 TCP패킷을 클라이언트에게 전송

3) 클라이언트는 커넥션이 잘 맺어졌음을 알리기위해 확인 응답 신호를 보냄 (+ 데이터)

 

HTTP 트랜잭션이 큰 데이터가 아닌 경우, 핸드셰이크 지연이 눈에 띔

크기가 작은 HTTP트랜잭션은 50%이상의 시간을 TCP를 구성하는데 쓰임

 

확인응답 지연

인터넷은 패킷전송을 완벽 보장X

TCP는 성공적 데이터 전송을 보장하기 위해 자체적인 확인 체계

 

걱  TCP 세그먼트는 순번, 무결성 체크섬을 가짐

세그먼트 수신자는 세그먼트를 온전히받으면 확인응답 패킷을 송신자에게 반환

송신자가 특정시간 안에 확인응답 메시지를 못받으면 패킷 파기 or 오류로 판단하고 데이터 재전송

 

확인응답 패킷은 작음 

같은 방향으로 송출되는 데이터 패킷에 편승

편승되는 경우를 늘리기 위해 확인응답 지연 알고리즘 구현

특정 시간 동안 확인응답 패킷을 버퍼에 저장해 두고, 편승 시키기 위한 송출데이터 패킷을 찾음

 

HTTP동작 방식은 요청과 응답 두가지 형식으로 이루어지기 때문에 편승 기회 감소

 

TCP의 느린 시작

TCP 커넥션 생성 시간에 따라 데이터 전송 속도 달라질 수 있음

시간이 지나면서 자체 튜닝

처음에는 최대 속도 제한

급작스러운 부하 방지를 위함

 

네이글 알고리즘

각 TCP 세그먼트는 40바이트 상당의 플래그 헤더 포함하여 전송.

작은 크기의 데이터를 포함한 많운 수의 패킷을 전송한다면 네트워크 성능 저하

 

네이글 알고리즘? 패킷 전송 전 많은 양의 TCP 데이터를 한개의 덩어리로 합침

세그먼트가 최대 크기가 되지 않으면 전송 X

모든 패킷이 확인 응답을 받았을 경우에는 최대 크기보다 작은 패킷 전송 허락 

 

문제1 크기가 작은 HTTP메시지는 패킷을 채우지 못하기 때문에 추가적인 데이터를 기다리며 지연

문제2 확인 응답지연과 함께 쓰이면, 확인 응답이 도착할 때까지 데이터 전송 멈춤

 

 

TIME_WAIT 지연과 포트고갈

TCP종단에서 커넥션을 끊으면 종단에서는 커넥션의 IP, 포트를 메모리의 control block에 기록

같은 주소와 포트 번호를 사용하는 신규 TCP 커넥션이 일정시간 생성되는 것을 방지 하기 위함 (2분 정도)

 

일반적인 종료지연은 문제 X
성능 시험 케이스에서 문제, IP 주소 개수 제한. 부하 발생 컴퓨터 수 제한

 

발신지 포트만 변경

발신지 포트의 수 제한

 

3. HTTP 커넥션 관리

커넥션을 생성하고 최적화하는 HTTP기술 설명

 

Connection 헤더

HTTP는 중개서버가 놓이는 것을 허락 (Proxy)

Connection 헤더 필드는 커넥션 토큰을 쉼표로 구분하여 가짐. 그 값들은 다른 커넥션에 전달 X

다음 메시지를 보낸 다음 끊어져야할 커넥션은 Connection: close라고 명시

 

Connection 헤더는 다음 세가지 종류의 토큰 전달 가능

HTTP 헤더 필드, 임시 토큰, close값

 

HTTP 헤더 필드는 현재 커넥션만을 위한 정보로 다음 커넥션에 전달 X

Connection 헤더에는 홉별 헤더명을 기술 (특정 두 서버 간에만 영향을 미치는 헤더)

 

순차적인 트랜잭션 처리에 의한 지연

심리적인 지연, 텅빈 화면만 보게됨

 

- 병렬 커넥션

- 지속 커넥션

- 파이프 라인 커넥션

- 다중 커넥션

 

4. 병렬 커넥션

병렬 커넥션은 더 빠르게 내려 받는다

각 커넥션의 지연시간을 겹치게 하면 총 지연 시간을 줄일 수 있음

나머지 객체를 내려받는 데에 남은 대역폭 사용

 

항상 더 빠르지는 않다!

제한된 대역폭 내에서 각 객체를 전송받는 것은 느림

다수의 커넥션은 메모리 많이 소모 자체 성능 문제

 

더 빠르게 느껴질 수는 있음

여러 개의 객체가 동시에 보이면서 내려받고 있는것을 보기 때문

 

5. 지속 커넥션

웹 클라이언트는 보통 같은 사이트에 여러 개의 커넥션을 맺음

사이트 지역성

 

HTTP/1.1 지원기기는 처리가 완료된 후에도 TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용 할 수 있음

처리가 완료된 후에도 계속 연결된 상태로 있는 TCP커넥션을 지속 커넥션이라고 부른다. 

 

시간을 절약

TCP의 느린 시작으로 인한 지연을 피함

 

지속 커넥션 vs 병렬 커넥션

병렬 커넥션의 단점.

매번 새로운 커넥션을 맺고 끈힉 때문에 시간 소요

TCP 느린 시작 때문에 성능 저하

병렬 커넥션 수의 제한

 

지속 커넥션의 장점

커넥션을 맺기 위한 사전작업 지연을 줄여줌

튜닝된 커넥션 유지

커넥션 수 줄여줌

 

But 관리 X 계속 연결된 커넥션이 쌓일 수 있음-> 불필요한 리소스 소모

 

HTTP/1.0+의 Keep-Alive 커넥션

커넥션을 맺고 끊는데 필요한 작업이 없어서 시간 단축

TCP느린 시작 지연 일어나지 않음

 

Keep-Alive 동작

keep-alive 커넥션을 구현한 클라이언트는 커넥션을 유지하기 위해서 요청에 Connection:Keep-Alive 헤더를 포함

서버는 그 다음 요청도 이 커넥션을 받고자 한다면 응답 메시지에 같은 헤더 포함하여 응답

 

Keep-Alive 옵션

Keep-Alive 헤더는 커넥션을 유지하기를 바라는 요청일 뿐이다. 

무조건 그것을 따를 필요 X

 

timeout 커넥션 유지 시간 (보장X)

max 몇 개의 HTTP 트랜잭션을 처리할때까지 유지될지 (보장X)

Keep-Alive 헤더는, Connection: Keep-Alive 헤더가 있을 때만 사용할 수 있음

 

Keep-Alive 커넥션 제한과 규칙

프록시와 게이트 웨이는 Connection 헤더 규칙을 철저히 지켜야 함 

메시지를 전달하거나 캐시에 넣기 전에 Connection 헤더에 명시된 모든 헤더 필드와 Connection 헤더를 제거 해야함

정석대로라면 keep-alive커넥션은 Connection 헤더를 인식 못하는 프록시서버와 맺어지면 안되나 현실적으로는 쉽지 않음

 

Keep-Alive와 멍청한 프락시

프록시에서 keep-alive를 사용할 때 생기는 문제

 

 Connection 헤더의 무조건 전달

프록시가 Connection 헤더를 이해하지 못해 해당 헤더를 삭제하지 않고 다음 프록시에 전달할 경우

Connection헤더는 홉별 헤더, 프록시는 Connection 헤더와 Connection 헤더에 명시된 헤더들은 절대 전달하면 안됨

 

Proxy-Connection 살펴보기

일반적인 Connection 헤더 대신

비표준인 Proxy-Connection 확장헤더를 프락시에게 전달 

웹서버는 그것을 무시

 

영리한 프록시라면? proxy-connection 헤더를 connection 헤더로 바꿈으로써 원하는 효과를 얻게 될 것임

이 방식은 클라이언트와 서버 사이에 한개의 프락시만 있는 경우 동작

 

멍청한 프락시 양 옆에 영리한 프락시가 있다면 잘못된 헤더를 만들어내는 문제가 다시 발생

 

HTTP/1.1의 지속 커넥션

기본으로 활성화

모든 커넥션을 지속 커넥션 취급

 

지속 커넥션의 제한과 규칙

길이 정보를 정확히 가지고 있을 때만 커넥션 지속

 

6. 파이프라인 커넥션

HTTP/1.1은 지속 커넥션을 통해서 요청을 파이프라이닝 할 수 있다.

여러 개의 요청은 응답이 도착하기 전까지 큐에 쌓인다. 

 

제약사항

- 지속 커넥션인지 확인하기 전까지 파이프라인을 이어서는 안됨

- 응답은 요청 순서와 같게 와야 함

- POST 요청같이 반복해서 보낼 경우 문제가 생기는 요청은 파이프라인을 통해 보니면 안됨

 

7. 커넥션 끊기에 대한 미스터리

커넥션 끊기에는 명확한 기준이 없다. 

 

마음대로 커넥션 끊기

HTTP어플리케이션은 언제든지 지속 커넥션을 임의로 끊을 수 있다. 

예를 들어 지속 커넥션이 일정시간 동안 요청을 전송하지 않고 유휴 상태에 있으면?

하지만 그 시점에 클라이언트게 요청할 수도 있잖아... 그럼 문제 생기지

 

Content-Length 와 Truncation

각 응답은 본문의 정확한 크기값을 가지는 Content-Length 헤더를 가지고 있어야 함

 

커넥션 끊기의 허용, 재시도, 멱등성

에러가 없더라도 언제든 끊을 수 있다. 

커넥션이 끊어졌을 때 적절히 대응할 수 있는 준비가 되어야 한다. 

파이프라인 커넥션에서 좀더 어려워짐

어떤 요청 데이터가 전송되었지만 응답이 오기전에 커넥션이 끊기면 실제 서버에서 얼마만큼 요청이 처리되었는지 알수 없음

 

여러번 호출할 경우

GET은 OK

POST는 문제발생 여지

 

GET, HEAD, PUT, DELETE, TRACE , OPTION -> 멱등

 

PUT과 DELETE는 왜 멱등이지?

 

POST는 비멱등

POST와 같은 요청은 파이프라인을 통해 요청하면 안된다. 

 

비멱등인 요청을 다시 보내야 한다면, 응답을 받을때까지 기다려야 한다. 

재시도 유의

 

우아한 커넥션 끊기

TCP커넥션은 양방향

입력, 출력 큐가 양쪽에 있음!

 

전체 끊기와 절반 끊기

입력, 출력 둘다 끊거나 한 개만 끊을 수 있음. 

close를 호출하면 입력, 출력 모두 끊음

shutdown은 절반 끊기

 

TCP 끊기와 리셋 에러

예상치 못한 에러 발생을 예방하기 위해 절반끊기를 사용해야함

보통은 커넥션의 출력채널을 끊는 것이 안전

 

우아하게 커넥션 끊기

자신의 출력채널을 먼저 끊고, 다른 쪽 기기의 출력채널이 끊기는 것을 기다림