포스트

06. Internet Engineering - Chapter 2 Problems (Application Layer)

2장 응용 계층 문제풀이(P1, P4, P7, P10, P28): HTTP 참/거짓, GET 메시지 해석, DNS와 RTT 계산, 지속/비지속 HTTP 비교, 그리고 TCP/UDP 소켓 코드를 직접 실행해 P28 상황 재현

06. Internet Engineering - Chapter 2 Problems (Application Layer)

학습 목표

  • HTTP 요청/응답, 지속 연결과 비지속 연결의 차이 정리
  • HTTP GET 메시지의 각 헤더가 뜻하는 것 읽기
  • DNS 조회, TCP 연결 설정, 객체 요청에 드는 RTT 계산
  • 병렬 비지속 HTTP와 지속 HTTP의 전송 시간 비교
  • Python 소켓으로 TCP/UDP 대문자 변환 서버를 실행하고 P28 상황을 재현

P1. HTTP 참/거짓

a

문항답이유
aFalseHTML 파일과 이미지는 각각 별도의 객체이므로 요청도 따로 보냄
bTrue같은 서버의 객체는 지속 연결이면 하나의 TCP 연결로 받을 수 있음
cFalse비지속 연결에서는 TCP 연결 하나가 요청/응답 하나만 처리함
dFalseDate는 응답을 만든 시각이고, 객체가 수정된 시각은 Last-Modified
eFalseHEAD 요청에 대한 응답, 304 Not Modified 등은 본문이 없음

P4. HTTP GET 메시지 해석

b

c

문항답근거
a. 요청한 URLhttp://gaia.cs.umass.edu/cs453/index.html요청 줄의 경로 + Host 헤더
b. HTTP 버전HTTP/1.1요청 줄 끝
c. 연결 방식지속 연결Connection: keep-alive
d. 호스트의 IP 주소알 수 없음HTTP 메시지에는 IP 주소가 없음. IP 주소는 이 메시지를 실은 IP 데이터그램 헤더에 있음
e. 브라우저 종류Netscape 7.2 (Mozilla 계열)User-Agent 헤더

e의 두 번째 질문(왜 브라우저 종류가 필요한가): 서버가 브라우저마다 다른 버전의 객체(같은 URL이라도 브라우저에 맞춘 페이지)를 보내 줄 수 있기 때문.


P7. DNS 조회를 포함한 응답 시간

d

  • DNS 서버 \(n\)개를 차례로 방문: \(RTT_1 + \cdots + RTT_n\)
  • 서버와 TCP 연결 설정: \(RTT_0\)
  • HTTP 요청을 보내고 응답의 첫 비트를 받기까지: \(RTT_0\)
  • 객체 전송 시간: \(0.002 RTT_0\)

따라서 전체 시간은 \(RTT_1 + \cdots + RTT_n + 2RTT_0 + 0.002RTT_0\)

수업에서는 TCP 연결과 요청/응답 RTT를 빼고 \(RTT_1 + \cdots + RTT_n + 0.002RTT_0\)를 답으로 함. 이는 DNS 조회와 전송 시간만 따진 경우이고, HTTP 객체를 받으려면 TCP 연결(1 RTT)과 요청/응답(1 RTT)이 필요하므로 교재 기준으로는 \(2RTT_0\)를 포함하는 것이 맞음


P10. 병렬 비지속 HTTP vs 지속 HTTP

e

f

조건: 30 m 링크, 전송 속도 300 bps, 객체 100,000 bits, 제어 패킷(SYN, ACK, 요청 등) 200 bits, 참조 객체 10개, 병렬 연결 \(N\)개는 대역폭을 \(1/N\)씩 나눠 가짐. 링크가 30 m라 전파 지연(약 0.1 μs)은 무시함.

계산

먼저 기준 객체(HTML)를 받는 시간. TCP 연결 설정(제어 패킷 3개), 요청, 객체 전송을 더함.

  • 기준 객체: \(3 \times \frac{200}{300} + \frac{200}{300} + \frac{100000}{300} \approx 2 + 0.67 + 333.33 \approx 336\)초

