실습 정보
- SERVER: Ubuntu 22.04
- IP:
192.168.219.113
- PORT:
65432
- IP:
- CLIENT: Windows 11
- IP:
192.168.219.164
- PORT:
51112
- IP:
TCP 패킷 분석
TCP 헤더 구조
- Source Port/ Detination Port: 출발지 포트와 목적지 포트를 나타냄
- Sequence Number: 올바른 순서로 데이터를 보내기 위한 고유 일련 번호
- Acknowledgement number: 다음 세그먼트를 수신할 준비가 된 상태를 나타내는 번호
- Header length: Header 길이를 32bit 단위로 나타낸다. 최소 필드 값은 20 bytes 이다.
- Window size Field: 흐름 제어를 위해 송신 시스템의 가용 가능 버퍼 크기를 나타냄 (Byte 단위)
- Checksum Field: TCP segment의 내용이 유효한지 검증
TCP 연결을 위한 준비
- TCP 연결을 테스트해보기 위해 파이썬으로 간단한 TCP 소켓 프로그래밍을 해볼 것이다.
server.py
import socket
# 서버 IP, 포트 설정
HOST :str = "192.168.219.113"
PORT :int = 65432
# 소켓 객체 생성
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s:
# 소켓을 HOST와 PORT에 바인딩
s.bind((HOST, PORT))
# 클라이언트의 연결 요청 대기
s.listen()
print("서버가 시작됨. 클라이언트의 연결을 기다리는 중 ..")
while True:
# 연결 수락
conn, addr = s.accept()
with conn:
print('연결 되었음!', addr)
while True:
data = conn.recv(1024)
if not data:
break
print("받은 데이터: ", data.decode())
conn.sendall(data) # 받은 데이터를 그대로 클라이언트에 전송
client.py
import socket
# 서버 IP, 포트 설정
HOST :str = "192.168.219.113"
PORT :int = 65432
# 소켓 객체 생성
with socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) as s:
# 서버에 연결 요청
s.connect((HOST,PORT))
# 메시지 생성
message :bytes = input("input string ..\n:").encode()
# 서버에 메시지 전송
s.sendall(message)
data = s.recv(1024)
print("받은 데이터: ", data.decode())
- Client와 Server 측 코드를 위와 같이 작성하였다.
- 위 프로그램을 실행하여 패킷을 캡쳐해보자.
- 위 방법 말고도, 웹 사이트에 접속하는 것만으로도 3-way, 4-way Handshake 과정 패킷 캡쳐가 가능하다.
코드를 실행하고 Client 측에서 포트를 확인해보자.
왼쪽 192.168.219.164(Client)
, 오른쪽 192.168.219.113(Server)
연결이 서로 성립(Established) 된 것을 볼 수 있다.
이제 패킷을 확인해보자.
TCP 패킷
3-Way Handshake 과정 - 연결 수립
사진에 나온 패킷 순서대로 살펴보도록 하자.
- 9.
192.168.219.164(Client)
측에서192.168.219.113(Server)
와 연결을 위해SYN
패킷을 보내고 있다. - 10.
.113(Server)
는.164(Client)
의 연결을 받았다는ACK
응답을 하였다. 그리고.113(Server)
도 연결 요청(SYN
)을 보낸다. - 11.
.164(client)
는 최종적으로ACK
응답을 하게 됨으로써 서로 1:1 연결이 되었다.
Data Send/Receive 과정
- 55.
PSH
flag의 의미는 데이터를 버퍼에 쌓지 말고 즉시 보내라 라는 의미이다.- 단순히 ACK만을 보내면 중간중간 데이터가 버퍼에 쌓이기 시작하여
PSH
flag와 함께 보내면 즉시 보내지게 된다.
- 단순히 ACK만을 보내면 중간중간 데이터가 버퍼에 쌓이기 시작하여
클라이언트단에서 "Hello?" 라는 문자열을 전송하였다.
패킷을 자세히 확인해보자.
ACK
,PSH
flag가 Set이 된 상태이다- 또한 내가
Hello?
라는 문자열을 보낸 상태이고 Data Size는 6 bytes 인 것을 확인할 수 있다. - 처음 Seq Number를 보면 1로 설정되어 있고 다음 Seq number는 Data Size(=6)를 더한 7을 다음 Seq number로 맞추고 있다.
- 56.
192.168.219.113(Server)
는192.168.219.164(Client)
가 보낸 메시지를 잘 받았다는ACK
응답을 보내고 있다.ACK
번호를 7로 맞추고 있음.
- 57.
192.168.219.113(Server)
도192.168.219.164(Client)
에게로 메시지를 보내고 있다. 55번 패킷과 유사하게PSH
,ACK
flag도 함께 보내고 있다.
4-Way Handshake 과정 - 연결 해제
- 58.
.164(Client)
는.113(Server)
에게 연결 종료를 위해FIN/ACK
패킷을 보내고 있다. - 59.
.113(Server)
는.164(Client)
에게 ACK 확인 응답을 보내고 세션이 끝날 때까지 기다린다 (CLOSE_WAIT) - 60. 마찬가지로 통신이 끝나면 Server도 Client에게
FIN/ACK
패킷을 보낸다. - 61. Client에서 수신 되었다는 ACK 메시지를 보내며 통신이 종료된다.
UDP 패킷 분석
UDP 연결을 위한 준비
- UDP 연결을 테스트해보기 위해 파이썬으로 간단한 UDP 소켓 프로그래밍을 해볼 것이다.
server.py
import socket
# 서버 IP, 포트 설정
UDP_IP :str = "192.168.219.113"
UDP_PORT :int = 5005
# UDP 소켓 생성
server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
# 소켓을 주소에 바인딩
server_socket.bind((UDP_IP, UDP_PORT))
print("UDP 서버 시작됨")
while True:
# 클라이언트로부터 데이터 수신
data, addr = server_socket.recvfrom(1024)
print(f"수신된 데이터 : {data.decode()}")
# 클라이언트에게 데이터 전송
server_socket.sendto(data, addr)
client.py
import socket
# 서버 IP, 포트 설정
UDP_IP :str = "192.168.219.113"
UDP_PORT :int = 5005
# UDP 소켓 생성
with socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) as s:
while True:
message :str = input("전송할 메시지 입력\n:")
# 서버에 데이터 전송
s.sendto(message.encode(), (UDP_IP, UDP_PORT))
# 서버로부터 데이터 수신
data, addr = s.recvfrom(1024)
print(f"서버로부터 수신된 데이터: {data.decode()}")
- Client와 Server 측 코드를 위와 같이 작성하였다.
- 위 프로그램을 실행하여 패킷을 캡쳐해보자.
- client 측에서 문자열을 입력하고 Server에 전달하면 서버는 그대로 반환하는 일종의 Echo system 형태이다.
- client에서
hi
,hello
를 보내고 서버로부터 그대로 반환 받았다.
UDP 패킷
- UDP 패킷의 경우 TCP 패킷과 다르게 매우 단순하다. TCP 에서는 연결 수립과 해제에서 handshake 과정이 필요하였다.
- 그러나 UDP의 경우 handshake 과정 없이 data 만을 전송한다.