실습 목표#
- TCP 예제 코드를 실행하고 연결이 맺어지는 과정을 WireShark로 분석한다.
- SEQ와 ACK 번호로 신뢰성이 어떻게 확보되는지 확인한다.
- 코드를 직접 고쳐 보내는 데이터를 바꾸고 서버 응답을 추가한다.
실습. TCP 예제 코드 실행#
실습 진행#
라즈베리파이와 노트북에서 각각 아래와 같이 build한다. 라즈베리파이에서 tcp_server를 먼저 실행하고, 노트북에서 tcp_client를 실행한다.
라즈베리파이·터미널
make
./tcp_server
노트북·WSL
make
./tcp_client
WireShark에서 필터를 'tcp.port==9999'로 설정한다.

SYN 분석#
다음 표를 참고하여 SYN 비트를 분석한다.
9c 6d 27 0f 5b 52 f3 91 00 00 00 00 a0 02 fa f0
63 e4 00 00 02 04 05 b4 04 02 08 0a a4 e6 9c 8c
00 00 00 00 01 03 03 0a
| 오프셋 | 바이트 | 필드 | 의미 |
|---|---|---|---|
| 0~33 | 1타임과 같은 구조 |
Ethernet + IP Header | - |
| 34 ~ 35 | 96 6d |
Src Port | OS가 임시 할당한 Port #, 40045 |
| 36 ~ 37 | 27 0f |
Dst Port | 9999 |
| 38 ~ 41 | 5b 52 f3 91 |
Seq # | 첫 Seq #는 임의의 값, 상대 번호 0 |
| 42 ~ 45 | 00 00 00 00 |
Ack # | 첫 Ack #는 0 |
| 46 | a0 |
Data Offset / Reserved | TCP Header 길이, 10 X 4 Bytes = 40 Bytes |
| 47 | 02 |
TCP Flags | SYN |
| 48 ~ 49 | fa f0 |
Window Size | 수신 버퍼 크기, 64240 |
| 50 ~ 51 | 63 e4 |
Checksum | 오류 검출용 |
| 52 ~ 53 | 00 00 |
Urgent Pointer | 긴급 데이터 위치, 미사용 |
| 54 ~ 73 | 02 04 05 b4 ⋯ 03 03 0a |
TCP Options | MSS 1460 · SACK · Timestamp · WS 1024 |
SYN, ACK 분석#
다음 표를 참고하여 SYN 비트와 ACK 비트를 분석한다.
27 0f 9c 6d 98 f3 0d d5 5b 52 f3 92 a0 12 fe 88
aa 9d 00 00 02 04 05 b4 04 02 08 0a 49 f2 c4 e2
a4 e6 9c 8c 01 03 03 0a
| 오프셋 | 바이트 | 필드 | 의미 |
|---|---|---|---|
| 0~33 | 1타임과 같은 구조 |
Ethernet + IP Header | - |
| 34 ~ 35 | 27 0f |
Src Port | 9999 |
| 36 ~ 37 | 9c 6d |
Dst Port | OS가 임시 할당한 Port #, 40045 |
| 38 ~ 41 | 98 f3 0d d5 |
Seq # | 첫 Seq #는 임의의 값, 상대 번호 0 |
| 42 ~ 45 | 5b 52 f3 92 |
Ack # | SYN의 Seq # + 1, 상대 번호 1 |
| 46 | a0 |
Data Offset / Reserved | TCP Header 길이, 10 X 4 Bytes = 40 Bytes |
| 47 | 12 |
TCP Flags | SYN + ACK |
| 48 ~ 49 | fe 88 |
Window Size | 수신 버퍼 크기, 65160 |
| 50 ~ 51 | aa 9d |
Checksum | 오류 검출용 |
| 52 ~ 53 | 00 00 |
Urgent Pointer | 긴급 데이터 위치, 미사용 |
| 54 ~ 73 | 02 04 05 b4 ⋯ 03 03 0a |
TCP Options | MSS 1460 · SACK · Timestamp · WS 1024 |
ACK 분석#
다음 표를 참고하여 ACK 비트를 분석한다.
9c 6d 27 0f 5b 52 f3 92 98 f3 0d d6 80 10 00 3f
d7 b5 00 00 01 01 08 0a a4 e6 9c 8d 49 f2 c4 e2
| 오프셋 | 바이트 | 필드 | 의미 |
|---|---|---|---|
| 0~33 | 1타임과 같은 구조 |
Ethernet + IP Header | - |
| 34 ~ 35 | 9c 6d |
Src Port | OS가 임시 할당한 Port #, 40045 |
| 36 ~ 37 | 27 0f |
Dst Port | 9999 |
| 38 ~ 41 | 5b 52 f3 92 |
Seq # | SYN의 Seq # + 1, 상대 번호 1 |
| 42 ~ 45 | 5b 52 f3 92 |
Ack # | SYN, ACK의 Seq # + 1, 상대 번호 1 |
| 46 | 80 |
Data Offset / Reserved | TCP Header 길이, 8 X 4 Bytes = 32 Bytes |
| 47 | 10 |
TCP Flags | ACK |
| 48 ~ 49 | 00 3f |
Window Size | 수신 버퍼 크기, 63 X 1024 = 64512 |
| 50 ~ 51 | d7 b5 |
Checksum | 오류 검출용 |
| 52 ~ 53 | 00 00 |
Urgent Pointer | 긴급 데이터 위치, 미사용 |
| 54 ~ 65 | 01 01 08 0a ⋯ c4 e2 |
TCP Options | NOP · Timestamp 12 Bytes |
데이터 전송 분석#
9c 6d 27 0f 5b 52 f3 92 98 f3 0d d6 80 18 00 3f
d3 15 00 00 01 01 08 0a a4 e6 9c 8e 49 f2 c4 e2
48 65 6c 6c 6f 20 54 43 50 20 66 72 6f 6d 20 6c
61 70 74 6f 70
| 오프셋 | 바이트 | 필드 | 의미 |
|---|---|---|---|
| 0~33 | 1타임과 같은 구조 |
Ethernet + IP Header | - |
| 34 ~ 35 | 9c 6d |
Src Port | OS가 임시 할당한 Port #, 40045 |
| 36 ~ 37 | 27 0f |
Dst Port | 9999 |
| 38 ~ 41 | 5b 52 f3 92 |
Seq # | 상대 번호 1, 데이터의 시작 위치 |
| 42 ~ 45 | 98 f3 0d d6 |
Ack # | 상대 번호 1 |
| 46 | 80 |
Data Offset / Reserved | TCP Header 길이, 8 X 4 Bytes = 32 Bytes |
| 47 | 18 |
TCP Flags | PSH + ACK |
| 48 ~ 49 | 00 3f |
Window Size | 수신 버퍼 크기, 63 X 1024 = 64512 |
| 50 ~ 51 | d3 15 |
Checksum | 오류 검출용 |
| 52 ~ 53 | 00 00 |
Urgent Pointer | 긴급 데이터 위치, 미사용 |
| 54 ~ 65 | 01 01 08 0a ⋯ c4 e2 |
TCP Options | NOP · Timestamp 12 Bytes |
| 66 ~ 86 | 48 65 6c 6c 6f ⋯ 6f 70 |
Data | Hello TCP from laptop, 21 Bytes |
과제 1. 보내는 데이터 변경#
노트북 tcp_client_blank.cpp에서 보내는 데이터를 변경한다. 실습 1과 같이 build후, 라즈베리파이에서 tcp_server를 먼저 실행하고 노트북에서 tcp_client_blank를 실행한다.
tcp_client_blank.cpp
const char* msg = "________________";
노트북·WSL
make
./tcp_client_blank
송신 측에서 보낸 바이트 수가 많을 수록 수신 측의 ACK 값이 증가한다. "_" 안의 문자열을 바꿔가며 전송해 보자.
관찰 포인트
이 때 라즈베리파이 화면에 바뀐 내용이 찍히는지, 캡처의 Len 값이 얼마로 바뀌는지 확인한다.
예시 문장
const char* msg = "Hello from ACE Lab";
과제 2. 서버가 응답 보내기#
이번에는 라즈베리파이 tcp_server_blank.cpp에서 recv 아래에 응답을 보내는 줄을 추가하여 수신 측에서 응답을 보낸다. TCP는 이미 연결된 소켓으로 보내기 때문에 주소를 다시 적지 않아도 된다.
send('_')에 들어갈 문장을 바꿔가며 보내보자.
tcp_server_blank.cpp
const char* reply = ________;
send(____, reply, strlen(reply), 0);
라즈베리파이·터미널
make
./tcp_server_blank
관찰 포인트
1타임 UDP의 sendto와 무엇이 다른지 생각해 보고 빈칸을 채워보자.
예시 문장
const char* reply = "ACK from Pi";
send(conn, reply, strlen(reply), 0);
관찰 포인트
노트북 측에서 ACK from Pi(또는 직접 작성한 문장)이 왔는지, 캡처에 양방향 데이터가 보이는지 확인한다.