2026년 7월 10일 금요일

Res – Tracker that tracks torrents used in North Korea - Part - 4

Sub Topic - EP.04 - How Does a Tracker Return Peer Lists?

===== Intro =====

1.     제 나름대로 구상한 방식입니다

2.     막히면 돌아가고 깊이가 방대하면 포기하고 다음으로!

3.     구성 : 토픽 기반기술 학습 기반기술 기반 실습(되는데로..) – 최종 프로젝트

 

===== Overall story =====

1.     1 = 북한 ip의 구성

2.     2 = Torrent 이용중 IP 동작

3.     3 = 마그넷 링크를 뜯어보고 토렌트가 무엇으로 파일을 식별하는지 이해

4.     4 = Tracker Peer 목록을 어떻게 알려줄까? (실제 통신)

 

 

Step – 0

== InfoHash를 알게되면 Peer는 어떻게 찾는 거지?

지난 글에서 Magnet Link 안에 있는 InfoHash가 무엇인지 살펴 보며 토렌트 네트워크에서 파일은 이름이 아니라 InfoHash 로 식별되는 사실도 확인했는데.. 궁금한점이 생긴다

è InfoHash 를 알게되면 Peer는 어떻게 찾는건데?

토렌트 클라이언트가 무작정 인터넷 전체를 뒤질수 없기 때문에 Tracker 서버에게 물어본다, 그리고 Tracker 서버는 해당 Torrent 를 공유하고 있는 Peer 들의 정보를 반환한다

 

이번 에피소드에서 Tracker와 실제로 어떤 데이터를 주고받는지 살펴보자

(실제 패킷을 보는 단계까지 해봅시다)

 

Step – 1

== Tracker?

Tracker를 간단히 정의하면!

è 이 토렌트를 공유하고 있는 사람들의 명단을 관리하는 서버

지난번부터 사용하고 있는 ubuntu infoHash Abcdefg12345 이고 만약 참여중인 Peer = 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4 라고 하면 Tracker InfoHash를 질의하면 Peer정보를 반환한다

 

Step – 2

== Tracker에게 보내는 첫 메시지

Tracker에 보내는 메시지중 첫번째 메시지를 Announce Request라고 부르며 Client(사용자) Tracker에게 보내는 메시지는 다음과 같음

GET /announce?

info_hash=...

peer_id=...

port=6881

uploaded=0

downloaded=0

left=123456

event=started

그리고 이를 url 형태로 붙혀서 보내는데 이런식으로 뒤에 QueryString 형태로 정보가 붙는다

è http://tracker.example.com/announce

이때 파라미터를 살펴보자

Info_hash

지난화에 설명한 InfoHash

peer_id=...

클라이언트 식별값(-UT2210-abcdefgh1234 = utorrent 식별값)

*다양한 Torrent client 가 있음)

port=6881

Peer가 연결을 받을 포트

uploaded=0

업로드한 바이트

downloaded=0

다운로드한 바이트

left=123456

남은 다운로드 크기

event=

현재 상태 started / completed / stopped 3가지 상태가 있음

 

이를 기반으로 통신 순서를 정리하면 다음과 같이 진행 된다

è Client ----(Announce) ------>Tracker ------(Peer List) ----> Client

è 매우 단순함

 

Step – 3

== Tracker응답은 어떻게 생겼지?

Tracker응답은 JSON이 아니다, 보통 이런 다양한 값을 보내면 응답이 JSON으로 오는 경우가 많은데 (아래 그림처럼 생긴게 JSON 타입)

{

  "peers":[]

}

 

토렌트는 오래된 프로토콜이라 Bencode라는 자체 포맷을 사용한다!

è Bencode = 토렌트에서 사용하는 직렬화 포맷 (아래를 보자)

 

타입

예시

의미

문자열

4:spam

Spam

숫자

i123e

123

리스트

l

4:spam

4:eggs

e

["spam","eggs"]

딕셔너리

d

3:cow

3:moo

4:spam

4:eggs

e

{

  "cow":"moo",

  "spam":"eggs"

}

*리스트와 딕셔너리에 시작에 l d로 구분하고 e로 마지막임을 표시한부분을 잘 보자*

 

실제 Tracker응답을 보자

d

8:interval

i1800e

 

5:peers

12:

...

e

*d로 시작하니 딕셔너리임을 알수 있다

*실제로는 한 줄의 바이너리 데이터로 온다

JSON 타입으로 해석하면

{

    "interval":1800,

    "peers":"..."

}

**Interval = 30분뒤에 다시 요청하라(Tracker의 과부하를 막기 위한 값)

 

여기서 핵심 정보가 바로 peer 정보이다(실제 Peer 목록이 들어있다)

 

Peer List 형식 = 2가지 방식이 있음

 

