ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Enabling Peer-to-Peer BitTorrent Downloads with Azureus

by Jacobus Steenkamp
06/22/2007

The type of traffic distribution on the Internet today is quite different from the type you might have encountered only a few years ago. In the past, the vast majority of internet bandwidth was used to transfer character streams (in most cases HTML) over either HTTP or HTTPS. This trend has changed over the past few years, with a great deal of bandwidth (33 to 50 percent by some estimates) now being used to distribute large files over peer-to-peer connections. BitTorrent is one of the more popular protocols being used for peer-to-peer file transfers, and enabling your Java applications to use this protocol has never been easier.

Peer-to-peer networks rely primarily on the bandwidth and hardware of the participants in the network rather than on a relatively small set of centralized servers. It is, therefore, much cheaper in terms of bandwidth and energy costs for the content provider to distribute large files using a peer-to-peer network rather than through the traditional client-server approach. There are quite a few examples in the industry where peer-to-peer networking has already been taken advantage of:

  • Blizzard's World of Warcraft game uses the BitTorrent protocol to send game updates to clients
  • The BitTorrent protocol is often used to distribute free and open source software. Open Office and popular Linux Distributions often offer the option of downloading their software using BitTorrent.
  • The BBC has recently announced that it will be making hundreds of episodes available over peer-to-peer file sharing networks. By opting to use a peer-to-peer paradigm to distribute its content, the BBC can reach a large audience without the need to invest vast amounts of money in building a server infrastructure.

The BitTorrent Protocol

The BitTorrent Protocol, which was designed and first implemented by Bram Cohen in 2001, is arguably the most popular and efficient peer-to-peer protocol currently in use.

To start sharing a file (or set of files) using BitTorrent the first peer, or initial seeder, creates a torrent file that contains all the metadata information required by clients to start downloading the shared file. This typically includes the name of the shared file (or files), the number of pieces the file has been broken down into, the checksum of each of the pieces, and the location of the tracker server which serves as a central point that coordinates all the connected peers. Unlike the rest of the traffic in a BitTorrent peer group (or swarm), communication to the tracker server is usually performed over HTTP.

Given a torrent file, a BitTorrent client would typically start off by connecting to the tracker server and getting the details of all other peers on the network. It would then start requesting pieces of the shared file from the rest of the swarm and use the checksum values in the torrent file to validate the received data. This BitTorrent process is very nicely illustrated on Wikipedia.

Azureus

Due to the openness of the BitTorrent protocol, numerous compatible BitTorrent clients have been implemented in a variety of programming languages and computing platforms. Out of all the options out there Azureus, which is implemented using Java and SWT, has proven itself to be one of the more popular and feature rich clients available. In fact, Azureus is the second most downloaded application on the Alltime Top Downloads list on SourceForge. One can argue that Azureus's popularity probably makes it one of the most successfulconsumer targetted Java desktop applications in the world.

In addition to being a great BitTorrent client, Azureus also contains functionality to create torrent files, set up a tracker server and an initial seeder. In the rest of the article we will be looking at how you can leverage these features for use in your own applications and take advantage of the cost benefits that peer-to-peer file distribution offers.

Getting Started with the Azureus API: A Simple Torrent File Downloader

In this section, we are going to implement a simple command-line application based on the Azureus API (or engine) to download a data file using the BitTorrent protocol. The URL of the torrent file will be passed in at the command line.

public class SimpleStandaloneDownloader {
  ...
  private static AzureusCore core;
  ...
  public static void main(String[] args) throws Exception{
    
    //Set the default root directory for the azureus engine.
    //If not set, it defaults to the user's home directory.
    System.setProperty("azureus.config.path", "run-environment/az-config");
    ...
    String url = null;
        ...      
        url = args[0];
    ...
    core = AzureusCoreFactory.create();
    core.start();
    ...
    System.out.println("Attempting to download torrent at : " + url);
    
    File downloadedTorrentFile = downloadTorrentFile(new URL(url));
    
    System.out.println("Completed download of : " + url);
    System.out.println("File stored as : " + downloadedTorrentFile.getAbsolutePath());
    
    File downloadDirectory = new File("downloads"); //Destination directory
    if(downloadDirectory.exists() == false) downloadDirectory.mkdir();
    
    //Start the download of the torrent 
    GlobalManager globalManager = core.getGlobalManager();
    DownloadManager manager = globalManager.addDownloadManager(downloadedTorrentFile.getAbsolutePath(),
                                                               downloadDirectory.getAbsolutePath());

    DownloadManagerListener listener = new DownloadStateListener();
    manager.addListener(listener);    
    globalManager.startAllDownloads();
    
  }
}

The singleton AzureusCore instance is the central axis on which the whole Azureus API revolves. After creating it (using the AzureusCoreFactory) and starting it, you are ready to start using its functionality. It should be noted that AzureusCore spawns its own Threads internally and generally runs asynchronously to the rest of the application.

After having downloaded the torrent file from the passed in URL using the downloadTorrentFile() method, the torrent is submitted to Azureus's GlobalManager instance, which is responsible for managing downloads. The DownloadManager that gets returned by the addDownloadManager() method can be used to retrieve a wealth of statistics on the download, including the data send rate and number of connected peers. In this example we have registered a DownloadManagerListener instance (implemented by the DownloadStateListener class) to track when the torrent data file has started downloading and to print out the completed percentage to the command line.

private static class DownloadStateListener implements DownloadManagerListener{
  ...
    public void stateChanged(DownloadManager manager, int state) {
      switch(state){
        ...
        case DownloadManager.STATE_DOWNLOADING :
          System.out.println("Downloading....");
          //Start a new daemon thread periodically check 
          //the progress of the upload and print it out 
          //to the command line
          Runnable checkAndPrintProgress = new Runnable(){
           
            public void run(){
              try{
                boolean downloadCompleted = false;
                while(!downloadCompleted){
                  AzureusCore core = AzureusCoreFactory.getSingleton();
                  List<DownloadManager> managers = core.getGlobalManager().getDownloadManagers();

                  //There is only one in the queue.
                  DownloadManager man = managers.get(0);
                  System.out.println("Download is " + 
                             (man.getStats().getCompleted() / 10.0) + 
                             " % complete");
                  downloadCompleted = man.isDownloadComplete(true);
                  //Check every 10 seconds on the progress
                  Thread.sleep(10000);
                }
              }catch(Exception e){
                throw new RuntimeException(e);
              }

            }
          };
          
          Thread progressChecker = new Thread(checkAndPrintProgress);
          progressChecker.setDaemon(true);
          progressChecker.start();
        break;
        ...
      }
    }
    
    public void downloadComplete(DownloadManager manager) {
      System.out.println("Download Completed - Exiting.....");
      AzureusCore core = AzureusCoreFactory.getSingleton();
      try{
        core.requestStop();
      }catch(AzureusCoreException aze){
        System.out.println("Could not end Azureus session gracefully - " +
                           "forcing exit.....");
        core.stop();
      }
    }
    ..
  }
}

Pages: 1, 2, 3, 4

Next Pagearrow