Stop and Wait ARQ 개선 및 Wireshark 패킷 분석
주제
- Our file transfer protocol using UDP: DCFTv3 Protocol
코드 설명 (client.py)
라이브러리 import 및 상수 정의

- 라이브러리를 Import하고 상수를 정의한다. 설명은 주석으로 대체한다.
Checksum 계산 함수
*week13 과제와 동일한 함수 사용

- 체크섬(checksum)은 데이터 전송 중에 오류를 감지하기 위해 사용되는 기술이다. 수신자(클라이언트)가 데이터 무결성을 체크하기 위해 사용된다.
- 서버는 모든 패킷에 checksum을 붙여 보낼 것이고, 클라이언트는 수신한 패킷의 checksum이 맞지 않으면 Ack를 보내지 않고 버려야 한다. 따라서 클라이언트에 checksum을 검증하는 기능을 구현해야 한다.
- UDP 프로토콜에서 체크섬은 데이터를 2바이트(16비트) 단위로 처리한다. 이때 데이터가 홀수 바이트라면, 마지막 바이트가 완전한 16비트를 형성하지 못하여 체크섬을 계산하지 못한다. 원본 데이터에 손상을 가하지 않으면서 이를 해결하기 위해 0을 추가하여 길이를 짝수로 만들었다. 이때 바이트, big-endian 형식임에 주의해야 한다.
- 2바이트마다 1의 보수를 계산해 합산해야한다. 이를 int로 바꿔 계속 더한다. 체크섬은 크기를 유지해야 하니, 더하다 넘친 부분은 다시 앞으로 더해준다.
- 최종적으로 송신자는 데이터 전체를 다 더하여 결과가 0xFFFF(2^16 -1)가 되는 코드를 보내야 하므로, 65535(UINT16_MAX)에서 checksum을 뺀 것을 최종 체크섬으로 결정한다.
Main
Info 처리

- entry block에서 인자를 전달받는다.
- 넣은 주소가 IPv4 주소 체계이니 이를 사용하도록 socket.AF_INET을, UDP 소켓을 만들어야 하니 socket.SOCK_DGRAM을 같이 인자로 사용한다. 지정된 IP와 포트에 바인딩하여 클라이언트 요청을 받을 준비를 한다.
- 파일 이름을 입력받고 struck.pack으로 요청 헤더를 생성한다. 빅엔디언 형식의 2바이트 정수 2개로 어떤 타입의 요청인지, 서버에 제안할 윈도우 크기는 얼마인지(INFO요청에서는 생략해도 되나 일관성을 위해 이렇게 작성함) 묶어 보낸다.
- mtu 크기만큼 서버 응답을 수신하여 4개의 2바이트 정수로 unpack, status 코드를 확인하고 만약 파일이 없을 경우(http코드가 성공 200이 아님) 다른 파일명을 입력하도록 처리해준다.
- 헤더 이후 부분을 디코드해서 파일 크기를 문자열로 가져온 뒤 정수로 변환하여 로그로 요청한 파일 이름과 크기를 출력해준다.
Download 처리

- INFO 요청에 이어 DONWLOAD요청을 비슷한 방식으로 서버에 보낸다. 이때 다른 점은,

- 서버가 보내는 패킷 수신
- 시퀀스 번호와 체크섬 검사
- DROP 조건
- 파일에 저장
- ACK 패킷 전송
- 남은 바이트 수가 0이 되면 다운로드 완료된다.

- throughput 계산
- 클라이언트 종료
Entry Point Block

- entry point block이다. 지난 주 과제에서 작성한 것과 같은 방식이나, address default를 GCP서버 주소로, port default를 3038로 설정하여 주소나 포트 인자 입력을 하지 않더라도 이후 진행할 GCP서버와의 통신에서도 수정 없이 작동되도록 하였다.
- 인자로 address와 port를 입력하니 argparse를 import에서 커맨드라인 인자를 처리하도록 한다.
- ArgumentParser()는 인자를 등록 및 파싱 가능하게 한다. UDP서버를 띄우기 위해 ip주소와 포트 번호가 필요하니 이를 사용해야한다.
- 주소를 –address로 입력받고 문자열로 처리, 값 입력 없을 시 기본값으로 34.64.96.250을 사용하도록 한다.
- 포트 번호를 –port로 입력받고 int로 처리, 값 입력 없을 시 기본값으로 3039를 사용하도록 한다.
- (week12) 과제 조건에 FLAGS.mtu가 명시되어있어서, 문자 그대로 써보려고 여기서 인자로 같이 넘기는 방법을 선택했다. default값을 1500으로 둬서, 요청받은 파일을 1500 Bytes씩 잘라서 전송하는 기준을 설정했다.
- 서버가 클라이언트에게 제공할 파일들이 있는 폴더의 경로를 명시하여 인자로 넣는다. 클라이언트 코드는 이 부분이 빠졌고 나머지는 동일하다.
- 앞서 받은 인자들을 파싱하여 FLAGS 객체에 저장하고, 이를 main()에 넘겨 실행한다.
과제 1: Wireshark Packet Capture
*이더넷 헤더 (14Bytes), IP 헤더 (20Bytes), UDP 헤더 (8Bytes)는 제외하고 설명
Control Message - Request

