TCP는 HTTP에 신뢰할 만한 통신 방식 제공
프로토콜 스택에서 최상위 계층
IP 패킷은 IP 패킷 헤더, TCP 세그먼트 헤더, TCP 데이터 조각 포함
<발신지 IP 주소, 발신지 포트, 수신지 IP주소, 수신지 포트>로 유일한 커넥션을 생성
HTTP는 TCP 바로 위에 있는 계층이기 때문에 HTTP 트랜잭션의 성능은 TCP 성능에 영향을 받음

클라이언트나 서버가 너무 많은 데이터를 내려받거나 복잡하고 동적인 자원 실행하지 않는 한 HTTP 지연은 TCP 네트워크 지연 때문에 발생
새로운 TCP 커넥션을 열면 TCP 소프트웨어는 커넥션을 맺기 위한 조건을 맞추기 위해 연속적으로 IP 패킷 교환하는데, 작은 크기의 데이터 전송에 커넥션이 사용된다면 이러한 패킷 교환은 HTTP 성능을 크게 저하시킬 수 있음

1. 클라이언트는 새로운 TCP 커넥션 생성 위해 작은 TCP 패킷(SYN(커넥션 생성 요청))을 서버에 보냄
2. 서버가 커넥션을 받으면 몇 가지 커넥션 매개변수를 산출하고, 커넥션 요청이 받아들여졌다는 TCP 패킷(SYN + ACK)을 보냄
3. 클라이언트는 커넥션이 잘 맺어졌음 알리기 위해 확인응답 신호를 보냄
HTTP 트랜잭션이 아주 큰 데이터를 주고 받지 않는 경우에는 SYN/SYN+ACK 핸드셰이크가 지연 발생시킴
(크기가 작은 HTTP 트랜잭션은 50% 이상의 시간을 TCP를 구성하는데 씀)
=> TCP 구성으로 인한 지연 제거하기 위해 HTTP가 이미 존재하는 커넥션 어떻게 재활용할까?
인터넷 자체가 패킷 전송을 완벽하게 보장하지는 않기 때문에 TCP는 성공적인 데이터 전송을 보장하기 위해 자체적인 확인 체계 가짐
확인응답이 같은 방향으로 가는 데이터 패킷에 편승되는 경우를 늘리기 위해 많은 TCP 스택은 확인응답 지연 알고리즘을 구현
확인응답 지연은 송출할 확인응답을 특정 시간동안 버퍼에 저장해두고, 확인응답을 편승시키기 위한 송출 데이터 패킷을 찾음
(일정 시간안에 송출 데이터 패킷 찾지 못하면 확인응답은 별도 패킷을 만들어 전송)
But, 요청과 응답 두 가지 형식으로만 이루어지는 HTTP 동작 방식은 확인 응답이 송출 데이터 패킷에 편승할 기회를 감소시킴
(편승할 패킷을 찾으려고 하면 해당 방향으로 송출될 패킷이 많지 않기 때문에 확인응답 지연 알고리즘으로 인한 지연 자주 발생)
혼잡제어기능(opening the congestion window)
1. 패킷이 성공적으로 전달되는 각 시점에 송신자는 추가로 2개의 패킷을 더 전송할 수 있는 권한 얻음
2. HTTP 트랜잭션에서 전송할 데이터의 양이 많으면 모든 패킷을 한 번에 전송할 수 없고, 한 개의 패킷만 전송 후 확인응답 기다림
3. 확인응답을 받으면 2개의 패킷을 보낼 수 있고, 그 패킷 각각 대한 확인응답을 받으면 총 4개의 패킷 보낼 수 있음
=> 새로운 커넥션은 이미 어느정도 데이터를 주고받은 튜닝된 커넥션보다 느림
=> 튜닝된 커넥션이 더 빠르기 때문에 HTTP에서는 이미 존재하는 커넥션을 재사용하는 기능 있음
추가 예정
추가 예정
HTTP 헤더 필드 명(이 커넥션에만 해당하는 헤더들), 임시적인 토큰 값(커넥션에 대한 비표준 옵션), close값(작업 완료 시 종료) 세 가지 종류의 토큰 전달될 수 있음
커넥션 토큰이 HTTP 헤더 필드 명을 가지고 있으면, 해당 필드들은 현재 커넥션만을 위한 정보로 다음 커넥션에 전달하면 안됨
(= Connection 헤더에 있는 모든 헤더 필드는 메시지를 다른 곳으로 전달하는 시점에 삭제되어야 함)

