5

更新 #4: UDP を使用してアナウンス メッセージを送信するためのデモ Java スニペットが追加されました (接続が最初であることを忘れないでください!) 以下の応答を確認してください。

================================================== ==

更新#3:私はそれを機能させることができました.以下に提示されたメソッドdoConnect()はOKです。詳細は以下の私自身の応答にあります.

================================================== ==

私は主に、アナウンス URL のプロトコルが UDP の場合にトラッカーの応答をダウンロードする方法に興味があります。

詳細: これらは、有効な torrent ファイルからのいくつかのアナウンス URL です (最初のものはメインのものです)。

http://tracker.torrentbox.com:2710/announce
udp://elbitz.net:80/announce.php?passkey=362fc69de3402e8ef5794c7ecf7c58d4
udp://tracker.podtropolis.com:2711/announce

プロトコルがHTTPの場合、すべてがうまくいきます。これが私がどのように機能するかです:

String fullUrl = announceURL + "?info_hash=" + this.m_TorrentInfoHashAsURL + .. // i add the params
URL url = new URL(fullUrl);
URLConnection connection = url.openConnection();
InputStream is = connection.getInputStream();
.. //reading the stream

プロトコルが UDP の場合、URL コンストラクターは「java.net.MalformedURLException: unknown protocol: udp」をスローします。

したがって、問題は次のように再開できると思います: UDP プロトコルで URL から resounce をダウンロードするにはどうすればよいですか? (簡単だといいのですが、データグラムのものは表示されません)

更新#1:

私はオンラインでさらに調査を行い、以下に貼り付けられた次の構造にたどり着きました(動作するはずですが、動作しません。つまり、ローカルでは動作しますが、実際のトラッカーでは動作しません)

仕様へのリンク: http://www.bittorrent.org/beps/bep_0015.html

例:これはソケットを作成する方法ですが、有効なトラッカーでは応答として何も返されないため、何かが機能していません:

if full url: udp://elbitz.net:80/announce.php?passkey=362fc69de3402e8ef5794c7ecf7c58d4
this.m_TrackerHost: elbitz.net 
this.m_TrackerPort: 80

private DatagramSocket m_WorkingSocket;
    private DatagramSocket getWorkingSocket() {
        Logger.d(TAG, "getWorkingSocket()");

        if(this.m_WorkingSocket==null){
            Random rnd = new Random();
            for (int i = 0; i < 100; i++) {
                try {
                    int port = 15000 + rnd.nextInt(15000); // [15000-30000)
                    DatagramSocket result = new DatagramSocket(port);
                    InetAddress trackerAddress = InetAddress.getByName(this.m_TrackerHost);
                    result.connect(trackerAddress, this.m_TrackerPort);
                    this.m_WorkingSocket = result;
                } catch (SocketException se) {
                    Logger.w(TAG, "getWorkingSocket() - port is taken");
                } catch (SecurityException se) {
                    Logger.w(TAG, "getWorkingSocket() - port is blocked?");
                } catch (UnknownHostException e) {
                    Logger.w(TAG, "getWorkingSocket() - unkwnown host?");
                }
            }
        }

        return this.m_WorkingSocket;
    }

& 今、最初の通信フェーズである doConnect からの完全なコード (次はアナウンス .. 同様のコードがあります)