DCFTv3 헤더 (4bytes)
- 00 02 → Request Type = 0x0002 = INFO
- 00 05 → Window Size = 0x0005 = 5
Data (13 bytes)
- 32 30 32 33 30 36 32 37 2e 6a 70 67 → 문자열 ”20230627.jpg”

DCFTv3 헤더 (4bytes)
- 00 03 → Request Type = 0x0003 = DOWNLOAD
- 00 05 → Window Size = 0x0005 = 5
Data (13 bytes)
- 32 30 32 33 30 36 32 37 2e 6a 70 67 → 문자열 ”20230627.jpg”
Control Message - Response

DCFTv3 헤더 (8bytes)
- 00 02 → Request Type = 0x0002 = INFO 응답
- 00 00 → 클라이언트에서 윈도우 사이즈를 따로 요청했는데 00 00이 왔다. INFO 요청에 대해서는 서버 측에서 Window size를 따로 같이 보내지 않도록 구현되어 0이 응답된 듯하다.
- 00 C8 → Status Code = 200 (OK)
- 00 00 → 아직 port 지정 X
Data (6 bytes)
- 35 38 34 32 31 30 → ”584210” (파일 크기)

DCFTv3 헤더 (8bytes)
- 00 03 → Request Type = 0x0003 = DOWNLOAD 응답
- 00 05 → 클라이언트에서 요청한 Window 사이즈를 수락했다.
- 00 C8 → Status Code = 200 (OK)
- d5 2b → 데이터 전송에 사용할 서버 측 포트 번호 (54571)
Data Message

DCFTv3 헤더 (4bytes)
- 00 0f → 시퀀스 번호(15)
- d5 2b → 체크섬
Payload (1456 bytes)
- 파일 데이터
Data ACK

DCFTv3 헤더 (4bytes)
- 00 01 → ACK Sequence Number = 1
- ff ff → Checksum 필드 (ACK는 실제 데이터가 없기 때문에 checksum 대신 0xffff를 사용함, 의미 없음)
과제 2: Download image using Go back N ARQ
과정
수신 시작

수신 종료

설명
- 상단 코드 설명에 따라, DOWNLOAD 요청 시 SUGGESTED_SERVER_WINDOW_SIZE에 저장된 값을 함께 넘겨 제안한다. 실제 확정된 Window size는 5로, 응답 패킷에서 확인할 수 있었다(과제1 참고).
- 상단 코드 설명에 따라, 수신한 패킷에 대한 ACK만 보낸다.
- 상단 코드 설명에 따라, time.sleep(TIMEOUT * 2) 코드를 통해 서버의 안전한 종료를 보장(마지막 Ack 후에도 대기)한다.
- 손실 없이 원본과 동일한지 어떻게 유추할 수 있을까?
결과

학번이 적힌 차차 이미지를 수신했다.
과제 3: Calculate throughput in user space
1) Stop And Wait
*기존 코드에서 SUGGESTED_SERVER_WINDOW_SIZE 값을 1로 설정하여 Stop and Wait과 동일하게 만듦


⇒ Throughput = 45,679.79 bps
2) Go Back N
*Server Window Size = 5


⇒ Throughput = 51,178.79 bps
비교
Stop And Wait이 Go Back N보다 성능이 좋지 않다. 매 패킷마다 서버가 ACK를 기다려야 하니, 전체 전송 속도가 느려져 throughput이 낮게 나온 것이다. 반면 Go Back N은 연속 전송이 가능해서 대기 시간이 줄고 처리 속도가 높아지니 throughput이 상대적으로 높게 나왔다.
Q. Window Size에 따라 달라질까?
Go Back N에서 SUGGESTED_SERVER_WINDOW_SIZE값을 여러 가지 시도해본 결과,
1 → 45,679.79 bps
2 → 53,256.80 bps
3 → 55,466.51 bps
4 → 48,626.36 bps
5 → 51,178.79 bps
10 → 60,113.38 bps
15 → 58,204.75 bps
다른 요인의 영향이 있을 수 있어 단조 증가하는 것은 아니지만, Window size가 커질수록 throughput이 높아지는 경향을 보인다. (같은 Window size로 여러 번 시도해봐도 매번 다른 값이 나온다)
cf) Window Size가 16 이상으로 지정되면?

- 클라이언트 요청을 무시하고 임의로 서버가 15로 지정한다. 시퀀스 번호의 최대 범위가 16(0~15)이기 때문에, 윈도우 크기가 이를 초과하면 중복된 시퀀스 번호가 동시에 존재할 수 있는 문제가 발생할 수 있어 이를 방지한다.
- 시퀀스 번호가 중복되면 클라이언트는 받은 패킷이 이전에 받았던 것인지, 새로운 것인지 구분할 수 없어 데이터의 순서가 꼬이거나 중복 저장되는 오류가 발생할 수 있다. 이런 충돌을 방지하기 위해 윈도우 크기는 시퀀스 범위보다 작아야 한다.
Leave a comment