각 트랜잭션이 새로운 커넥션을 필요로 하면, 커넥션을 맺는데 발생하는 지연과 느린 시작 지연이 발생

순차적으로 로드 시, 특정 브라우저의 경우 객체를 화면에 배치하려면 객체의 크기를 알아야하기 때문에 모든 객체를 내려받기 전까지 텅 화면을 보여줄 수 있음
그래서 HTTP 커넥션의 성능을 향상시킬 수 있는 여러 최신 기술이 존재
병렬커넥션: 여러 개의 TCP 커넥션을 통한 동시 HTTP 요청지속커넥션: 커넥션을 맺고 끊는데서 발생하는 지연을 제거하기 위한 TCP 커넥션의 재활용파이프라인커넥션: 공유 TCP 커넥션을 통한 병렬 HTTP 요청다중커넥션: 요청과 응답들에 대한 중재HTTP는 클라이언트가 여러 개의 커넥션을 맺음으로써 여러개의 HTTP 트랜잭션을 병렬로 처리할 수 있게 함
(예제에서는 4개의 이미지를 할당받은 각 TCP 커넥션 상의 트랜잭션을 통해 병렬로 내려 받음)


화면에 여러 개의 객체가 동시에 보이면서 내려받고 있는 상황을 볼 수 있기 때문에 사용자는 더 빠르게 내려받고 있는 것처럼 느낄 수 있음
사이트 지역성: 웹 클라이언트는 보통 같은 사이트에 여러 개의 커넥션 맺음
(웹페이지에 첨부된 이미지들은 대부분 같은 웹 사이트에 있고, 상당 수의 하이퍼링크도 같은 사이트를 가리키기 때문)
=> TCP 커넥션을 유지하여 앞으로 있을 HTTP 요청에 재사용 할 수 있음
비지속 커넥션은 각 처리가 끝날 때마다 커넥션을 끊지만, 지속 커넥션은 클라이언트나 서버가 커넥션을 끊기 전까지는 트랜잭션 간에도 커넥션 유지
지속 커넥션을 잘못 관리할 경우, 계속 연결된 상태로 있는 수많은 커넥션 쌓임
=> 로컬의 리소스, 원격의 클라이언트와 서버의 리소스에 불필요한 소모 발생
지속 커넥션은 병렬 커넥션과 함께 사용할 때 가장 효과적
연속적으로 4개의 커넥션을 생성하여 처리하는 방식과 하나의 지속 커넥션으로만 처리하는 방식 비교 시 커넥션을 맺고 끊는데 필요한 작업이 없어서 시간 단축
keep-alive는 사용하지 않기로 결정되어 HTTP/1.1 명세에서는 빠졌지만 아직 브라우저와 서버 간 keep-alive 핸드셰이크가 널리 사용되고 있어 처리할 수 있게 개발해야함
Connection:Keep-Alive헤더 포함Keep-Alive는 커넥션 유지를 바라는 요청일 뿐, 받았다고 해서 무조건 따를 필요는 없음
timeout: 커넥션이 얼마간 유지될 것인지 의미max: 커넥션이 몇 개의 HTTP 트랜잭션을 처리할 때까지 유지될 것인지
=> 이러한 잘못된 통신 피하기 위해서 프락시는 Connection 헤더와 Connection 헤더에 명시된 헤더, 홉별 헤더 전달해서는 안된다.
dumb 프락시 최종 정리
홉별 헤더들은 한 개의 특정 커넥션에서 쓰이고 이후에 전달하면 안되는데,
dump 프락시는 Connection: Keep-Alive 같은 홉별 헤더를 무조건 전달하기 때문에 문제를 일으킴
(홉별 헤더를 전달받은 서버가 그 헤더를 자신과 프락시 간의 커넥션으로 오해하기에 문제 발생)
Connection 헤더 대신 비표준인 Proxy-Connection 확장 헤더를 프락시에게 전달
(프락시가 Proxy-Connection 헤더를 무조건 전달해도 웹 서버는 무시하기 때문에 문제가 되지 않는다.)
영리한 프락시(지속 커넥션 핸드셰이킹을 이해할 수 있는 것)이라면 의미 없는 Proxy-Connection 헤더를 Connection 헤더로 바꿈으로써 원하는 효과를 얻을 수 있음
위의 내용을 풀어서 설명하자면
Proxy-Connection 헤더가 웹 서버에 전달되더라도 클라이언트와 프락시 사이 혹은 프락시와 서버 사이에 keep-alive 커넥션이 맺어지지 않는데, 영리한 프락시는 Proxy-Connection 헤더가 keep-alive를 요청하는 것을 인식해서 자체적으로 Connection:Keep-Alive 헤더를 웹 서버에 전송
하지만 이 방식은 클라이언트와 서버 사이에 한 개의 프락시만 있는 경우 동작

