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 + -
显示快捷键?