connection.c
来自「elinks下lynx是最重要的二个文本浏览器, 在linux下非常实用, el」· C语言 代码 · 共 411 行
C
411 行
/* Internal "bittorrent" protocol implementation */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdlib.h>#include <string.h>#include "elinks.h"#include "cache/cache.h"#include "config/options.h"#include "main/timer.h"#include "network/connection.h"#include "network/progress.h"#include "network/socket.h"#include "protocol/bittorrent/bencoding.h"#include "protocol/bittorrent/bittorrent.h"#include "protocol/bittorrent/common.h"#include "protocol/bittorrent/connection.h"#include "protocol/bittorrent/tracker.h"#include "protocol/bittorrent/peerconnect.h"#include "protocol/bittorrent/peerwire.h"#include "protocol/bittorrent/piececache.h"#include "protocol/protocol.h"#include "protocol/uri.h"#include "session/download.h"#include "util/bitfield.h"#include "util/conv.h"#include "util/memory.h"#include "util/string.h"#include "util/time.h"/* ************************************************************************** *//* Peer selection and connection scheduling: *//* ************************************************************************** *//* Reschedule updating of the connection state. */static voidset_bittorrent_connection_timer(struct connection *conn){ struct bittorrent_connection *bittorrent = conn->info; milliseconds_T interval = sec_to_ms(get_opt_int("protocol.bittorrent.choke_interval")); install_timer(&bittorrent->timer, interval, (void (*)(void *)) update_bittorrent_connection_state, conn);}/* Sort the peers based on the stats rate, bubbaly style! */static voidsort_bittorrent_peer_connections(struct bittorrent_connection *bittorrent){ struct bittorrent_peer_connection *peer, *prev; while (1) { struct bittorrent_peer_connection *next; int resort = 0; prev = NULL; foreachsafe (peer, next, bittorrent->peers) { if (prev && prev->stats.download_rate < peer->stats.download_rate) { resort = 1; del_from_list(prev); add_at_pos(peer, prev); } prev = peer; } if (!resort) break; };#ifdef CONFIG_DEBUG prev = NULL; foreach (peer, bittorrent->peers) { assert(!prev || prev->stats.download_rate >= peer->stats.download_rate); prev = peer; }#endif}/* This is basically the choke period handler. */voidupdate_bittorrent_connection_state(struct connection *conn){ struct bittorrent_connection *bittorrent = conn->info; struct bittorrent_peer_connection *peer, *next_peer; int peer_conns, max_peer_conns; int min_uploads = get_opt_int("protocol.bittorrent.min_uploads"); int max_uploads = get_opt_int("protocol.bittorrent.max_uploads"); set_bittorrent_connection_timer(conn); set_connection_timeout(conn); peer_conns = list_size(&bittorrent->peers); max_peer_conns = get_opt_int("protocol.bittorrent.peerwire.connections"); /* First ``age'' the peer rates _before_ the sorting. */ foreach (peer, bittorrent->peers) update_bittorrent_peer_connection_stats(peer, 0, 0, 0); /* Sort the peers so that the best peers are at the list start. */ sort_bittorrent_peer_connections(bittorrent); /* Unchoke all the optimal peers. In good spirit, also unchoke all * uninterested peers until the maximum number of interested peers have * been unchoked. The rest is choked. */ foreachsafe (peer, next_peer, bittorrent->peers) { if (!peer->remote.handshake) continue; if (min_uploads < max_uploads) { if (peer->remote.choked) unchoke_bittorrent_peer(peer); peer->remote.choked = 0; /* Uninterested peers are not counted as uploads. */ if (peer->remote.interested) max_uploads--; } else { if (!peer->remote.choked) choke_bittorrent_peer(peer); peer->remote.choked = 1; } /* Can remove the peer so we use foreachsafe(). */ update_bittorrent_peer_connection_state(peer); } /* FIXME: Find peer(s) to optimistically unchoke. */ update_bittorrent_piece_cache_state(bittorrent); /* Close or open peers connections. */ if (peer_conns > max_peer_conns) { struct bittorrent_peer_connection *prev; foreachsafe (peer, prev, bittorrent->peers) { done_bittorrent_peer_connection(peer); if (--peer_conns <= max_peer_conns) break; } } else if (peer_conns < max_peer_conns) { struct bittorrent_peer *peer_info, *next_peer_info; foreachsafe (peer_info, next_peer_info, bittorrent->peer_pool) { enum bittorrent_state state; state = make_bittorrent_peer_connection(bittorrent, peer_info); if (state != BITTORRENT_STATE_OK) break; del_from_list(peer_info); mem_free(peer_info); if (++peer_conns >= max_peer_conns) break; } } assert(peer_conns <= max_peer_conns); /* Shrink the peer pool. */ if (!list_empty(bittorrent->peers)) { struct bittorrent_peer *peer_info, *next_peer_info; int pool_size = get_opt_int("protocol.bittorrent.peerwire.pool_size"); int pool_peers = 0; foreachsafe (peer_info, next_peer_info, bittorrent->peer_pool) { /* Unlimited. */ if (!pool_size) break; if (pool_peers < pool_size) { pool_peers++; continue; } del_from_list(peer_info); mem_free(peer_info); } }}static voidupdate_bittorrent_connection_upload(void *data){ struct bittorrent_connection *bittorrent = data; update_progress(&bittorrent->upload_progress, bittorrent->uploaded, bittorrent->downloaded, bittorrent->uploaded);}voidupdate_bittorrent_connection_stats(struct bittorrent_connection *bittorrent, off_t downloaded, off_t uploaded, off_t received){ struct bittorrent_meta *meta = &bittorrent->meta; if (bittorrent->conn->est_length == -1) { off_t length = (off_t) (meta->pieces - 1) * meta->piece_length + meta->last_piece_length; bittorrent->conn->est_length = length; bittorrent->left = length; start_update_progress(&bittorrent->upload_progress, update_bittorrent_connection_upload, bittorrent); } if (bittorrent->upload_progress.timer == TIMER_ID_UNDEF && bittorrent->uploaded) update_bittorrent_connection_upload(bittorrent); bittorrent->conn->received += received; bittorrent->conn->from += downloaded; if (downloaded > 0) bittorrent->downloaded += downloaded; bittorrent->uploaded += uploaded; bittorrent->left -= downloaded; if (!bittorrent->downloaded) return; bittorrent->sharing_rate = (double) bittorrent->uploaded / bittorrent->downloaded;}/* ************************************************************************** *//* The ``main'' BitTorrent connection setup: *//* ************************************************************************** *//* Callback which is attached to the ELinks connection and invoked when the * connection is shutdown. */static voiddone_bittorrent_connection(struct connection *conn){ struct bittorrent_connection *bittorrent = conn->info; struct bittorrent_peer_connection *peer, *next; assert(bittorrent); /* We don't want the tracker to see the fetch. */ if (bittorrent->fetch) done_bittorrent_fetch(&bittorrent->fetch); foreachsafe (peer, next, bittorrent->peers) done_bittorrent_peer_connection(peer); done_bittorrent_tracker_connection(conn); done_bittorrent_listening_socket(conn); if (bittorrent->cache) done_bittorrent_piece_cache(bittorrent); done_bittorrent_meta(&bittorrent->meta); kill_timer(&bittorrent->timer); kill_timer(&bittorrent->upload_progress.timer); free_list(bittorrent->peer_pool); mem_free_set(&conn->info, NULL);}static struct bittorrent_connection *init_bittorrent_connection(struct connection *conn){ struct bittorrent_connection *bittorrent; bittorrent = mem_calloc(1, sizeof(*bittorrent)); if (!bittorrent) return NULL; init_list(bittorrent->peers); init_list(bittorrent->peer_pool); conn->info = bittorrent; conn->done = done_bittorrent_connection; init_bittorrent_peer_id(bittorrent->peer_id); bittorrent->conn = conn; bittorrent->tracker.timer = TIMER_ID_UNDEF; return bittorrent;}voidbittorrent_resume_callback(struct bittorrent_connection *bittorrent){ enum connection_state state; /* Failing to create the listening socket is fatal. */ state = init_bittorrent_listening_socket(bittorrent->conn); if (state != S_OK) { retry_connection(bittorrent->conn, state); return; } set_connection_state(bittorrent->conn, S_CONN_TRACKER); send_bittorrent_tracker_request(bittorrent->conn);}/* Metainfo file download callback */static voidbittorrent_metainfo_callback(void *data, enum connection_state state, struct string *response){ struct connection *conn = data; struct bittorrent_connection *bittorrent = conn->info; bittorrent->fetch = NULL; if (state != S_OK) { abort_connection(conn, state); return; } switch (parse_bittorrent_metafile(&bittorrent->meta, response)) { case BITTORRENT_STATE_OK: { size_t size = list_size(&bittorrent->meta.files); int *selection; assert(bittorrent->tracker.event == BITTORRENT_EVENT_STARTED); selection = get_bittorrent_selection(conn->uri, size); if (selection) { struct bittorrent_file *file; int index = 0; foreach (file, bittorrent->meta.files) file->selected = selection[index++]; mem_free(selection); } switch (init_bittorrent_piece_cache(bittorrent, response)) { case BITTORRENT_STATE_OK: bittorrent_resume_callback(bittorrent); return; case BITTORRENT_STATE_CACHE_RESUME: set_connection_state(bittorrent->conn, S_RESUME); return; case BITTORRENT_STATE_OUT_OF_MEM: state = S_OUT_OF_MEM; break; default: state = S_BITTORRENT_ERROR; } break; } case BITTORRENT_STATE_OUT_OF_MEM: state = S_OUT_OF_MEM; break; case BITTORRENT_STATE_ERROR: default: /* XXX: This can also happen when passing bittorrent:<uri> and * <uri> gives an HTTP 404 response. It might be worth fixing by * looking at the protocol header, however, direct usage of the * internal bittorrent: is at your own risk ... at least for * now. --jonas */ state = S_BITTORRENT_METAINFO; } abort_connection(conn, state);}/* The entry point for BitTorrent downloads. */voidbittorrent_protocol_handler(struct connection *conn){ struct uri *uri; struct bittorrent_connection *bittorrent; bittorrent = init_bittorrent_connection(conn); if (!bittorrent) { abort_connection(conn, S_OUT_OF_MEM); return; } assert(conn->uri->datalen); uri = get_uri(conn->uri->data, 0); if (!uri) { abort_connection(conn, S_OUT_OF_MEM); return; } set_connection_state(conn, S_CONN); set_connection_timeout(conn); conn->from = 0; init_bittorrent_fetch(&bittorrent->fetch, uri, bittorrent_metainfo_callback, conn, 0); done_uri(uri);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?