问题1
如果您正在使用基于流的I/O,则应坚持使用DataInputStream和DataOutputStream来写入原始类型(例如,byte,int,long等)的简单方法:
Socket s; // assume this is already connected
DataOutputStream out = new DataOutputStream( s.getOutputStream );
out.writeByte( 1 );
out.writeInt( 0 );
out.flush(); // optional
如果您正在使用非阻塞I/O(例如java.nio包中的类),则请使用
ByteBuffer。
Socket s; // assume this is already connected
SocketChannel = s.getChannel();
ByteBuffer buf = ByteBuffer.allocate(8); // two 4-byte integers
buf.put( 1 ).putInt( 0 );
buf.flip();
c.write( buf ); // assuming channel is writable :)
这些方法会自动处理字节序问题。
问题2
(请注意,通常您传输的是片段(blocks),而不是整块文件。我将在此处略过:) )
在发送/接收片段时,最好将文件(或文件)视为连续的,就像您所说的那样。 .torrent 文件在 info 字典中包含有关文件边界的信息。 在 多文件案例 中,每个文件都有路径和长度;单文件案例 有一个可选名称和长度。由于您知道块大小、块数量和总内容长度(所有这些都来自 .torrent 文件),因此可以在接收它们时将块“放在正确的位置”。
一个简单的做法是创建一个与 torrent 大小相等的单个文件。当您接收到一个块时,请将其写入此单个文件内的正确字节偏移量 (有时称为“.downloading”文件)。例如,考虑由两个文件组成的 torrent:
a/b/file1.txt [100 bytes]
a/b/file2.txt [200 bytes]
piece size (pz) = 50 bytes
total size (tz) = 100+200 = 300 bytes
number pieces (np) = 300/50 = 6
file = my_torrent.downloading
假设我们将数据块和字节偏移量从零开始编号。如果你接收到了第一个数据块的所有内容,那么它在my_torrent.downloading中的(起始)字节偏移是多少?它在(1 * pz)=(1 * 50)=
50处。第0个数据块在哪里?在(0 * pz)=(0 * 50)=
0处。依此类推...
我敢打赌你现在可以想出如何将这个.downloading文件转换为你的种子中的“真实”内容。
问题3
当参与BitTorrent群集时,你同时向多个对等方上传和下载数据块。请思考一下这一点。在你从某个对等方请求一个数据块的同时,另一个对等方可能正在从你那里请求同样的数据块。这与HTTP的语义完全不同,正如你已经指出的那样。因此,直接回答你的问题,其他对等方会向你请求他们感兴趣的数据。 :)
在向同行请求数据块之前,请确保该同行确实拥有您想要的数据块(可以查看
位域和have消息),并且您已经遵守了适当的
chocking/interested行为。在此基础上,您通常需要按照
最稀有优先的顺序从已知同行列表(由跟踪器或DHT告诉您)请求数据。规范讨论了这一点,并且在这里有很多优化和礼貌方面的考虑。(例如tit-for-tat行为)。您可能会注意到
规范没有详细说明这些内容。这是因为BitTorrent客户端的许多机密都隐藏在这部分实现中。 :)
希望这能对你有所帮助!