diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Data/Bitfield.hs | 8 | ||||
-rw-r--r-- | src/Network/BitTorrent.hs | 10 | ||||
-rw-r--r-- | src/Network/BitTorrent/Exchange.hs | 124 | ||||
-rw-r--r-- | src/Network/BitTorrent/Exchange/Protocol.hs | 4 | ||||
-rw-r--r-- | src/Network/BitTorrent/Internal.hs | 20 | ||||
-rw-r--r-- | src/Network/BitTorrent/Peer.hs | 4 | ||||
-rw-r--r-- | src/Network/BitTorrent/Tracker.hs | 4 |
7 files changed, 128 insertions, 46 deletions
diff --git a/src/Data/Bitfield.hs b/src/Data/Bitfield.hs index 9c62e183..18f80a4e 100644 --- a/src/Data/Bitfield.hs +++ b/src/Data/Bitfield.hs | |||
@@ -41,6 +41,8 @@ module Data.Bitfield | |||
41 | , member, notMember | 41 | , member, notMember |
42 | , findMin, findMax | 42 | , findMin, findMax |
43 | 43 | ||
44 | , isSubsetOf | ||
45 | |||
44 | , Frequency, frequencies, rarest | 46 | , Frequency, frequencies, rarest |
45 | 47 | ||
46 | -- * Combine | 48 | -- * Combine |
@@ -182,6 +184,9 @@ findMax Bitfield {..} | |||
182 | | S.null bfSet = Nothing | 184 | | S.null bfSet = Nothing |
183 | | otherwise = Just (S.findMax bfSet) | 185 | | otherwise = Just (S.findMax bfSet) |
184 | 186 | ||
187 | isSubsetOf :: Bitfield -> Bitfield -> Bool | ||
188 | isSubsetOf a b = bfSet a `S.isSubsetOf` bfSet b | ||
189 | |||
185 | -- | Frequencies are needed in piece selection startegies which use | 190 | -- | Frequencies are needed in piece selection startegies which use |
186 | -- availability quantity to find out the optimal next piece index to | 191 | -- availability quantity to find out the optimal next piece index to |
187 | -- download. | 192 | -- download. |
@@ -208,7 +213,8 @@ frequencies xs = runST $ do | |||
208 | rarest :: [Bitfield] -> Maybe PieceIx | 213 | rarest :: [Bitfield] -> Maybe PieceIx |
209 | rarest xs | 214 | rarest xs |
210 | | V.null freqMap = Nothing | 215 | | V.null freqMap = Nothing |
211 | | otherwise = Just $ fst $ V.ifoldr' minIx (0, freqMap V.! 0) freqMap | 216 | | otherwise |
217 | = Just $ fst $ V.ifoldr' minIx (0, freqMap V.! 0) freqMap | ||
212 | where | 218 | where |
213 | freqMap = frequencies xs | 219 | freqMap = frequencies xs |
214 | 220 | ||
diff --git a/src/Network/BitTorrent.hs b/src/Network/BitTorrent.hs index e9b5f17d..f7c4c004 100644 --- a/src/Network/BitTorrent.hs +++ b/src/Network/BitTorrent.hs | |||
@@ -22,10 +22,11 @@ module Network.BitTorrent | |||
22 | 22 | ||
23 | -- * Peer to Peer | 23 | -- * Peer to Peer |
24 | , P2P | 24 | , P2P |
25 | , Event(..) | ||
25 | , PeerSession ( connectedPeerAddr, enabledExtensions ) | 26 | , PeerSession ( connectedPeerAddr, enabledExtensions ) |
26 | , Block(..), BlockIx(..) | 27 | , Block(..), BlockIx(..), ppBlock, ppBlockIx |
27 | 28 | ||
28 | , awaitEvent, signalEvent | 29 | , awaitEvent, yieldEvent |
29 | ) where | 30 | ) where |
30 | 31 | ||
31 | import Control.Monad | 32 | import Control.Monad |
@@ -34,6 +35,7 @@ import Data.IORef | |||
34 | import Data.Torrent | 35 | import Data.Torrent |
35 | import Network.BitTorrent.Internal | 36 | import Network.BitTorrent.Internal |
36 | import Network.BitTorrent.Exchange | 37 | import Network.BitTorrent.Exchange |
38 | import Network.BitTorrent.Exchange.Protocol | ||
37 | import Network.BitTorrent.Tracker | 39 | import Network.BitTorrent.Tracker |
38 | 40 | ||
39 | 41 | ||
@@ -49,10 +51,14 @@ discover swarm action = do | |||
49 | port | 51 | port |
50 | 52 | ||
51 | progress <- getCurrentProgress (clientSession swarm) | 53 | progress <- getCurrentProgress (clientSession swarm) |
54 | |||
55 | putStrLn "lookup peers" | ||
52 | withTracker progress conn $ \tses -> do | 56 | withTracker progress conn $ \tses -> do |
53 | forever $ do | 57 | forever $ do |
54 | addr <- getPeerAddr tses | 58 | addr <- getPeerAddr tses |
59 | putStrLn "connecting to peer" | ||
55 | withPeer swarm addr action | 60 | withPeer swarm addr action |
56 | 61 | ||
57 | 62 | ||
63 | |||
58 | port = 10000 | 64 | port = 10000 |
diff --git a/src/Network/BitTorrent/Exchange.hs b/src/Network/BitTorrent/Exchange.hs index 65ec0eb7..de13d4ce 100644 --- a/src/Network/BitTorrent/Exchange.hs +++ b/src/Network/BitTorrent/Exchange.hs | |||
@@ -18,7 +18,7 @@ module Network.BitTorrent.Exchange | |||
18 | , Event(..) | 18 | , Event(..) |
19 | 19 | ||
20 | , P2P, withPeer | 20 | , P2P, withPeer |
21 | , awaitEvent, signalEvent | 21 | , awaitEvent, yieldEvent |
22 | ) where | 22 | ) where |
23 | 23 | ||
24 | import Control.Applicative | 24 | import Control.Applicative |
@@ -70,29 +70,31 @@ runConduit sock p2p = | |||
70 | conduitPut S.put $$ | 70 | conduitPut S.put $$ |
71 | sinkSocket sock | 71 | sinkSocket sock |
72 | 72 | ||
73 | waitMessage :: P2P Message | 73 | awaitMessage :: P2P Message |
74 | waitMessage = P2P (ReaderT go) | 74 | awaitMessage = P2P (ReaderT go) |
75 | where | 75 | where |
76 | go se = do | 76 | go se = do |
77 | liftIO $ putStrLn "trying recv:" | ||
77 | mmsg <- await | 78 | mmsg <- await |
78 | case mmsg of | 79 | case mmsg of |
79 | Nothing -> go se | 80 | Nothing -> go se |
80 | Just msg -> do | 81 | Just msg -> do |
81 | liftIO $ updateIncoming se | 82 | -- liftIO $ updateIncoming se |
82 | liftIO $ print msg | 83 | liftIO $ print ("recv:" <+> ppMessage msg) |
83 | return msg | 84 | return msg |
84 | 85 | ||
85 | signalMessage :: Message -> P2P () | 86 | yieldMessage :: Message -> P2P () |
86 | signalMessage msg = P2P $ ReaderT $ \se -> do | 87 | yieldMessage msg = P2P $ ReaderT $ \se -> do |
87 | C.yield msg | 88 | C.yield msg |
89 | liftIO $ print $ "sent:" <+> ppMessage msg | ||
88 | liftIO $ updateOutcoming se | 90 | liftIO $ updateOutcoming se |
89 | 91 | ||
90 | 92 | ||
91 | peerWant :: P2P Bitfield | 93 | peerWant :: P2P Bitfield |
92 | peerWant = BF.difference <$> getPeerBF <*> use bitfield | 94 | peerWant = BF.difference <$> getClientBF <*> use bitfield |
93 | 95 | ||
94 | clientWant :: P2P Bitfield | 96 | clientWant :: P2P Bitfield |
95 | clientWant = BF.difference <$> use bitfield <*> getPeerBF | 97 | clientWant = BF.difference <$> use bitfield <*> getClientBF |
96 | 98 | ||
97 | peerOffer :: P2P Bitfield | 99 | peerOffer :: P2P Bitfield |
98 | peerOffer = do | 100 | peerOffer = do |
@@ -104,13 +106,17 @@ clientOffer = do | |||
104 | sessionStatus <- use status | 106 | sessionStatus <- use status |
105 | if canUpload sessionStatus then peerWant else emptyBF | 107 | if canUpload sessionStatus then peerWant else emptyBF |
106 | 108 | ||
107 | revise :: P2P () | 109 | revise :: P2P Bitfield |
108 | revise = do | 110 | revise = do |
109 | peerInteresting <- (not . BF.null) <$> clientWant | 111 | want <- clientWant |
112 | let peerInteresting = not (BF.null want) | ||
110 | clientInterested <- use (status.clientStatus.interested) | 113 | clientInterested <- use (status.clientStatus.interested) |
111 | 114 | ||
112 | when (clientInterested /= peerInteresting) $ | 115 | when (clientInterested /= peerInteresting) $ do |
113 | signalMessage $ if peerInteresting then Interested else NotInterested | 116 | yieldMessage $ if peerInteresting then Interested else NotInterested |
117 | status.clientStatus.interested .= peerInteresting | ||
118 | |||
119 | return want | ||
114 | 120 | ||
115 | requireExtension :: Extension -> P2P () | 121 | requireExtension :: Extension -> P2P () |
116 | requireExtension required = do | 122 | requireExtension required = do |
@@ -118,9 +124,6 @@ requireExtension required = do | |||
118 | unless (required `elem` enabled) $ | 124 | unless (required `elem` enabled) $ |
119 | sessionError $ ppExtension required <+> "not enabled" | 125 | sessionError $ ppExtension required <+> "not enabled" |
120 | 126 | ||
121 | peerHave :: P2P Event | ||
122 | peerHave = undefined | ||
123 | |||
124 | -- haveMessage bf = do | 127 | -- haveMessage bf = do |
125 | -- cbf <- undefined -- liftIO $ readIORef $ clientBitfield swarmSession | 128 | -- cbf <- undefined -- liftIO $ readIORef $ clientBitfield swarmSession |
126 | -- if undefined -- ix `member` bf | 129 | -- if undefined -- ix `member` bf |
@@ -129,12 +132,21 @@ peerHave = undefined | |||
129 | 132 | ||
130 | 133 | ||
131 | -- | | 134 | -- | |
135 | -- +----------+---------+ | ||
136 | -- | Leacher | Seeder | | ||
137 | -- |----------+---------+ | ||
138 | -- | Available| | | ||
139 | -- | Want | Want | | ||
140 | -- | Fragment | | | ||
141 | -- +----------+---------+ | ||
142 | -- | ||
143 | -- | ||
132 | -- properties: | 144 | -- properties: |
133 | -- | 145 | -- |
134 | -- forall (Fragment block). isPiece block == True | 146 | -- forall (Fragment block). isPiece block == True |
135 | -- | 147 | -- |
136 | awaitEvent :: P2P Event | 148 | awaitEvent :: P2P Event |
137 | awaitEvent = waitMessage >>= go | 149 | awaitEvent = awaitMessage >>= go |
138 | where | 150 | where |
139 | go KeepAlive = awaitEvent | 151 | go KeepAlive = awaitEvent |
140 | go Choke = do | 152 | go Choke = do |
@@ -142,8 +154,11 @@ awaitEvent = waitMessage >>= go | |||
142 | awaitEvent | 154 | awaitEvent |
143 | 155 | ||
144 | go Unchoke = do | 156 | go Unchoke = do |
145 | status.clientStatus.choking .= False | 157 | status.peerStatus.choking .= False |
146 | awaitEvent | 158 | offer <- peerOffer |
159 | if BF.null offer | ||
160 | then awaitEvent | ||
161 | else return (Available offer) | ||
147 | 162 | ||
148 | go Interested = do | 163 | go Interested = do |
149 | status.peerStatus.interested .= True | 164 | status.peerStatus.interested .= True |
@@ -153,21 +168,45 @@ awaitEvent = waitMessage >>= go | |||
153 | status.peerStatus.interested .= False | 168 | status.peerStatus.interested .= False |
154 | awaitEvent | 169 | awaitEvent |
155 | 170 | ||
156 | -- go (Have ix) = peerHave =<< singletonBF ix | 171 | go (Have ix) = do |
157 | -- go (Bitfield bf) = peerHave =<< adjustBF bf | 172 | new <- singletonBF ix |
173 | bitfield %= BF.union new | ||
174 | revise | ||
175 | |||
176 | offer <- peerOffer | ||
177 | if not (BF.null offer) | ||
178 | then return (Available offer) | ||
179 | else awaitEvent | ||
180 | |||
181 | go (Bitfield bf) = do | ||
182 | new <- adjustBF bf | ||
183 | bitfield .= new | ||
184 | revise | ||
185 | |||
186 | offer <- peerOffer | ||
187 | if not (BF.null offer) | ||
188 | then return (Available offer) | ||
189 | else awaitEvent | ||
190 | |||
158 | go (Request bix) = do | 191 | go (Request bix) = do |
159 | bf <- use bitfield | 192 | bf <- clientOffer |
160 | if ixPiece bix `BF.member` bf | 193 | if ixPiece bix `BF.member` bf |
161 | then return (Want bix) | 194 | then return (Want bix) |
162 | else do | 195 | else do |
163 | signalMessage (RejectRequest bix) | 196 | -- check if extension is enabled |
197 | -- yieldMessage (RejectRequest bix) | ||
164 | awaitEvent | 198 | awaitEvent |
165 | 199 | ||
166 | go (Piece blk) = undefined | 200 | go (Piece blk) = do |
201 | -- this protect us from malicious peers and duplication | ||
202 | wanted <- clientWant | ||
203 | if blkPiece blk `BF.member` wanted | ||
204 | then return (Fragment blk) | ||
205 | else awaitEvent | ||
167 | 206 | ||
168 | {- | 207 | {- |
169 | go msg @ (Port _) | 208 | go (Port _) = do |
170 | = checkExtension msg ExtDHT $ do | 209 | requireExtension ExtDHT |
171 | undefined | 210 | undefined |
172 | 211 | ||
173 | go HaveAll = do | 212 | go HaveAll = do |
@@ -189,24 +228,39 @@ awaitEvent = waitMessage >>= go | |||
189 | then Available <$> singletonBF ix | 228 | then Available <$> singletonBF ix |
190 | else awaitEvent | 229 | else awaitEvent |
191 | 230 | ||
192 | go msg @ (RejectRequest ix) | 231 | go (RejectRequest ix) = do |
193 | = checkExtension msg ExtFast $ do | 232 | requireExtension ExtFast |
194 | undefined | 233 | awaitMessage |
195 | 234 | ||
196 | go msg @ (AllowedFast pix) | 235 | go (AllowedFast pix) = |
197 | = checkExtension msg ExtFast $ do | 236 | requireExtension ExtFast |
198 | undefined | 237 | awaitMessage |
199 | -} | 238 | -} |
200 | 239 | ||
201 | signalEvent :: Event -> P2P () | 240 | |
202 | signalEvent (Available bf) = undefined | 241 | -- | |
203 | signalEvent _ = undefined | 242 | -- @ |
243 | -- +----------+---------+ | ||
244 | -- | Leacher | Seeder | | ||
245 | -- |----------+---------+ | ||
246 | -- | Available| | | ||
247 | -- | Want |Fragment | | ||
248 | -- | Fragment | | | ||
249 | -- +----------+---------+ | ||
250 | -- @ | ||
251 | -- | ||
252 | yieldEvent :: Event -> P2P () | ||
253 | yieldEvent (Available bf) = undefined | ||
254 | yieldEvent _ = undefined | ||
204 | 255 | ||
205 | --flushBroadcast :: P2P () | 256 | --flushBroadcast :: P2P () |
206 | --flushBroadcast = nextBroadcast >>= maybe (return ()) go | 257 | --flushBroadcast = nextBroadcast >>= maybe (return ()) go |
207 | -- where | 258 | -- where |
208 | -- go = undefined | 259 | -- go = undefined |
209 | 260 | ||
261 | checkPiece :: PieceLIx -> {-ByteString -> -} P2P Bool | ||
262 | checkPiece = undefined | ||
263 | |||
210 | {----------------------------------------------------------------------- | 264 | {----------------------------------------------------------------------- |
211 | P2P monad | 265 | P2P monad |
212 | -----------------------------------------------------------------------} | 266 | -----------------------------------------------------------------------} |
diff --git a/src/Network/BitTorrent/Exchange/Protocol.hs b/src/Network/BitTorrent/Exchange/Protocol.hs index 718e339d..6b97d8d1 100644 --- a/src/Network/BitTorrent/Exchange/Protocol.hs +++ b/src/Network/BitTorrent/Exchange/Protocol.hs | |||
@@ -164,14 +164,18 @@ defaultHandshake = Handshake defaultBTProtocol defaultReserved | |||
164 | -- | Handshaking with a peer specified by the second argument. | 164 | -- | Handshaking with a peer specified by the second argument. |
165 | handshake :: Socket -> Handshake -> IO Handshake | 165 | handshake :: Socket -> Handshake -> IO Handshake |
166 | handshake sock hs = do | 166 | handshake sock hs = do |
167 | putStrLn "send handshake" | ||
167 | sendAll sock (S.encode hs) | 168 | sendAll sock (S.encode hs) |
168 | 169 | ||
170 | putStrLn "recv handshake size" | ||
169 | header <- recv sock 1 | 171 | header <- recv sock 1 |
170 | when (B.length header == 0) $ | 172 | when (B.length header == 0) $ |
171 | throw $ userError "Unable to receive handshake." | 173 | throw $ userError "Unable to receive handshake." |
172 | 174 | ||
173 | let protocolLen = B.head header | 175 | let protocolLen = B.head header |
174 | let restLen = handshakeSize protocolLen - 1 | 176 | let restLen = handshakeSize protocolLen - 1 |
177 | |||
178 | putStrLn "recv handshake body" | ||
175 | body <- recv sock restLen | 179 | body <- recv sock restLen |
176 | let resp = B.cons protocolLen body | 180 | let resp = B.cons protocolLen body |
177 | 181 | ||
diff --git a/src/Network/BitTorrent/Internal.hs b/src/Network/BitTorrent/Internal.hs index 2f538652..3d07a82f 100644 --- a/src/Network/BitTorrent/Internal.hs +++ b/src/Network/BitTorrent/Internal.hs | |||
@@ -34,8 +34,8 @@ module Network.BitTorrent.Internal | |||
34 | ) | 34 | ) |
35 | , SessionState | 35 | , SessionState |
36 | , bitfield, status | 36 | , bitfield, status |
37 | , emptyBF, fullBF, singletonBF | 37 | , emptyBF, fullBF, singletonBF, adjustBF |
38 | , getPieceCount, getPeerBF | 38 | , getPieceCount, getClientBF |
39 | , sessionError, withPeerSession | 39 | , sessionError, withPeerSession |
40 | 40 | ||
41 | -- * Timeouts | 41 | -- * Timeouts |
@@ -250,10 +250,15 @@ withPeerSession ss @ SwarmSession {..} addr | |||
250 | let caps = encodeExts $ allowedExtensions $ clientSession | 250 | let caps = encodeExts $ allowedExtensions $ clientSession |
251 | let pid = clientPeerID $ clientSession | 251 | let pid = clientPeerID $ clientSession |
252 | let chs = Handshake defaultBTProtocol caps (tInfoHash torrentMeta) pid | 252 | let chs = Handshake defaultBTProtocol caps (tInfoHash torrentMeta) pid |
253 | 253 | putStrLn "trying to connect" | |
254 | sock <- connectToPeer addr | 254 | sock <- connectToPeer addr |
255 | |||
256 | putStrLn "trying to handshake" | ||
255 | phs <- handshake sock chs `onException` close sock | 257 | phs <- handshake sock chs `onException` close sock |
256 | 258 | ||
259 | cbf <- readTVarIO clientBitfield | ||
260 | sendAll sock (encode (Bitfield cbf)) | ||
261 | |||
257 | let enabled = decodeExts (enabledCaps caps (handshakeCaps phs)) | 262 | let enabled = decodeExts (enabledCaps caps (handshakeCaps phs)) |
258 | ps <- PeerSession addr ss enabled | 263 | ps <- PeerSession addr ss enabled |
259 | <$> registerTimeout (eventManager clientSession) | 264 | <$> registerTimeout (eventManager clientSession) |
@@ -281,8 +286,11 @@ fullBF = liftM haveAll getPieceCount | |||
281 | singletonBF :: (MonadReader PeerSession m) => PieceIx -> m Bitfield | 286 | singletonBF :: (MonadReader PeerSession m) => PieceIx -> m Bitfield |
282 | singletonBF ix = liftM (BF.singleton ix) getPieceCount | 287 | singletonBF ix = liftM (BF.singleton ix) getPieceCount |
283 | 288 | ||
284 | getPeerBF :: (MonadIO m, MonadReader PeerSession m) => m Bitfield | 289 | adjustBF :: (MonadReader PeerSession m) => Bitfield -> m Bitfield |
285 | getPeerBF = asks swarmSession >>= liftIO . readTVarIO . clientBitfield | 290 | adjustBF bf = (`adjustSize` bf) `liftM` getPieceCount |
291 | |||
292 | getClientBF :: (MonadIO m, MonadReader PeerSession m) => m Bitfield | ||
293 | getClientBF = asks swarmSession >>= liftIO . readTVarIO . clientBitfield | ||
286 | 294 | ||
287 | --data Signal = | 295 | --data Signal = |
288 | --nextBroadcast :: P2P (Maybe Signal) | 296 | --nextBroadcast :: P2P (Maybe Signal) |
@@ -317,7 +325,7 @@ updateOutcoming PeerSession {..} = | |||
317 | sendKA :: Socket -> IO () | 325 | sendKA :: Socket -> IO () |
318 | sendKA sock {- SwarmSession {..} -} = do | 326 | sendKA sock {- SwarmSession {..} -} = do |
319 | print "I'm sending keep alive." | 327 | print "I'm sending keep alive." |
320 | sendAll sock (encode BT.KeepAlive) | 328 | -- sendAll sock (encode BT.KeepAlive) |
321 | -- let mgr = eventManager clientSession | 329 | -- let mgr = eventManager clientSession |
322 | -- updateTimeout mgr | 330 | -- updateTimeout mgr |
323 | print "Done.." | 331 | print "Done.." |
diff --git a/src/Network/BitTorrent/Peer.hs b/src/Network/BitTorrent/Peer.hs index 9aa924d3..6e5db0e0 100644 --- a/src/Network/BitTorrent/Peer.hs +++ b/src/Network/BitTorrent/Peer.hs | |||
@@ -531,8 +531,12 @@ peerSockAddr = SockAddrInet <$> (g . peerPort) <*> (htonl . peerIP) | |||
531 | -- | Tries to connect to peer using reasonable default parameters. | 531 | -- | Tries to connect to peer using reasonable default parameters. |
532 | connectToPeer :: PeerAddr -> IO Socket | 532 | connectToPeer :: PeerAddr -> IO Socket |
533 | connectToPeer p = do | 533 | connectToPeer p = do |
534 | putStrLn "socket" | ||
534 | sock <- socket AF_INET Stream Network.Socket.defaultProtocol | 535 | sock <- socket AF_INET Stream Network.Socket.defaultProtocol |
536 | |||
537 | putStrLn "connect" | ||
535 | connect sock (peerSockAddr p) | 538 | connect sock (peerSockAddr p) |
539 | putStrLn "connected" | ||
536 | return sock | 540 | return sock |
537 | 541 | ||
538 | -- | Pretty print peer address in human readable form. | 542 | -- | Pretty print peer address in human readable form. |
diff --git a/src/Network/BitTorrent/Tracker.hs b/src/Network/BitTorrent/Tracker.hs index 275b5422..cb776431 100644 --- a/src/Network/BitTorrent/Tracker.hs +++ b/src/Network/BitTorrent/Tracker.hs | |||
@@ -94,8 +94,8 @@ genericReq ses pr = TRequest { | |||
94 | } | 94 | } |
95 | 95 | ||
96 | 96 | ||
97 | -- | The first request to the tracker that should be created is 'startedReq'. | 97 | -- | The first request to the tracker that should be created is |
98 | -- It includes necessary 'Started' event field. | 98 | -- 'startedReq'. It includes necessary 'Started' event field. |
99 | -- | 99 | -- |
100 | startedReq :: TConnection -> Progress -> TRequest | 100 | startedReq :: TConnection -> Progress -> TRequest |
101 | startedReq ses pr = (genericReq ses pr) { | 101 | startedReq ses pr = (genericReq ses pr) { |