distribustream 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|