1.딕셔너리 방식

[

  {

    "ip":"1.2.3.4",

    "port":6881

  }

]

 

2.Compact 방식

=대부분 사용하며 훨씬 크기도 작다

 

Compact Peer Format

=Peer 하나당 ( IP 4바이트 , Port 2바이트 = 6 바이트만 사용)

=ex) 01 02 03 04 1A E1 -> 4바이트 1.2.3.4  2바이트 6881

è 01 02 03 04 1A E1   1.2.3.4:6881 가 된다

 

여러 Peer가 있을때 상황을 보면

01 02 03 04 1A E1

05 06 07 08 1A E1

09 0A 0B 0C 1A E1

1.2.3.4:6881

5.6.7.8:6881

9.10.11.12:6881

으로 6 바이트씩 끊어서 읽으면 된다

 

Step – 4

== Tracker 통신 분석

패킷의 관점에서 살펴보면 다음과 같이 Tracer에게 질의한다

è Client---------(infoHash, Peer_id, port)----------> Tracker

그럼 Tracker는 다음과 같이 응답해 준다

interval = 1800

 

peers =

[

  1.2.3.4:6881

  5.6.7.8:51413

  9.9.9.9:6881

]

 

그럼 이제 Client 는 전달받은 Peer 1.2.3.4 에게 연결을 시도하고 실패하면 다음 5.6.7.8에 연결을 시도하며 순차적으로 진행한다

 

 

Step – 5

==다음화 예고 – Peer HandShack 는 어떻게 이루어 질까?

l   BitTorrent Handshake

l   Peer ID

l   Reserved Bytes

l   InfoHash 검증

l   C#으로 Handshake 생성하기

 

Tracker에게 Peer를 받았다고 끝이 아니고 이제부터 진짜 본격적입니다~

Peer TCP 연결이 성공하면 BitTorrent 프로토콜의 첫번째 메시지인 HandShacke가 시작 되기 때문입니다

 

===== 실습 =====

=C# 콘솔 프로그램으로 진행

==Compact Peer를 파싱하는 프로그램을 만들어 보자

 

예제 데이터

byte[] peers =

{

    1, 2, 3, 4, 0x1A, 0xE1,

           5, 6, 7, 8, 0x1A, 0xE1,

    9, 10, 11, 12, 0xC8, 0xD5

};

 

파싱 코드

using System;

 

namespace TorrentTrackerDemo

{

    class Program

    {

        static void Main(string[] args)

        {

            // Tracker가 보내준 Peer 데이터라고 가정

            byte[] peers =

            {

                // 1.2.3.4:6881

                1, 2, 3, 4, 0x1A, 0xE1,

 

                // 5.6.7.8:6881

                5, 6, 7, 8, 0x1A, 0xE1,

 

                // 9.10.11.12:51413

                9, 10, 11, 12, 0xC8, 0xD5

            };

 

            Console.WriteLine("=== Peer List ===");

            Console.WriteLine();

 

            for (int i = 0; i < peers.Length; i += 6)

            {

                string ip =

                    $"{peers[i]}." +

                    $"{peers[i + 1]}." +

                    $"{peers[i + 2]}." +

                    $"{peers[i + 3]}";

 

                ushort port =

                    (ushort)((peers[i + 4] << 8) | peers[i + 5]);

 

                Console.WriteLine($"{ip}:{port}");

            }

 

            Console.WriteLine();

            Console.WriteLine("Press any key...");

            Console.ReadKey();

        }

    }

}

 

=변환이 잘 됫네요~

 

***당부의 말***

만들려고 하는 Torrent Tracker 프로젝트 목표는 이거에요

è 어떤 토렌트가, 어떤 Peer에 의해 , 어디서 공유되고 있는지 관찰한다

그 첫번째가 바로 Tracker 응답 분석이에요! 그래서 Tracker를 읽을수 있으면

l   현재 참여 Peer

l   Peer IP

l   Peer Port

l   토렌트 활성도

를 수집할수 있답니다! 그래서 지금까지 두 번의 실습이 문자열 파싱이나 바이트 데이터 해석 정도라서 "이게 뭐가 중요한 거지?" 라고 생각하실 수도 있습니다

하지만 실제 네트워크 프로그래밍에서는 데이터를 주고받는 것보다도, 받은 데이터를 정확하게 해석하는 과정이 훨씬 중요할 때가 많습니다

지금까지 만든 기능들은 모두 최종 목표인 Torrent Tracker를 구성하는 부품들입니다..

아직은 각각의 기능을 따로 살펴보고 있지만, 이후 글에서 하나씩 연결하다 보면 결국 하나의 프로그램으로 완성되는 모습을 보실 수 있을 거에요

댓글 없음:

댓글 쓰기