private boolean doConnect() throws IOException{
    Logger.d(TAG, "doConnect()");

    DatagramSocket workingSocket = this.getWorkingSocket();
    DatagramPacket sendPacket = null, receivePacket = null;

    byte[] sendData = null;
    byte[] receiveData = null;
    int round = 0;

    Logger.d(TAG, "doConnect(): first round, timeout: " + this.getTimeoutInMillis(ACTION_ID_CONNECT, round));
    while(true) {
        if(round==8){
            Logger.w(TAG, "doConnect() - failed to connect with tracker, consumed in vain all 8 rounds..");
            return false;
        }

        workingSocket.setSoTimeout(this.getTimeoutInMillis(ACTION_ID_CONNECT, round));

        if(receivePacket==null){
            /*
            Offset  Size            Name            Value
            0       32-bit integer  action          0 // connect
            4       32-bit integer  transaction_id
            8       64-bit integer  connection_id
            16  */
            receiveData = new byte[16]; //CONNECT: at least 16 bytes
            receivePacket = new DatagramPacket(receiveData, receiveData.length);

            sendData = this.getConnectRequest();//return byte[] with everything..just like in specs
            sendPacket = new DatagramPacket(sendData, sendData.length); 
        }

        try {
            Logger.d(TAG, "doConnect() - sending connect data: " + (Arrays.toString(sendData)));
            workingSocket.send(sendPacket);
            workingSocket.receive(receivePacket);
            break;
        } catch (SocketTimeoutException ste) {
            round ++;
            Logger.w(TAG, "doConnect() connect - new round: " + (round+1) + "|timeout: " + this.getTimeoutInMillis(ACTION_ID_CONNECT, round));
            continue;
        }
    }

    byte[] connectResponse = receivePacket.getData();
    int actionIdFromResponse = Utils.byteArrayToInt(Utils.subArray(connectResponse, 0, 4));
    int transactionIdFromResponse = Utils.byteArrayToInt(Utils.subArray(connectResponse, 4, 4));
    long connectionIdFromResponse = Utils.byteArrayToLong(Utils.subArray(connectResponse, 8, 8));

    if(transactionIdFromResponse!=this.m_TransactionId){
        Logger.w(TAG, "doConnect() - received different transactionId");
        return false;
    }

    if(actionIdFromResponse!=ACTION_ID_CONNECT){
        Logger.w(TAG, "doConnect() - didnt received ACTION_ID_CONNECT");
        return false;
    }

    //store connectionId
    this.m_ConnectionId = connectionIdFromResponse;
    return true;
}

問題が残っています..トラッカーからの応答を受信しません(他のURLでも試しました)また、新しい質問:完全なURLに詳細情報が含まれている場合(例:/アナウンス)、elbitz.net、ポート:80でソケットを作成しても問題ありませんか?

アップデート #2

上記のコードは問題なく動作するようです.. Google で、この仕様を実装したトラッカーのリストを見つけました & 出来上がりの応答が発生しました (例: "udp://tracker.openbittorrent.com:80")

新しい質問と仕様はこちら: http://www.bittorrent.org/beps/bep_0015.html - ピアのリストを取得する方法がわかりません?? .. torrent トラッカーへの通常のリクエスト (http による) には、通常のレスポンス (ベンコードされたマップ) と圧縮されたレスポンス (バイナリ形式) の 2 つのケースがありました。それで、ピアのリストは今ですか?

  • 仕様から、これはアナウンス応答です:
/*
              Offset      Size            Name            Value
              0           32-bit integer  action          1 // announce
              4           32-bit integer  transaction_id
              8           32-bit integer  interval
              12          32-bit integer  leechers
              16          32-bit integer  seeders
              20 + 6 * n  32-bit integer  IP address
              24 + 6 * n  16-bit integer  TCP port
              20 + 6 * N  */

私のテストから、私は常にフィールドに同じ値を受け取ります:IPアドレスとTCPポート..さらに、リクエストごとに1つの応答を取得する..だから、これはできません!..ピアのリストが必要です!

助けてください!まだ実装していない応答メッセージのタイプはスクレイプとエラーだけです...しかし、誰も興味のある情報を含んでいません(ピア情報:IPとポート)例:スクレイプ

Offset      Size            Name            Value
0           32-bit integer  action          2 // scrape
4           32-bit integer  transaction_id
8 + 12 * n  32-bit integer  seeders
12 + 12 * n 32-bit integer  completed
16 + 12 * n 32-bit integer  leechers
8 + 12 * N
4

2 に答える 2

3