병렬 비지속 HTTP: 참조 객체 10개를 10개 연결로 동시에 받으면 연결마다 대역폭이 \(300 / 10 = 30\) bps

  • 참조 객체 10개: \(3 \times \frac{200}{30} + \frac{200}{30} + \frac{100000}{30} \approx 20 + 6.67 + 3333.33 \approx 3360\)초
  • 합계: 약 3,696초

지속 HTTP: 연결 설정 없이 요청과 전송만 10번 (전체 대역폭 300 bps 사용)

  • 참조 객체 10개: \(10 \times \left( \frac{200}{300} + \frac{100000}{300} \right) \approx 10 \times 334 = 3340\)초
  • 합계: 약 3,676초

답

  • 병렬 비지속 HTTP도 꽤 효율적임. 10개 연결이 병목 링크를 나눠 쓰므로, 링크가 계속 꽉 차 있다는 점은 지속 HTTP와 같음
  • 지속 HTTP가 약 20초 빠르지만, 전체 수천 초에 비하면 큰 이득은 아님
  • 객체가 커서 전송 시간이 대부분을 차지하고, 두 방식의 차이는 연결 설정 오버헤드에서만 생기기 때문

P28. TCP/UDP 소켓 실험

g

교재 2.7절의 대문자 변환 서버를 Python으로 작성하고, 한 컴퓨터(127.0.0.1)에서 직접 실행해 보았음.

TCP 서버와 클라이언트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# TCPServer.py
from socket import *

serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)   # TCP 소켓
serverSocket.bind(('', serverPort))
serverSocket.listen(1)                        # 연결 요청 대기
print("서버 실행 중...")

while True:
    connectionSocket, addr = serverSocket.accept()   # 클라이언트마다 새 소켓
    print(f"연결됨: {addr}")
    sentence = connectionSocket.recv(1024).decode()
    connectionSocket.send(sentence.upper().encode())
    connectionSocket.close()
1
2
3
4
5
6
7
8
9
10
11
12
# TCPClient.py
from socket import *

serverName = '127.0.0.1'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((serverName, serverPort))   # 3-way handshake
sentence = input("소문자 문장 입력: ")
clientSocket.send(sentence.encode())
modifiedSentence = clientSocket.recv(1024)
print("서버 응답:", modifiedSentence.decode())
clientSocket.close()

실행 결과 (클라이언트에 hello internet engineering 입력)

1
2
3
4
5
6
[클라이언트]
소문자 문장 입력: 서버 응답: HELLO INTERNET ENGINEERING

[서버]
서버 실행 중...
연결됨: ('127.0.0.1', 59164)
  • 서버 출력의 59164는 운영체제가 클라이언트 소켓에 자동으로 붙인 포트 번호

UDP 서버와 클라이언트

1
2
3
4
5
6
7
8
9
10
11
12
# UDPServer.py
from socket import *

serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)   # UDP 소켓, listen/accept 없음
serverSocket.bind(('', serverPort))
print("UDP 서버 실행 중...")

while True:
    message, clientAddress = serverSocket.recvfrom(1024)
    print("받음:", message.decode())
    serverSocket.sendto(message.decode().upper().encode(), clientAddress)
1
2
3
4
5
6
7
8
9
10
11
# UDPClient.py
from socket import *

serverName = '127.0.0.1'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM)
msg = input("메시지 입력: ")
clientSocket.sendto(msg.encode(), (serverName, serverPort))   # 연결 없이 바로 전송
response, _ = clientSocket.recvfrom(1024)
print("서버 응답:", response.decode())
clientSocket.close()

실행 결과 (클라이언트에 udp is connectionless 입력)

1
2
3
4
5
6
[클라이언트]
메시지 입력: 서버 응답: UDP IS CONNECTIONLESS

[서버]
UDP 서버 실행 중...
받음: udp is connectionless

P28 상황 재현

timeout 5로 클라이언트를 5초까지만 기다리게 해서 실행함. 종료 코드 124는 5초 동안 끝나지 않아 강제로 종료되었다는 뜻.

