Update #4: Demo java snippet added for working with UDP & sending announce msg (remember connect is first!) check own response bellow.
============================================= =====
Update #3: I managed to make it work, method doConnect() presented bellow is OK, more info in my own response bellow.
============================================= =====
我主要对当 announce url 协议(protocol)为 UDP 时如何下载跟踪器响应感兴趣。
详细信息: 所以这些是来自有效 torrent 文件的一些公告 url(第一个是主要的)
http://tracker.torrentbox.com:2710/announce
udp://elbitz.net:80/announce.php?passkey=362fc69de3402e8ef5794c7ecf7c58d4
udp://tracker.podtropolis.com:2711/announce
如果协议(protocol)是 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
如果协议(protocol)是 UDP,URL 构造函数将抛出“java.net.MalformedURLException:未知协议(protocol):udp”
所以我想问题可以归结为以下几点:如何从 UDP 协议(protocol)上的 URL 下载响应? (希望它简单 & 我没有看到数据报的东西)
更新#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 包含更多信息(例如:/announce)时,是否可以在 elbitz.net、端口:80 上创建套接字?
更新#2
上面的代码似乎工作正常..我在谷歌上找到了一个已经实现了这个规范的跟踪器列表&瞧,响应发生了(例如:“udp://tracker.openbittorrent.com:80”)
新问题和规范在这里:http://www.bittorrent.org/beps/bep_0015.html - 我似乎没有看到如何获得同行名单? .. 在对 Torrent Tracker 的正常请求中(通过 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 端口 ..再加上每个请求我都会得到一个响应..所以这不可能!..我需要一份同行名单!
请帮忙! 我还没有实现的唯一类型的响应消息是抓取和错误...但没有人包含我感兴趣的信息(对等信息: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
最佳答案
我对我自己的问题的回答..谢谢,我!
- 如果以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
,阅读规范) 我的doConnect()
没问题,我从中更改了很少几行(例如:我使用的byteBuffer
以字节为单位使用最大大小 通过DataGramPacket
,表示新字节 [65508] 而不是 [16] - 最小大小。当收到一个 response 其余部分将是填充字节,即 0。 - 正如您在
doConnect
中看到的那样,有一个 while 循环(它显示了最多 8 轮规则); socketTimeOut
不断增加(使用规范中的公式,30 秒,90.. 等)。- 收到connect response后要做announce(),方法内容类似,
请求不同(再次参见规范),我再次使用缓冲区的最大大小(新字节[65508])但这次它
更有意义,因为收到的对等点的列表大小不是硬编码的(它可以是 1/8/10 个对等点......我总是要求 10)..
(最小大小为 20 字节,当没有在 msg 中添加对等信息时会发生这种情况)。同样,这个响应只是一个数据包(那里 最大 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();
}
关于java - 洪流相关 : tracker response on UDP protocol (Update #3 - working),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15184376/