프락시가 많은 구조에서는 Proxy-Connection을 사용해도 여전히 문제 발생할 수 있음

Connection:close 헤더 명시 필요Connection:close 없어도 언제든지 커넥션 끊을 수 있음Connection:close 헤더를 포함해 보냈으면 그 커넥션으로 추가적인 요청 불가Connection:close 헤더를 포함해야함추가 예정
추가 예정
HTTP 애플리케이션은 언제든지 지속 커넥션을 임의로 끊을 수 있음
But, 서버가 유휴상태(지속 커넥션이 일정 시간동안 요청 전송하지 않고 있음)에 있는 커넥션을 끊는 시점에 서버는 클라이언트가 데이터를 전송하지 않을 것이라 확신하지 못하고, 클라이언트는 요청 메시지를 보내는 도중에 문제 생김
각 HTTP 응답은 본문의 정확한 크기 값을 가지고 Content-Length 헤더를 가지고 있어야함
클라이언트나 프락시가 커넥션이 끊어졌다는 HTTP 응답을 받은 후, 실제 전달된 엔티티의 길이와 Content-Length의 값이 일치하지 않거나, Content-Length가 존재하지 않으면 수신자는 데이터의 정확한 길이를 서버에게 물어봐야 함
커넥션은 에러가 없더라도 끊을 수 있기에, HTTP 애플리케이션은 예상치 못하게 커넥션이 끊어졌을 때 대응할 준비가 되어있어야 함
TCP 커넥션은 양방향으로, 양쪽에는 데이터를 읽거나 쓰기 위한 입력 큐와 출력 큐가 있음
한쪽 출력 큐에 있는 데이터는 다른 쪽의 입력 큐에 보내질 것

close(): TCP 커넥션의 입력 채널과 출력 채널의 커넥션 모두 끊음shutdown(): 입력 채널이나 출력 채널 중 하나 개별적으로 끊음단순한 HTTP 애플리케이션은 전체 끊기만 사용할 수 있지만, 기기들의 예상치 못한 쓰기 에러를 발생하는 것을 예방하기 위해 절반 끊기를 사용해야 함
보통 커넥션의 출력 채널을 끊는 것이 안전하며, 커넥션의 반대편에 있는 기기는 모든 데이터를 버퍼로부터 읽고 나서 데이터 전송이 끝남과 동시에 커넥션을 끊었단느 것을 알게 될 것임
애플리케이션 자신의 출력 채널을 먼저 끊고 다른 쪽에 있는 기기의 출력 채널이 끊기는 것을 기다리는 것이 우아하게 커넥션 끊는 방법
양쪽에서 더는 데이터를 전송하지 않을 것이라고 알려주면 커넥션은 리셋의 위험 없이 온전히 종료
출처
HTTP 완벽 가이드