先来一张图:
在此之前,了解下bt客户端与tracker的交互:
http://my.oschina.net/blackylin/blog/113513
1.解析种子,放到SharedTorrent对象中
/**1.解析torrent文件到SharedTorrent对象中,并分片段*/ BtClient_ShareTorrent=SharedTorrent.fromFile( new File(torrentPath), new File(sourceFileParentPath));
/**发布种子文件 * @param torrentPath torrent文件存放位置 * @param sourceFileParentPath 共享文件的父路径,如:/share1/share2/file.pdf * 则取/share1/share2 * @return Client * */ public Client shareTorrentFile(String torrentPath, String sourceFileParentPath) { //注:这里sourceFileParentPath 如果是自己做种时,这个路径一定得是源文件所在的目录 //如果是添加torrentFile而下载时,为下载文件目录 this.saveDownloadPath=sourceFileParentPath; this.torrentPath=torrentPath; try { /**1.解析torrent文件到SharedTorrent对象中,并分片段*/ BtClient_ShareTorrent=SharedTorrent.fromFile( new File(torrentPath), new File(sourceFileParentPath)); /**2.初始化客户端,并注册监听*/ BtClient_Client = new Client(Client.getIPv4Address("eth0"),BtClient_ShareTorrent); /**3.连接tracker服务器做种或下载*/ BtClient_Client.share(); if (ClientState.ERROR.equals(BtClient_Client.getState())) { System.exit(1); } } catch (Exception e) { Log.e(TAG, "Fatal error: "+ e.getMessage(), e); System.exit(2); } return BtClient_Client; }
2.在第2步,初始化客户端时,首先注册一个ConnectionHandler连接通道,等待其他peers来连接,这样就相当于自己变成一个服务端(故此既是客户端也是服务端)
/**提供其他peers连接交互的通道*/ this.service = new ConnectionHandler(this.torrent, id, address); this.service.register(this);
再注册announce的通道负责去请求Tracker服务器
this.announce = new Announce(this.torrent, this.self); this.announce.register(this);
/** * Initialize the BitTorrent client. * * @param address The address to bind to. * @param torrent The torrent to download and share. */ public Client(InetAddress address, SharedTorrent torrent) throws UnknownHostException, IOException { this.torrent = torrent; this.state = ClientState.WAITING; String id = Client.BITTORRENT_ID_PREFIX + UUID.randomUUID() .toString().split("-")[4]; // Initialize the incoming connection handler and register ourselves to // it. /**提供其他peers连接交互的通道*/ this.service = new ConnectionHandler(this.torrent, id, address); this.service.register(this); this.self = new Peer( this.service.getSocketAddress() .getAddress().getHostAddress(), (short)this.service.getSocketAddress().getPort(), ByteBuffer.wrap(id.getBytes(Torrent.BYTE_ENCODING))); // Initialize the announce request thread, and register ourselves to it // as well. this.announce = new Announce(this.torrent, this.self); this.announce.register(this); logger.info("BitTorrent client [{}] for {} started and " + "listening at {}:{}...", new Object[] { this.self.getShortHexPeerId(), this.torrent.getName(), this.self.getIp(), this.self.getPort() }); // this.peers = new ConcurrentHashMap<String, SharingPeer>(); this.connected = new ConcurrentHashMap<String, SharingPeer>(); this.random = new Random(System.currentTimeMillis()); }
客户端初始化完成后,调用share()启动Client线程服务,在线程run()方法中,继续启动刚注册的几个线程
/**3.连接tracker服务器做种或下载*/ BtClient_Client.share();
具体代码请看到Client.java里的run()方法
3.第三步,得到tracker服务器的响应,在代码com.turn.ttorrent.announce.HTTPTrackerClient.java中line:106行
HttpURLConnection conn = null; InputStream in = null; try { //连接tracker服务器,成功返回200状态码 conn = (HttpURLConnection)target.openConnection(); in = conn.getInputStream(); } catch (IOException ioe) { if (conn != null) { in = conn.getErrorStream(); } } // At this point if the input stream is null it means we have neither a // response body nor an error stream from the server. No point in going // any further. if (in == null) { throw new AnnounceException("No response or unreachable tracker!"); } try { //从流中得到tracker返回的消息 ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(in); // Parse and handle the response HTTPTrackerMessage message = HTTPTrackerMessage.parse(ByteBuffer.wrap(baos.toByteArray())); this.handleTrackerAnnounceResponse(message, inhibitEvents); } catch (IOException ioe) { throw new AnnounceException("Error reading tracker response!", ioe); } catch (MessageValidationException mve) { throw new AnnounceException("Tracker message violates expected " + "protocol (" + mve.getMessage() + ")", mve); } finally { // Make sure we close everything down at the end to avoid resource // leaks. try { in.close(); } catch (IOException ioe) { logger.warn("Problem ensuring error stream closed!", ioe); } // This means trying to close the error stream as well. InputStream err = conn.getErrorStream(); if (err != null) { try { err.close(); } catch (IOException ioe) { logger.warn("Problem ensuring error stream closed!", ioe); } } }
这个http的请求格式如:
192.168.21.154是android机子的ip,也就是做种者的ip,tracker服务器我是在192.168.21.91上搭的,也就是我电脑的ip,上面图片就是tracker回显出的请求信息
在info_hash后的参数全部都是封装在这个target对象中
conn = (HttpURLConnection)target.openConnection();
之后再管道流中得到一个tracker响应的被bencoding编码过的数据,再解析出peers列表
//从流中得到tracker返回的消息 ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(in); // Parse and handle the response HTTPTrackerMessage message = HTTPTrackerMessage.parse(ByteBuffer.wrap(baos.toByteArray())); this.handleTrackerAnnounceResponse(message, inhibitEvents);
此时是做种,所有tracker上的信息之后seeder的信息,即做种者的信息
4、5、6:之后,就是peers连接tracker得到peers列表,里面包含peers自己和seeder的信息,与seeder建立对等连接,交换数据
下面是对等连接的分析:
待续。。。O(∩_∩)O~