1
2
3
4
5
6
7
8
9
# a. TCPServer만 실행 중일 때 UDPClient 실행
python3 TCPServer.py &
echo "test" | timeout 5 python3 UDPClient.py; echo "exit=$?"

# b. UDPServer 없이 UDPClient 실행
echo "test" | timeout 5 python3 UDPClient.py; echo "exit=$?"

# c-1. UDP 서버는 12000, 클라이언트는 12001로 전송
# c-2. UDP 서버만 12000에 있고, TCP 클라이언트가 12001로 연결

실행 결과

1
2
3
4
a   exit=124                              (UDP 클라이언트가 응답을 기다리며 멈춤)
b   exit=124                              (전송은 되지만 응답이 오지 않아 멈춤)
c-1 exit=124                              (다른 포트로 보낸 데이터그램은 서버가 받지 못함)
c-2 ConnectionRefusedError: [Errno 111] Connection refused
문항답이유
a통신이 되지 않고 UDP 클라이언트가 응답을 기다리며 멈춤UDP 데이터그램은 같은 포트 번호라도 UDP 소켓으로만 전달됨. TCP 서버 소켓은 이를 받지 않음
b클라이언트는 데이터를 보내지만 응답을 받지 못하고 멈춤UDP는 연결 설정이 없어서 서버가 없어도 sendto()는 성공함. 받을 프로세스가 없으니 데이터그램은 버려지고, recvfrom()은 계속 기다림
c통신할 수 없음소켓은 포트 번호로 식별됨. TCP는 연결 요청이 거절되고(Connection refused), UDP는 데이터그램이 다른 포트로 가서 서버에 전달되지 않음

서버를 끈 직후 바로 다시 실행하면 OSError: [Errno 98] Address already in use가 날 수 있음. 서버가 먼저 연결을 닫으면 그 소켓이 TIME_WAIT 상태로 잠시 남기 때문. 실습할 때는 serverSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)을 bind() 전에 넣으면 됨


과제 노트에서 정정한 부분 정리

위치원래정정이유
P4 dIP 주소는 DNS 조회로만 알 수 있음HTTP 메시지에는 없지만, 이 메시지를 실은 IP 데이터그램의 출발지 주소로 알 수 있음문제는 HTTP 메시지만 보고 답하라는 뜻
P4 e브라우저 종류만 답함브라우저 종류가 필요한 이유(브라우저별로 다른 객체 제공)를 추가문항에 질문이 두 개 있음
P7수업 정답 \(RTT_1 + \cdots + RTT_n + 0.002RTT_0\)\(2RTT_0\)를 더한 식이 교재 기준 답객체를 받으려면 TCP 연결과 요청/응답에 각각 1 RTT가 필요
P28 aTCP 클라이언트와 UDP 서버도 통신 불가로 함께 설명문제는 TCPServer + UDPClient 조합실행해 보면 UDP 클라이언트가 응답 없이 멈춤

최종 핵심 정리

  1. 웹 페이지의 HTML과 이미지는 각각 별도 객체이고, 비지속 연결은 객체마다 TCP 연결을 새로 맺음
  2. Date는 응답 생성 시각, Last-Modified는 객체 수정 시각이며, HEAD와 304 응답에는 본문이 없음
  3. 요청한 URL은 요청 줄의 경로와 Host 헤더를 합친 것이고, Connection: keep-alive는 지속 연결 요청
  4. DNS 조회 후 객체를 받는 시간은 \(RTT_1 + \cdots + RTT_n + 2RTT_0 +\) 전송 시간
  5. 병목 링크가 꽉 차 있으면 병렬 연결 수와 상관없이 총 전송률은 같고, 지속 HTTP의 이득은 연결 설정 시간만큼
  6. TCP 서버는 listen()과 accept()로 클라이언트마다 새 소켓을 만들고, UDP 서버는 연결 없이 recvfrom()으로 받음
  7. UDP는 받는 쪽이 없어도 전송이 성공하므로, 애플리케이션이 타임아웃을 직접 처리해야 함
  8. 프로토콜(TCP/UDP)이나 포트 번호가 다르면 같은 컴퓨터 안이라도 서로 통신할 수 없음

End.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.