** Client.cpp
**   Create:2010.12.28
**  Author:gong
**
**
**/


#include "Client.h"
//#include "configuration.h"
#include "common.h"
#include <conio.h>
#include <ctype.h>
static char const* state_str[] =
 {"checking (q)", "checking", "dl metadata"
 , "downloading", "finished", "seeding", "allocating", "checking (r)"};
int main(int argc, char* argv[])
{
#if BOOST_VERSION < 103400
 using boost::filesystem::no_check;
 path::default_name_check(no_check);
#endif
 if (argc == 1)
 {//如果参数不够,输出格式说明
  fprintf(stderr, "usage: client_test [OPTIONS] [TORRENT|MAGNETURL]\n\n"
   "OPTIONS:\n"
   "  -f <log file>         logs all events to the given file\n"   //日志文件
   "  -o <limit>            limits the number of simultaneous\n"  //限制tcp连接的数量
   "                        half-open TCP connections to the\n"
   "                        given number.\n"
   "  -p <port>             sets the listen port\n"  //设置监听端口
   "  -r <ratio>            sets the preferred share ratio\n"  //设置什么比率
   "  -d <rate>             limits the download rate\n"  //限制下载速度
   "  -u <rate>             limits the upload rate\n"  //限制上传速度
   "  -S <limit>            limits the upload slots\n"  //
   "  -a <mode>             sets the allocation mode. [compact|full]\n" //设置分配模式
   "  -s <path>             sets the save path for downloads\n" //设置文件保存的路径
   "  -U <rate>             sets per-torrent upload rate\n"      //设置种子上传的速度
   "  -D <rate>             sets per-torrent download rate\n"  //设置种子下载的速度
   "  -m <path>             sets the .torrent monitor directory\n"  //设置种子监视的目录
   "  -b <IP>               sets IP of the interface to bind the\n"  //设置IP端口绑定
   "                        listen socket to\n"
   "  -w <seconds>          sets the retry time for failed web seeds\n"  //设置重试的等待时间
   "  -t <seconds>          sets the scan interval of the monitor dir\n"   //设置对监视目录浏览间隔的时间
   "  -x <file>             loads an emule IP-filter file\n"      //加载一个电驴IP过滤文件
   "  -c <limit>            sets the max number of connections\n"   //设置连接的最大数量
   "  -T <limit>            sets the max number of connections per torrent\n"  //设置每个种子文件连接的最大数目
   "  -C <limit>            sets the max cache size. Specified in 16kB blocks\n" //设置缓冲区大小
   "  -F <seconds>          sets the UI refresh rate. This is the number of\n"    //设置UI刷新速率
   "                        seconds between screen refreshes.\n"
   "  -n                    announce to trackers in all tiers\n"
   "  -h                    allow multiple connections from the same IP\n" //允许来自同一个IP的多重连接
   "  -A <num pieces>       allowed pieces set size\n" //设置每一片片的大小
   "  -R <num blocks>       number of blocks per read cache line\n"  //设置每读一行的块数量
   "  -O                    Disallow disk job reordering\n"  //不允许光盘运行
   "  "
   "\n\n"
   "TORRENT is a path to a .torrent file\n"
   "MAGNETURL is a magnet: url\n")
   ;
  return 0;
 }

 proxy_settings ps;
//设置session的属性
 using namespace libtorrent;
 session_settings settings;
 handles_t handles; //存储libtorrent句柄的容器
 settings.user_agent = "client_test/" LIBTORRENT_VERSION;
 settings.auto_upload_slots_rate_based = true;
 //settings.announce_to_all_trackers = true;
 settings.optimize_hashing_for_speed = false;
 settings.disk_cache_algorithm = session_settings::largest_contiguous;
 int refresh_delay = 1;
 std::deque<std::string> events;
 ptime next_dir_scan = time_now();
 // the string is the filename of the .torrent file, but only if
 // it was added through the directory monitor. It is used to
 // be able to remove torrents that were added via the directory
 // monitor when they're not in the directory anymore.
 //打开一个session
 session ses(fingerprint("LT", LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0)
  , session::start_default_features | session::add_default_plugins
  , alert::all_categories
   & ~(alert::dht_notification
   + alert::progress_notification
   + alert::debug_notification
   + alert::stats_notification));
 std::vector<char> in;
 if (load_file(".ses_state", in) == 0)
 {
  lazy_entry e;
  if (lazy_bdecode(&in[0], &in[0] + in.size(), e) == 0)
   ses.load_state(e);
 }
#ifndef TORRENT_DISABLE_DHT
 settings.use_dht_as_fallback = false;
 ses.add_dht_router(std::make_pair(
   std::string("router.bittorrent.com"), 6881));
 ses.add_dht_router(std::make_pair(
   std::string("router.utorrent.com"), 6881));
 ses.add_dht_router(std::make_pair(
   std::string("router.bitcomet.com"), 6881));
 ses.start_dht();
#endif
#ifndef TORRENT_DISABLE_GEO_IP
 ses.load_asnum_db("GeoIPASNum.dat");
 ses.load_country_db("GeoIP.dat");
#endif
 // 加载命令行给出的种子文件
 for (int i = 1; i < argc; ++i)
 {
  if (argv[i][0] != '-')
  {
   // interpret this as a torrent
   // first see if this is a torrentless download
   if (std::strstr("magnet:", argv[i]) == argv[i])
   {
    add_torrent_params p;
    p.save_path = save_path;
    p.storage_mode = (storage_mode_t)allocation_mode;
    printf("adding MANGET link: %s\n", argv[i]);
    error_code ec;
    torrent_handle h = add_magnet_uri(ses, argv[i], p, ec);
    if (ec)
    {
     fprintf(stderr, "%s\n", ec.message().c_str());
     continue;
    }
    handles.insert(std::pair<const std::string, torrent_handle>(std::string(), h));
    h.set_max_connections(max_connections_per_torrent);
    h.set_max_uploads(-1);
    h.set_ratio(preferred_ratio);
    h.set_upload_limit(torrent_upload_limit);
    h.set_download_limit(torrent_download_limit);
    continue;
   }
   // match it against the <hash>@<tracker> format
   if (strlen(argv[i]) > 45
    && is_hex(argv[i], 40)
    && string_begins_no_case(argv[i] + 40, "@http"))
   {
    sha1_hash info_hash;
    from_hex(argv[i], 40, (char*)&info_hash[0]);
    add_torrent_params p;
    p.tracker_url = argv[i] + 41;
    p.info_hash = info_hash;
    p.save_path = save_path;
    p.storage_mode = (storage_mode_t)allocation_mode;
    p.paused = true;
    p.duplicate_is_error = false;
    p.auto_managed = true;
    error_code ec;
    torrent_handle h = ses.add_torrent(p, ec);
    if (ec)
    {
     fprintf(stderr, "%s\n", ec.message().c_str());
     continue;
    }
    handles.insert(std::pair<const std::string, torrent_handle>(std::string(), h));
    h.set_max_connections(max_connections_per_torrent);
    h.set_max_uploads(-1);
    h.set_ratio(preferred_ratio);
    h.set_upload_limit(torrent_upload_limit);
    h.set_download_limit(torrent_download_limit);
    continue;
   }
   // if it's a torrent file, open it as usual
   add_torrent(ses, handles, argv[i], preferred_ratio
    , allocation_mode, save_path, false
    , torrent_upload_limit, torrent_download_limit);
   continue;
  }
  // if there's a flag but no argument following, ignore it
  if (argc == i) continue;
  char const* arg = argv[i+1];
  //加载参数
  switch (argv[i][1])
  {
   case 'f': g_log_file = fopen(arg, "w+"); break;
   case 'o': ses.set_max_half_open_connections(atoi(arg)); break;
   case 'h': settings.allow_multiple_connections_per_ip = true; --i; break;
   case 'p': listen_port = atoi(arg); break;
   case 'r':
    preferred_ratio = atoi(arg);
    if (preferred_ratio != 0 && preferred_ratio < 1.f) preferred_ratio = 1.f;
    break;
   case 'n': settings.announce_to_all_tiers = true; --i; break;
   case 'd': ses.set_download_rate_limit(atoi(arg) * 1000); break;
   case 'u': ses.set_upload_rate_limit(atoi(arg) * 1000); break;
   case 'S': ses.set_max_uploads(atoi(arg)); break;
   case 'a':
    if (strcmp(arg, "allocate") == 0) allocation_mode = storage_mode_allocate;
    if (strcmp(arg, "compact") == 0) allocation_mode = storage_mode_compact;
    break;
   case 's': save_path = arg; break;
   case 'U': torrent_upload_limit = atoi(arg) * 1000; break;
   case 'D': torrent_download_limit = atoi(arg) * 1000; break;
   case 'm': monitor_dir = arg; break;
   case 'b': bind_to_interface = arg; break;
   case 'w': settings.urlseed_wait_retry = atoi(arg); break;
   case 't': poll_interval = atoi(arg); break;
   case 'F': refresh_delay = atoi(arg); break;
   case 'x':
    {
     /*
     std::ifstream in(arg);
     ip_filter filter;
     while (in.good())
     {
      char line[300];
      in.getline(line, 300);
      int len = in.gcount();
      if (len <= 0) continue;
      if (line[0] == '#') continue;
      int a, b, c, d;
      char dummy;
      std::stringstream ln(line);
      ln >> a >> dummy >> b >> dummy >> c >> dummy >> d >> dummy;
      address_v4 start((a << 24) + (b << 16) + (c << 8) + d);
      ln >> a >> dummy >> b >> dummy >> c >> dummy >> d;
      address_v4 last((a << 24) + (b << 16) + (c << 8) + d);
      int flags;
      ln >> flags;
      if (flags <= 127) flags = ip_filter::blocked;
      else flags = 0;
      if (ln.fail()) break;
      filter.add_rule(start, last, flags);
     }
     ses.set_ip_filter(filter);
     */
    }
    break;
   case 'c': ses.set_max_connections(atoi(arg)); break;
   case 'T': max_connections_per_torrent = atoi(arg); break;
   case 'C':
    settings.cache_size = atoi(arg);
    settings.use_read_cache = settings.cache_size > 0;
    settings.cache_buffer_chunk_size = settings.cache_size / 100;
    break;
   case 'A': settings.allowed_fast_set_size = atoi(arg); break;
   case 'R': settings.read_cache_line_size = atoi(arg); break;
   case 'O': settings.allow_reordered_disk_operations = false; --i; break;
  }
  ++i; // skip the argument
 }
 ses.listen_on(std::make_pair(listen_port, listen_port + 10)
  , bind_to_interface.c_str());
 ses.set_settings(settings);
//分析这一块的东西
 // main loop
 std::vector<peer_info> peers;
 std::vector<partial_piece_info> queue;
 for (;;)
 {
  char c;
  while (sleep_and_input(&c, refresh_delay))
  {
   if (c == 27)
   {
    // escape code, read another character
#ifdef _WIN32
    c = _getch();
#else
    c = getc(stdin);
#endif
    if (c != '[') break;
#ifdef _WIN32
    c = _getch();
#else
    c = getc(stdin);
#endif
    if (c == 65) //c==A
    {
     // arrow up
     --active_torrent;
     if (active_torrent < 0) active_torrent = 0;
    }
    else if (c == 66) //c==B
    {
     // arrow down
     ++active_torrent;
     if (active_torrent >= handles.size()) active_torrent = handles.size() - 1;
    }
   }
   if (c == ' ')
   {
    if (ses.is_paused()) ses.resume();
    else ses.pause();
   }
   if (c == 'm')
   {
    printf("saving peers for torrents\n");
    std::vector<peer_list_entry> peers;
    for (handles_t::iterator i = handles.begin();
     i != handles.end(); ++i)
    {
     i->second.get_full_peer_list(peers);
     FILE* f = fopen(("peers_" + i->second.name()).c_str(), "w+");
     if (!f) break;
     for (std::vector<peer_list_entry>::iterator k = peers.begin()
      , end(peers.end()); k != end; ++k)
     {
      fprintf(f, "%s\t%d\n", print_address(k->ip.address()).c_str()
#ifndef TORRENT_DISABLE_GEO_IP
       , ses.as_for_ip(k->ip.address())
#else
       , 0
#endif
       );
     }
    }
   }
   if (c == 'q') break;
   if (c == 'j')
   {
    //强制重新检查
    torrent_handle h = get_active_torrent(handles);
    if (h.is_valid()) h.force_recheck();
   }
   if (c == 'r')
   {
    torrent_handle h = get_active_torrent(handles);
    if (h.is_valid()) h.force_reannounce();
   }
   if (c == 's')
   {
    torrent_handle h = get_active_torrent(handles);
    if (h.is_valid()) h.set_sequential_download(!h.is_sequential_download());
   }
   if (c == 'o')
   {
    torrent_handle h = get_active_torrent(handles);
    if (h.is_valid())
    {
     int num_pieces = h.get_torrent_info().num_pieces();
     if (num_pieces > 300) num_pieces = 300;
     for (int i = 0; i < num_pieces; ++i)
     {
      h.set_piece_deadline(i, (i+5) * 1000, torrent_handle::alert_when_available);
     }
    }
   }
   if (c == 'v')
   {
    torrent_handle h = get_active_torrent(handles);
    if (h.is_valid()) h.scrape_tracker();
   }
         //切换暂停
   if (c == 'p')
   {
    torrent_handle h = get_active_torrent(handles);
    if (h.is_valid())
    {
     if (!h.is_auto_managed() && h.is_paused())
     {
      h.auto_managed(true);
     }
     else
     {
      h.auto_managed(false);
      h.pause();
     }
     // the alert handler for save_resume_data_alert
     // will save it to disk
     h.save_resume_data();
    }
   }
   //清楚错误
   if (c == 'c')
   {
    torrent_handle h = get_active_torrent(handles);
    if (h.is_valid()) h.clear_error();
   }
   // toggle displays
   if (c == 't') print_trackers = !print_trackers;
   if (c == 'i') print_peers = !print_peers;
   if (c == 'l') print_log = !print_log;
   if (c == 'd') print_downloads = !print_downloads;
   if (c == 'f') print_file_progress = !print_file_progress;
   if (c == 'h') show_pad_files = !show_pad_files;
   if (c == 'a') print_piece_bar = !print_piece_bar;
   if (c == 'g') show_dht_status = !show_dht_status;
   // toggle columns
   if (c == '1') print_ip = !print_ip;
   if (c == '2') print_as = !print_as;
   if (c == '3') print_timers = !print_timers;
   if (c == '4') print_block = !print_block;
   if (c == '5') print_peer_rate = !print_peer_rate;
   if (c == '6') print_fails = !print_fails;
   if (c == '7') print_send_bufs = !print_send_bufs;
  }
  if (c == 'q') break;
  int terminal_width = 80;
#ifndef _WIN32
  {
   winsize size;
   ioctl(STDOUT_FILENO, TIOCGWINSZ, (char*)&size);
   terminal_width = size.ws_col;
  }
#endif
  // loop through the alert queue to see if anything has happened.
  std::auto_ptr<alert> a;
  a = ses.pop_alert();
  std::string now = time_now_string();
  while (a.get())
  {
   std::string event_string;
   ::print_alert(a.get(), event_string);
   ::handle_alert(ses, a.get(), handles);
   events.push_back(event_string);
   if (events.size() >= 20) events.pop_front();
   a = ses.pop_alert();
  }
  session_status sess_stat = ses.status();  //返回事务的带宽统计与状态
  std::string out;
  out = "[q] quit [i] toggle peers [d] toggle downloading pieces [p] toggle paused "
   "[a] toggle piece bar [s] toggle download sequential [f] toggle files "
   "[j] force recheck [space] toggle session pause [c] clear error [v] scrape [g] show DHT\n"
   "[1] toggle IP [2] toggle AS [3] toggle timers [4] toggle block progress "
   "[5] toggle peer rate [6] toggle failures [7] toggle send buffers\n";
  char str[500];
  int torrent_index = 0;
  torrent_handle active_handle;
  for (handles_t::iterator i = handles.begin();
   i != handles.end(); ++torrent_index)
  {
    torrent_handle& h = i->second;
    //检验现在种子的合法性,如果是不合法的句柄则删除句柄
    if (!h.is_valid())
    {//清除种子
      handles.erase(i++);
      continue;
    }
    else
    {
     ++i;
    }
#ifdef ANSI_TERMINAL_COLORS
   char const* term = "\x1b[0m";
#else
   char const* term = "";
#endif
    if (active_torrent == torrent_index)
    {
     term = "\x1b[0m\x1b[7m";
     out += esc("7");
     out += "*";
    }
    else
    {
     out += " ";
    }
//返回任务在队列中的任务
    int queue_pos = h.queue_position();
    if (queue_pos == -1) out += "-  ";
    else
    {
     snprintf(str, sizeof(str), "%-3d", queue_pos);
     out += str;
    }
    if (h.is_paused()) out += esc("34");
       else out += esc("37");
//种子名字
   std::string name = h.name();
   if (name.size() > 40) name.resize(40);
   snprintf(str, sizeof(str), "%-40s %s ", name.c_str(), term);
   out += str;
   torrent_status s = h.status();
   bool paused = h.is_paused();
   bool auto_managed = h.is_auto_managed();
   bool sequential_download = h.is_sequential_download();
   if (!s.error.empty())
   {
    out += esc("31");
    out += "error ";
    out += s.error;
    out += esc("0");
    out += "\n";
    continue;
   }
   int seeds = 0;
   int downloaders = 0;
   if (s.num_complete >= 0) seeds = s.num_complete;  //供下载的对等点的所有数目
   else seeds = s.list_seeds;
   if (s.num_incomplete >= 0) downloaders = s.num_incomplete; //仍然在下载任务的对等点数目
   else downloaders = s.list_peers - s.list_seeds;  //对等点的所有数目(包括种子源),对等点链表中的供下载源的数目
   if (s.state != torrent_status::queued_for_checking && s.state != torrent_status::checking_files)
   {
    snprintf(str, sizeof(str), "%-13s down: (%s%s%s) up: %s%s%s (%s%s%s) swarm: %4d:%4d"
     "  bw queue: (%d|%d) all-time (Rx: %s%s%s Tx: %s%s%s) seed rank: %x%s\n"
     , (paused && !auto_managed)?"paused":(paused && auto_managed)?"queued":state_str[s.state]
     , esc("32"), add_suffix(s.total_download).c_str(), term    //下载的总字节数
     , esc("31"), add_suffix(s.upload_rate, "/s").c_str(), term //上传速率
     , esc("31"), add_suffix(s.total_upload).c_str(), term  //上传的总字节数
     , downloaders, seeds  
     , s.up_bandwidth_queue, s.down_bandwidth_queue
     //计算上传或者下载的承载字节计数。它们将保存在或者从恢复数据中重装来碰到所有事务。
     , esc("32"), add_suffix(s.all_time_download).c_str(), term
     , esc("31"), add_suffix(s.all_time_upload).c_str(), term
     , s.seed_rank, esc("0"));   //任务得到种子的重要级别,它决定那个任务将得到种子或者放在队列中。
    out += str;
    if (torrent_index != active_torrent && s.state == torrent_status::seeding) continue;
    char const* progress_bar_color = "33"; // yellow
    if (s.state == torrent_status::downloading_metadata)
    {
     progress_bar_color = "35"; // magenta
    }
    else if (s.current_tracker.empty())
    {
     progress_bar_color = "31"; // red
    }
    else if (sess_stat.has_incoming_connections)
    {
     progress_bar_color = "32"; // green
    }
    snprintf(str, sizeof(str), "     %-10s: %s%-11"PRId64"%s Bytes %6.2f%% %s\n"
     , sequential_download?"sequential":"progress"
     , esc("32"), s.total_done, esc("0")  //下载的文件的所有字节数。
     , s.progress_ppm / 10000.f
     , progress_bar(s.progress_ppm / 1000, terminal_width - 43, progress_bar_color).c_str());
    out += str;
   }
   else
   {
    snprintf(str, sizeof(str), "%-13s %s\n"
     , state_str[s.state]
     , progress_bar(s.progress_ppm / 1000, terminal_width - 43 - 20, "35").c_str());
    out += str;
   }
   if (print_piece_bar && s.progress_ppm < 1000000 && s.progress > 0)
   {
    out += "     ";
    out += piece_bar(s.pieces, terminal_width - 7);
    out += "\n";
   }
   if (s.state != torrent_status::queued_for_checking && s.state != torrent_status::checking_files)
   {
    boost::posix_time::time_duration t = s.next_announce;  //任务向服务器通知自已的情况的时间。并且通知的间隔是服务器希望任务等待并下次再次通知的时间。
    snprintf(str, sizeof(str)
     , "     peers: %s%d%s (%s%d%s) seeds: %s%d%s distributed copies: %s%4.2f%s "
     "sparse regions: %d download: %s%s%s next announce: %s%02d:%02d:%02d%s "
     "tracker: %s%s%s\n"
     , esc("37"), s.num_peers, esc("0")  //任务现在连接的对等点的数目。
     , esc("37"), s.connect_candidates, esc("0")  //正准备连接的任务的对等点链表中的对等点数目。
     , esc("37"), s.num_seeds, esc("0")   //客户端连接的正在提供下载源的对等点的数目。
     , esc("37"), s.distributed_copies, esc("0")  //任务的分发拷贝的数目。
     , s.sparse_regions
     , esc("32"), add_suffix(s.download_rate, "/s").c_str(), esc("0")
     , esc("37"), t.hours(), t.minutes(), t.seconds(), esc("0")
     , esc("36"), s.current_tracker.c_str(), esc("0"));  //最近的服务器的网址。
    out += str;
   }
   if (torrent_index != active_torrent) continue;
   active_handle = h;
  }
  cache_status cs = ses.get_cache_status();
  if (cs.blocks_read < 1) cs.blocks_read = 1;
  if (cs.blocks_written < 1) cs.blocks_written = 1;
//========================================================================================//
  snprintf(str, sizeof(str), "==== conns: %d down: %s%s%s (%s%s%s) up: %s%s%s (%s%s%s) "
   "tcp/ip: %s%s%s %s%s%s DHT: %s%s%s %s%s%s tracker: %s%s%s %s%s%s ====\n"
   , sess_stat.num_peers   //事务中含有的对等点连接的总数
   
   , esc("32"), add_suffix(sess_stat.download_rate, "/s").c_str(), esc("0")
   , esc("32"), add_suffix(sess_stat.total_download).c_str(), esc("0")  //从所有任务中得到的下载字节
   
   , esc("31"), add_suffix(sess_stat.upload_rate, "/s").c_str(), esc("0")
   , esc("31"), add_suffix(sess_stat.total_upload).c_str(), esc("0")//从所有任务中得到的所有上传字节
   
   , esc("32"), add_suffix(sess_stat.ip_overhead_download_rate, "/s").c_str(), esc("0")
   , esc("31"), add_suffix(sess_stat.ip_overhead_upload_rate, "/s").c_str(), esc("0")
   
   , esc("32"), add_suffix(sess_stat.dht_download_rate, "/s").c_str(), esc("0")
   , esc("31"), add_suffix(sess_stat.dht_upload_rate, "/s").c_str(), esc("0")
   
   , esc("32"), add_suffix(sess_stat.tracker_download_rate, "/s").c_str(), esc("0")
   , esc("31"), add_suffix(sess_stat.tracker_upload_rate, "/s").c_str(), esc("0"));
  out += str;
//======================================================================================
  snprintf(str, sizeof(str), "==== waste: %s fail: %s unchoked: %d / %d "
   "bw queues: %8d (%d) | %8d (%d) cache: w: %"PRId64"%% r: %"PRId64"%% size: %s (%s) / %s dq: %"PRId64" ===\n"
   , add_suffix(sess_stat.total_redundant_bytes).c_str()   //多收的字节数。
   , add_suffix(sess_stat.total_failed_bytes).c_str()  //下载下来了的数据但后来在hash-check失败了的数据。
   , sess_stat.num_unchoked, sess_stat.allowed_upload_slots
   , sess_stat.up_bandwidth_bytes_queue
   , sess_stat.up_bandwidth_queue
   , sess_stat.down_bandwidth_bytes_queue
   , sess_stat.down_bandwidth_queue
   , (cs.blocks_written - cs.writes) * 100 / cs.blocks_written
   , cs.blocks_read_hit * 100 / cs.blocks_read
   , add_suffix(cs.cache_size * 16 * 1024).c_str()
   , add_suffix(cs.read_cache_size * 16 * 1024).c_str()
   , add_suffix(cs.total_used_buffers * 16 * 1024).c_str()
   , cs.queued_bytes);
  out += str;
//==========================================================
  snprintf(str, sizeof(str), "==== optimistic unchoke: %d unchoke counter: %d ====\n"
   , sess_stat.optimistic_unchoke_counter, sess_stat.unchoke_counter);
  out += str;
#ifndef TORRENT_DISABLE_DHT
  if (show_dht_status)
  {
   snprintf(str, sizeof(str), "DHT nodes: %d DHT cached nodes: %d total DHT size: %"PRId64"\n"
    , sess_stat.dht_nodes, sess_stat.dht_node_cache, sess_stat.dht_global_nodes);
   out += str;
   for (std::vector<dht_lookup>::iterator i = sess_stat.active_requests.begin()
    , end(sess_stat.active_requests.end()); i != end; ++i)
   {
    snprintf(str, sizeof(str), "  %s %d (%d) ( timeouts %d responses %d)\n"
     , i->type, i->outstanding_requests, i->branch_factor, i->timeouts, i->responses);
    out += str;
   }
  }
#endif
  if (active_handle.is_valid())
  {
   torrent_handle h = active_handle;
   torrent_status s = h.status();
   if ((print_downloads && s.state != torrent_status::seeding)
    || print_peers)
    h.get_peer_info(peers);
   out += "====== ";
   out += h.name();
   out += " ======\n";
   if (print_peers && !peers.empty())
    print_peer_info(out, peers);
   if (print_trackers)
   {
    std::vector<announce_entry> tr = h.trackers();
    ptime now = time_now();
    for (std::vector<announce_entry>::iterator i = tr.begin()
     , end(tr.end()); i != end; ++i)
    {
     snprintf(str, sizeof(str), "%2d %-55s fails: %-3d %s %s\n"
      , i->tier, i->url.c_str(), i->fails, i->verified?"OK ":"-  "
      , i->updating?"updating"
       :!i->verified?""
       :to_string(total_seconds(i->next_announce - now), 8).c_str());
     out += str;
    }
   }
   if (print_downloads)
   {
    h.get_download_queue(queue);
    std::sort(queue.begin(), queue.end(), bind(&partial_piece_info::piece_index, _1)
     < bind(&partial_piece_info::piece_index, _2));
    std::vector<cached_piece_info> pieces;
    ses.get_cache_info(h.info_hash(), pieces);
    for (std::vector<partial_piece_info>::iterator i = queue.begin();
     i != queue.end(); ++i)
    {
     cached_piece_info* cp = 0;
     std::vector<cached_piece_info>::iterator cpi = std::find_if(pieces.begin(), pieces.end()
      , bind(&cached_piece_info::piece, _1) == i->piece_index);
     if (cpi != pieces.end()) cp = &*cpi;
     snprintf(str, sizeof(str), "%5d: [", i->piece_index);
     out += str;
     for (int j = 0; j < i->blocks_in_piece; ++j)
     {
      int index = peer_index(i->blocks[j].peer(), peers) % 36;
      char chr = '+';
      if (index >= 0)
       chr = (index < 10)?'0' + index:'A' + index - 10;
      char const* color = "";
#ifdef ANSI_TERMINAL_COLORS
      if (cp && cp->blocks[j]) color = esc("36;7");
      else if (i->blocks[j].bytes_progress > 0
       && i->blocks[j].state == block_info::requested)
      {
       if (i->blocks[j].num_peers > 1) color = esc("1;7");
       else color = esc("33;7");
       chr = '0' + (i->blocks[j].bytes_progress / float(i->blocks[j].block_size) * 10);
      }
      else if (i->blocks[j].state == block_info::finished) color = esc("32;7");
      else if (i->blocks[j].state == block_info::writing) color = esc("35;7");
      else if (i->blocks[j].state == block_info::requested) color = esc("0");
      else { color = esc("0"); chr = ' '; }
#else
      if (cp && cp->blocks[j]) chr = 'c';
      else if (i->blocks[j].state == block_info::finished) chr = '#';
      else if (i->blocks[j].state == block_info::writing) chr = '+';
      else if (i->blocks[j].state == block_info::requested) chr = '-';
      else chr = ' ';
#endif
      snprintf(str, sizeof(str), "%s%c", color, chr);
      out += str;
     }
#ifdef ANSI_TERMINAL_COLORS
     out += esc("0");
#endif
     char const* piece_state[4] = {"", " slow", " medium", " fast"};
     snprintf(str, sizeof(str), "]%s", piece_state[i->piece_state]);
     out += str;
     if (cp)
     {
      snprintf(str, sizeof(str), " %scache age: %-.1f"
       , i->piece_state > 0?"| ":""
       , total_milliseconds(time_now() - cp->last_use) / 1000.f);
      out += str;
     }
     out += "\n";
    }
    for (std::vector<cached_piece_info>::iterator i = pieces.begin()
     , end(pieces.end()); i != end; ++i)
    {
     if (i->kind != cached_piece_info::read_cache) continue;
     snprintf(str, sizeof(str), "%5d: [", i->piece);
     out += str;
     for (std::vector<bool>::iterator k = i->blocks.begin()
      , end(i->blocks.end()); k != end; ++k)
     {
      char const* color = "";
      char chr = ' ';
#ifdef ANSI_TERMINAL_COLORS
      color = *k?esc("33;7"):esc("0");
#else
      chr = *k?'#':' ';
#endif
      snprintf(str, sizeof(str), "%s%c", color, chr);
      out += str;
     }
#ifdef ANSI_TERMINAL_COLORS
     out += esc("0");
#endif
     snprintf(str, sizeof(str), "] cache age: %-.1f\n"
      , total_milliseconds(time_now() - i->last_use) / 1000.f);
     out += str;
    }
    out += "___________________________________\n";
   }
   if (print_file_progress
    && s.state != torrent_status::seeding
    && h.has_metadata())
   {
    std::vector<size_type> file_progress;
    h.file_progress(file_progress);
    torrent_info const& info = h.get_torrent_info();
    for (int i = 0; i < info.num_files(); ++i)
    {
     bool pad_file = info.file_at(i).pad_file;
     if (!show_pad_files && pad_file) continue;
     int progress = info.file_at(i).size > 0
      ?file_progress[i] * 1000 / info.file_at(i).size:1000;
     char const* color = (file_progress[i] == info.file_at(i).size)
      ?"32":"33";
     snprintf(str, sizeof(str), "%s %s %-5.2f%% %s %s%s\n",
      progress_bar(progress, 100, color).c_str()
      , pad_file?esc("34"):""
      , progress / 10.f
      , add_suffix(file_progress[i]).c_str()
      , info.file_at(i).path.leaf().c_str()
      , pad_file?esc("0"):"");
     out += str;
    }
    out += "___________________________________\n";
   }
  }
  if (print_log)
  {
   for (std::deque<std::string>::iterator i = events.begin();
    i != events.end(); ++i)
   {
    out += "\n";
    out += *i;
   }
  }
  clear_home();
  puts(out.c_str());
  if (!monitor_dir.empty()
   && next_dir_scan < time_now())
  {
   scan_dir(monitor_dir, ses, handles, preferred_ratio
    , allocation_mode, save_path, torrent_upload_limit
    , torrent_download_limit);
   next_dir_scan = time_now() + seconds(poll_interval);
  }
 }
//====================================================================================//
 // keep track of the number of resume data
 // alerts to wait for
 int num_resume_data = 0;
 ses.pause(); //断开所有连接
 for (handles_t::iterator i = handles.begin();
  i != handles.end(); ++i)
 {
  torrent_handle& h = i->second;
  if (!h.is_valid()) continue;  //查看是否任务被找到
  if (h.is_paused()) continue;  //检查任务是否是激活的
  if (!h.has_metadata()) continue; //如果任务有元数据,则返回true
  printf("saving resume data for %s\n", h.name().c_str());
  // save_resume_data will generate an alert when it's done
  h.save_resume_data(); //产生一个快速恢复的数据并且返回一个接入值。
  ++num_resume_data;
 }
 printf("waiting for resume data\n");
//==========================================================================================//
 while (num_resume_data > 0)
 {
  alert const* a = ses.wait_for_alert(seconds(30)); //等待警告
  if (a == 0)
  {
   printf(" aborting with %d outstanding "
    "torrents to save resume data for\n", num_resume_data);
   break;
  }
  std::auto_ptr<alert> holder = ses.pop_alert();  //告诉事务是否一些错误或者事情已经发生
  std::string log;
  ::print_alert(holder.get(), log);
  printf("%s\n", log.c_str());
  if (alert_cast<save_resume_data_failed_alert>(a))
  {
   --num_resume_data;
   continue;
  }
  save_resume_data_alert const* rd = alert_cast<save_resume_data_alert>(a);
  if (!rd) continue;
  --num_resume_data;
  if (!rd->resume_data) continue;
  torrent_handle h = rd->handle;
  std::vector<char> out;
  bencode(std::back_inserter(out), *rd->resume_data);
  save_file((h.save_path() / h.name()).string() + ".resume", out);
 }
 printf("saving session state\n");
 {
  entry session_state;
  ses.save_state(session_state);
  std::vector<char> out;
  bencode(std::back_inserter(out), session_state);
  save_file(".ses_state", out);
 }
 printf("closing session");
    system("pause");
 return 0;
}