私自身の質問に対する私の回答..ありがとう、私!

  • udp:// で始まる場合は有効です
  • URL が udp://elbitz.net:80/announce.php?passkey=362fc69de3402e8ef5794c7ecf7c58d4 の場合、ポート 80 を使用して udp://elbitz.net にソケットを作成します (UDP ソケットの作成時に /announce 部分は使用されません)。
  • UDP であるため、応答は保証されません (例外はスローされず、待つだけです)。仕様には最大 8 ラウンドのルールが記載されています...しかし、これは、トラッカーが機能していないことに気付くまでかなりの時間を待つことを意味します。
  • UDP による通信の場合、2 種類のリクエスト (接続とアナウンス、それぞれ 1 つの UDP パッケージとして) を送信し、1 つの応答を受け取ります (もちろん、運が良ければ、UDP パッケージとしても)..使用可能な情報を抽出するには、応答を解析しbyteBufferます。ここでも、実際の情報は次のとおりです: http://www.bittorrent.org/beps/bep_0015.html ;
  • Connect 応答が得られるまではアナウンスできません (transactionId仕様を読む必要があります) my doConnect()is OK, 私はそれからほとんど何行も変更しませんでした (例: 私は使用された by にバイト単位の最大サイズをbyteBuffer使用していますDataGramPacket。新しいバイトを意味します[ [16] の代わりに 65508] - 最小サイズ 応答を受信すると、残りはパディング バイト、つまり 0 になります)。
  • ご覧のとおりdoConnect、while ループがあります (最大 8 ラウンドのルールを示しています)。
  • socketTimeOut継続的に増加しています (仕様、30 秒、90 などの数式を使用)。
  • 接続応答を受信した後、アナウンス()を行う必要があります。メソッドの内容は似ていますが、要求は異なります(仕様を参照)。また、バッファ(新しいバイト[65508])に最大サイズを使用しますが、今回はより意味があります。受信したピアのリスト サイズはハードコードされていません (1/8/10 ピアの可能性があります...私は常に 10 を要求します)..
    (最小サイズは 20 バイトです。これは、メッセージにピア情報が追加されていない場合に発生します)。繰り返しますが、この応答は 1 つのパケットです (最大 65508 バイトには余裕があります... 70% のパディングが得られると思います)。
  • ブロッキング メソッド ( ) が原因ですべてが遅く、receive()私はそれを独自のスレッドに持っています (通常、応答するトラッカーは 2 ラウンド以上消費する必要はありません..それは素晴らしいことです)。
  • 上記のコードで見られないのは、1 分ルール (1 分後に再度接続する) というルールです。
  • 私の意見: この醜いコード! UDP での作業.. & スレッド、すべてが遅くてブロックされているため...

アップデート***

udp を操作するために切り取られたコード

DatagramSocket workingSocket = ?;//
DatagramPacket sendPacket = null, receivePacket = null;
byte[] sendData = null;
byte[] receiveData = null;

receiveData = new byte[65508]; //NOTE: at least 16 bytes | 65508 is max size, unused bytes are 0
receivePacket = new DatagramPacket(receiveData, receiveData.length);

sendData = this.getRequestTypeAnnounce(announceUDPWrapper.a_TransactionId);
sendPacket = new DatagramPacket(sendData, sendData.length); 
workingSocket.send(sendPacket);
workingSocket.receive(receivePacket);
byte[] fullResponse = receivePacket.getData();

アナウンスメッセージを作成するために切り取られたコード

private byte[] getRequestTypeAnnounce(AnnounceUDPWrapper announceUDPWrapper) {
    //long connectionId, int transactionId, int tcpPort
    ByteBuffer bBuffer = ByteBuffer.allocate(98);
    bBuffer.putLong(announceUDPWrapper.a_ConnectionId);
    bBuffer.putInt(ACTION_ID_ANNOUNCE);
    bBuffer.putInt(announceUDPWrapper.a_TransactionId);
    bBuffer.put(announceUDPWrapper.a_InfoHash);//<<<< what you asked.. adding the infoHash which is byte[]
    bBuffer.put(this.m_MyId);

    bBuffer.putLong(announceUDPWrapper.a_Downloaded);
    bBuffer.putLong(announceUDPWrapper.a_Uploaded);
    bBuffer.putLong(announceUDPWrapper.a_Left);
    bBuffer.putInt(announceUDPWrapper.a_EventType.ordinal());

    bBuffer.put(Utils.intToByteArray(0));// ip, 0 = default
    bBuffer.putInt(0);//key
    bBuffer.putInt(10);//num_want

    byte[] portAsBytes = Utils.intToByteArray(announceUDPWrapper.a_ListeningTCPPort);
    bBuffer.put(Utils.subArray(portAsBytes, 2, 2)); //port
    return bBuffer.array();
}
于 2013-03-20T18:48:17.920 に答える
0

ここでネットワーク層を混在させているようです。

udp は下位層にあります。http:// は tcp を経由しますが、他の一部のプロトコルは udp を経由できます。IP はその下にあるため、使用しようとしているプロトコルは「torrent over udp, over IP」のようなものです。

torrent/udp プロトコルのドキュメントについては、こちらを参照してください。

于 2013-03-03T09:49:46.027 に答える