distribustream 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +13 -0
- data/README +3 -7
- data/Rakefile +2 -2
- data/distribustream.gemspec +18 -9
- data/lib/pdtp/client/callbacks.rb +4 -1
- data/lib/pdtp/common.rb +1 -1
- data/lib/pdtp/common/http_server.rb +5 -0
- data/lib/pdtp/common/protocol.rb +1 -8
- data/lib/pdtp/server/bandwidth_estimator.rb +148 -0
- data/lib/pdtp/server/chunk_info.rb +97 -0
- data/lib/pdtp/server/connection.rb +102 -3
- data/lib/pdtp/server/dispatcher.rb +82 -210
- data/lib/pdtp/server/file_service.rb +5 -4
- data/lib/pdtp/server/status_helper.rb +19 -3
- data/lib/pdtp/server/transfer.rb +26 -14
- data/lib/pdtp/server/transfer_manager.rb +146 -0
- data/status/index.erb +31 -17
- data/status/stylesheets/style.css +1 -1
- metadata +33 -30
- data/lib/pdtp/server/client_info.rb +0 -154
@@ -0,0 +1,146 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# This source file is distributed as part of the
|
18
|
+
# DistribuStream file transfer system.
|
19
|
+
#
|
20
|
+
# See http://distribustream.org/
|
21
|
+
#++
|
22
|
+
|
23
|
+
require File.dirname(__FILE__) + '/transfer'
|
24
|
+
|
25
|
+
module PDTP
|
26
|
+
class Server
|
27
|
+
# Decision-making logic for initiating peer-to-peer transfers
|
28
|
+
class TransferManager
|
29
|
+
def initialize(connections, file_service)
|
30
|
+
@connections, @file_service = connections, file_service
|
31
|
+
@updated_clients = []
|
32
|
+
end
|
33
|
+
|
34
|
+
# Add client to list of ones needing updates
|
35
|
+
def process_client(client)
|
36
|
+
@updated_clients << client
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates new transfers for all clients that have been updated
|
40
|
+
def spawn_all_transfers
|
41
|
+
@updated_clients.each { |client| spawn_transfers_for_client(client) }
|
42
|
+
@updated_clients.clear
|
43
|
+
end
|
44
|
+
|
45
|
+
#########
|
46
|
+
protected
|
47
|
+
#########
|
48
|
+
|
49
|
+
# Spawns uploads and downloads for this client.
|
50
|
+
# Should be called every time there is a change that would affect
|
51
|
+
# what this client has or wants
|
52
|
+
def spawn_transfers_for_client(connection)
|
53
|
+
while connection.wants_download? do
|
54
|
+
break unless spawn_download_for_client(connection)
|
55
|
+
end
|
56
|
+
|
57
|
+
while connection.wants_upload? do
|
58
|
+
break unless spawn_upload_for_client(connection)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Creates a single download for the specified client
|
63
|
+
# Returns true on success, false on failure
|
64
|
+
def spawn_download_for_client(connection)
|
65
|
+
feasible_peers = []
|
66
|
+
|
67
|
+
begin
|
68
|
+
url, chunkid = connection.chunk_info.high_priority_chunk
|
69
|
+
rescue
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
|
73
|
+
@connections.each do |c2|
|
74
|
+
next if connection == c2
|
75
|
+
next unless c2.wants_upload?
|
76
|
+
if c2.chunk_info.provided?(url, chunkid)
|
77
|
+
feasible_peers << c2
|
78
|
+
break if feasible_peers.size > 5
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# we now have a list of clients that have the requested chunk.
|
83
|
+
# pick one and start the transfer
|
84
|
+
if feasible_peers.size > 0
|
85
|
+
#FIXME base this on the trust model
|
86
|
+
giver = feasible_peers[rand(feasible_peers.size)]
|
87
|
+
return begin_transfer(connection,giver,url,chunkid)
|
88
|
+
#FIXME should we try again if begin_transfer fails?
|
89
|
+
end
|
90
|
+
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
94
|
+
# Creates a single upload for the specified client
|
95
|
+
# Returns true on success, false on failure
|
96
|
+
def spawn_upload_for_client(connection)
|
97
|
+
@connections.each do |c2|
|
98
|
+
next if connection == c2
|
99
|
+
next unless c2.wants_download?
|
100
|
+
|
101
|
+
begin
|
102
|
+
url, chunkid = c2.chunk_info.high_priority_chunk
|
103
|
+
rescue
|
104
|
+
next
|
105
|
+
end
|
106
|
+
|
107
|
+
if connection.chunk_info.provided?(url, chunkid)
|
108
|
+
return begin_transfer(c2, connection, url, chunkid)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
false
|
113
|
+
end
|
114
|
+
|
115
|
+
# Creates a new transfer between two peers
|
116
|
+
# Returns true on success, or false if the specified transfer is already in progress
|
117
|
+
def begin_transfer(taker, giver, url, chunkid)
|
118
|
+
byte_range = @file_service.get_info(url).chunk_range(chunkid)
|
119
|
+
t = Transfer.new(taker, giver, url, chunkid, byte_range)
|
120
|
+
|
121
|
+
#make sure this transfer doesnt already exist
|
122
|
+
t1 = taker.transfers[t.transfer_id]
|
123
|
+
t2 = giver.transfers[t.transfer_id]
|
124
|
+
return false unless t1.nil? and t2.nil?
|
125
|
+
|
126
|
+
taker.chunk_info.transfer(url, chunkid..chunkid)
|
127
|
+
taker.transfers[t.transfer_id] = t
|
128
|
+
giver.transfers[t.transfer_id] = t
|
129
|
+
|
130
|
+
#send transfer message to the connector
|
131
|
+
addr, port = t.acceptor.get_peer_info
|
132
|
+
|
133
|
+
t.connector.send_message(:transfer,
|
134
|
+
:host => addr,
|
135
|
+
:port => t.acceptor.listen_port,
|
136
|
+
:method => t.connector == t.taker ? "get" : "put",
|
137
|
+
:url => url,
|
138
|
+
:range => byte_range,
|
139
|
+
:peer_id => t.acceptor.client_id
|
140
|
+
)
|
141
|
+
|
142
|
+
true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/status/index.erb
CHANGED
@@ -12,24 +12,38 @@
|
|
12
12
|
</div>
|
13
13
|
<center>
|
14
14
|
<table border="0" cellpadding="0" cellspacing="0">
|
15
|
-
<tr
|
15
|
+
<tr>
|
16
|
+
<th>Client</th>
|
17
|
+
<th>Bandwidth</th>
|
18
|
+
<th>Transfers</th>
|
19
|
+
<th>Files</th>
|
20
|
+
</tr>
|
21
|
+
|
16
22
|
<% each_peer do |peer| %>
|
17
|
-
<tr class="row<%= cycle(' row_alternate', '') %>"
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
23
|
+
<tr class="row<%= cycle(' row_alternate', '') %>">
|
24
|
+
<td>
|
25
|
+
<%= peer_name(peer) %><br />
|
26
|
+
<%= peer_address(peer) %>
|
27
|
+
</td>
|
28
|
+
<td>
|
29
|
+
Upstream: <%= upstream_bandwidth(peer) %><br />
|
30
|
+
<% unless peer.file_service? %>
|
31
|
+
Downstream: <%= downstream_bandwidth(peer) %>
|
32
|
+
<% end %>
|
33
|
+
</td>
|
34
|
+
<td>
|
35
|
+
<% each_transfer(peer) do |transfer| %>
|
36
|
+
<%= transfer_info(peer, transfer) %><br />
|
37
|
+
<% end %>
|
38
|
+
</td>
|
39
|
+
<td>
|
40
|
+
<% each_file(peer) do |file| %>
|
41
|
+
<%= file_path(file) %>
|
42
|
+
(<%= chunks_completed(file) %>/<%= chunks_active(file) %>,
|
43
|
+
<%= percent_complete(file) %>%)<br />
|
44
|
+
<% end %>
|
45
|
+
</td>
|
46
|
+
</tr>
|
33
47
|
<% end %>
|
34
48
|
</table>
|
35
49
|
</center>
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: distribustream
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2008-11-
|
6
|
+
version: 0.5.0
|
7
|
+
date: 2008-11-27 00:00:00 -07:00
|
8
8
|
summary: DistribuStream is a fully open peercasting system allowing on-demand or live streaming media to be delivered at a fraction of the normal cost
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,43 +33,45 @@ authors:
|
|
33
33
|
- James Sanders
|
34
34
|
- Tom Stapleton
|
35
35
|
files:
|
36
|
-
- bin/dstream
|
37
36
|
- bin/dsclient
|
37
|
+
- bin/dstream
|
38
38
|
- lib/pdtp
|
39
|
-
- lib/pdtp/server
|
40
|
-
- lib/pdtp/common.rb
|
41
39
|
- lib/pdtp/client
|
42
|
-
- lib/pdtp/
|
43
|
-
- lib/pdtp/client.rb
|
44
|
-
- lib/pdtp/server.rb
|
45
|
-
- lib/pdtp/server/file_service_protocol.rb
|
46
|
-
- lib/pdtp/server/status_handler.rb
|
47
|
-
- lib/pdtp/server/trust.rb
|
48
|
-
- lib/pdtp/server/status_helper.rb
|
49
|
-
- lib/pdtp/server/transfer.rb
|
50
|
-
- lib/pdtp/server/client_info.rb
|
51
|
-
- lib/pdtp/server/connection.rb
|
52
|
-
- lib/pdtp/server/file_service.rb
|
53
|
-
- lib/pdtp/server/dispatcher.rb
|
40
|
+
- lib/pdtp/client/callbacks.rb
|
41
|
+
- lib/pdtp/client/connection.rb
|
54
42
|
- lib/pdtp/client/file_buffer.rb
|
43
|
+
- lib/pdtp/client/file_service.rb
|
55
44
|
- lib/pdtp/client/http_client.rb
|
56
45
|
- lib/pdtp/client/http_handler.rb
|
57
|
-
- lib/pdtp/client/callbacks.rb
|
58
46
|
- lib/pdtp/client/transfer.rb
|
59
|
-
- lib/pdtp/client
|
60
|
-
- lib/pdtp/
|
61
|
-
- lib/pdtp/common/
|
47
|
+
- lib/pdtp/client.rb
|
48
|
+
- lib/pdtp/common
|
49
|
+
- lib/pdtp/common/file_service.rb
|
62
50
|
- lib/pdtp/common/http_server.rb
|
51
|
+
- lib/pdtp/common/length_prefix_protocol.rb
|
63
52
|
- lib/pdtp/common/protocol.rb
|
64
|
-
- lib/pdtp/common
|
65
|
-
-
|
53
|
+
- lib/pdtp/common.rb
|
54
|
+
- lib/pdtp/server
|
55
|
+
- lib/pdtp/server/bandwidth_estimator.rb
|
56
|
+
- lib/pdtp/server/chunk_info.rb
|
57
|
+
- lib/pdtp/server/connection.rb
|
58
|
+
- lib/pdtp/server/dispatcher.rb
|
59
|
+
- lib/pdtp/server/file_service.rb
|
60
|
+
- lib/pdtp/server/file_service_protocol.rb
|
61
|
+
- lib/pdtp/server/status_handler.rb
|
62
|
+
- lib/pdtp/server/status_helper.rb
|
63
|
+
- lib/pdtp/server/transfer.rb
|
64
|
+
- lib/pdtp/server/transfer_manager.rb
|
65
|
+
- lib/pdtp/server/trust.rb
|
66
|
+
- lib/pdtp/server.rb
|
66
67
|
- conf/bigchunk.yml
|
67
68
|
- conf/debug.yml
|
68
|
-
-
|
69
|
+
- conf/example.yml
|
69
70
|
- status/images
|
71
|
+
- status/images/logo.png
|
70
72
|
- status/index.erb
|
73
|
+
- status/stylesheets
|
71
74
|
- status/stylesheets/style.css
|
72
|
-
- status/images/logo.png
|
73
75
|
- Rakefile
|
74
76
|
- distribustream.gemspec
|
75
77
|
- COPYING
|
@@ -79,10 +81,11 @@ files:
|
|
79
81
|
test_files: []
|
80
82
|
|
81
83
|
rdoc_options:
|
82
|
-
- --
|
83
|
-
-
|
84
|
-
- --
|
85
|
-
-
|
84
|
+
- --title
|
85
|
+
- PDTP
|
86
|
+
- --main
|
87
|
+
- README
|
88
|
+
- --line-numbers
|
86
89
|
extra_rdoc_files:
|
87
90
|
- COPYING
|
88
91
|
- README
|
@@ -1,154 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
3
|
-
#
|
4
|
-
# This program is free software: you can redistribute it and/or modify
|
5
|
-
# it under the terms of the GNU General Public License as published by
|
6
|
-
# the Free Software Foundation, either version 3 of the License, or
|
7
|
-
# (at your option) any later version.
|
8
|
-
#
|
9
|
-
# This program is distributed in the hope that it will be useful,
|
10
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
-
# GNU General Public License for more details.
|
13
|
-
#
|
14
|
-
# You should have received a copy of the GNU General Public License
|
15
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
-
#
|
17
|
-
# This source file is distributed as part of the
|
18
|
-
# DistribuStream file transfer system.
|
19
|
-
#
|
20
|
-
# See http://distribustream.org/
|
21
|
-
#++
|
22
|
-
|
23
|
-
require File.dirname(__FILE__) + '/trust'
|
24
|
-
|
25
|
-
module PDTP
|
26
|
-
class Server
|
27
|
-
#stores information about a single connected client
|
28
|
-
class ClientInfo
|
29
|
-
attr_accessor :chunk_info, :trust
|
30
|
-
attr_accessor :listen_port, :client_id
|
31
|
-
attr_accessor :transfers
|
32
|
-
|
33
|
-
def initialize
|
34
|
-
@chunk_info=ChunkInfo.new
|
35
|
-
@listen_port=6000 #default
|
36
|
-
@trust=Trust.new
|
37
|
-
@transfers=Hash.new
|
38
|
-
end
|
39
|
-
|
40
|
-
# returns true if this client wants the server to spawn a transfer for it
|
41
|
-
def wants_download?
|
42
|
-
transfer_state_allowed=5
|
43
|
-
total_allowed=10
|
44
|
-
transferring=0
|
45
|
-
@transfers.each do |key, t|
|
46
|
-
transferring=transferring+1 if t.verification_asked
|
47
|
-
return false if transferring >= transfer_state_allowed
|
48
|
-
end
|
49
|
-
|
50
|
-
@transfers.size < total_allowed
|
51
|
-
end
|
52
|
-
|
53
|
-
#this could have a different definition, but it works fine to use wants_download?
|
54
|
-
alias_method :wants_upload?, :wants_download?
|
55
|
-
|
56
|
-
#returns a list of all the stalled transfers this client is a part of
|
57
|
-
def get_stalled_transfers
|
58
|
-
stalled=[]
|
59
|
-
timeout=20.0
|
60
|
-
now=Time.now
|
61
|
-
@transfers.each do |key,t|
|
62
|
-
#only delete if we are the acceptor to prevent race conditions
|
63
|
-
next if t.acceptor.user_data != self
|
64
|
-
if now-t.creation_time > timeout and not t.verification_asked
|
65
|
-
stalled << t
|
66
|
-
end
|
67
|
-
end
|
68
|
-
stalled
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
#stores information about the chunks requested or provided by a client
|
73
|
-
class ChunkInfo
|
74
|
-
def initialize
|
75
|
-
@files={}
|
76
|
-
end
|
77
|
-
|
78
|
-
#each chunk can either be provided, requested, transfer, or none
|
79
|
-
def provide(filename,range); set(filename,range,:provided) ; end
|
80
|
-
def unprovide(filename,range); set(filename,range, :none); end
|
81
|
-
def request(filename,range); set(filename,range, :requested); end
|
82
|
-
def unrequest(filename,range); set(filename,range, :none); end
|
83
|
-
def transfer(filename,range); set(filename,range, :transfer); end
|
84
|
-
|
85
|
-
def provided?(filename,chunk); get(filename,chunk) == :provided; end
|
86
|
-
def requested?(filename,chunk); get(filename,chunk) == :requested; end
|
87
|
-
|
88
|
-
#returns a high priority requested chunk
|
89
|
-
def high_priority_chunk
|
90
|
-
#right now return any chunk
|
91
|
-
@files.each do |name,file|
|
92
|
-
file.each_index do |i|
|
93
|
-
return [name,i] if file[i]==:requested
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
nil
|
98
|
-
end
|
99
|
-
|
100
|
-
#calls a block for each chunk of the specified type
|
101
|
-
def each_chunk_of_type(type)
|
102
|
-
@files.each do |name,file|
|
103
|
-
file.each_index do |i|
|
104
|
-
yield(name,i) if file[i]==type
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
class FileStats
|
110
|
-
attr_accessor :file_chunks, :chunks_requested,:url
|
111
|
-
attr_accessor :chunks_provided, :chunks_transferring
|
112
|
-
|
113
|
-
def initialize
|
114
|
-
@url=""
|
115
|
-
@file_chunks=0
|
116
|
-
@chunks_requested=0
|
117
|
-
@chunks_provided=0
|
118
|
-
@chunks_transferring=0
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
#returns an array of FileStats objects for debug output
|
123
|
-
def get_file_stats
|
124
|
-
stats=[]
|
125
|
-
@files.each do |name,file|
|
126
|
-
fs=FileStats.new
|
127
|
-
fs.file_chunks=file.size
|
128
|
-
fs.url=name
|
129
|
-
file.each do |chunk|
|
130
|
-
fs.chunks_requested+=1 if chunk==:requested
|
131
|
-
fs.chunks_provided+=1 if chunk==:provided
|
132
|
-
fs.chunks_transferring+=1 if chunk==:transfer
|
133
|
-
end
|
134
|
-
stats << fs
|
135
|
-
end
|
136
|
-
|
137
|
-
stats
|
138
|
-
end
|
139
|
-
|
140
|
-
#########
|
141
|
-
protected
|
142
|
-
#########
|
143
|
-
|
144
|
-
def get(filename,chunk)
|
145
|
-
@files[filename][chunk] rescue :neither
|
146
|
-
end
|
147
|
-
|
148
|
-
def set(filename,range,state)
|
149
|
-
chunks=@files[filename]||=Array.new
|
150
|
-
range.each { |i| chunks[i]=state }
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|