コード例 #1
0
ファイル: dht.go プロジェクト: skycoin/skycoin
// Called when the DHT finds a peer
func (self *DHT) ReceivePeers(r map[dht.InfoHash][]string) {

	for infoHash, results := range r {
		for _, p := range results {
			peerAddress := dht.DecodePeerAddress(p)
			logger.Debug("DHT Peer: %s", peerAddress)

			if self.Config.AddPeerCallback == nil {
				log.Panic("Must set callback for receiving DHT peers")
			}

			str, ok := self.InfoHashes[string(infoHash)]
			if ok != true {
				log.Panic("infohash not requested")
			}

			self.Config.AddPeerCallback(str, peerAddress)

			//_, err := peers.AddPeer(peer)
			//if err != nil {
			//	logger.Info("Failed to add DHT peer: %v", err)
			//}
		}
	}
}
コード例 #2
0
ファイル: supernode.go プロジェクト: spikebike/supernode
// drainresults loops, printing the address of nodes it has found.
func drainresults(n *dht.DHT) {
	fmt.Println("=========================== DHT")
	for r := range n.PeersRequestResults {
		for ih, peers := range r {
			for _, x := range peers {
				l4g.Warn("Found peer for infohash %x at %v", ih, dht.DecodePeerAddress(x))
			}
		}
	}
}
コード例 #3
0
func checkPeers(n *dht.DHT, cm *network.ConnectionManager) {
	for r := range n.PeersRequestResults {
		for _, peers := range r {
			for _, x := range peers {
				address := dht.DecodePeerAddress(x)
				log.Printf("DHT Found Peer: %v\n", address)
				go attemptConnectionToPeer(address, cm)
			}
		}
	}
}
コード例 #4
0
ファイル: wherez.go プロジェクト: nictuku/wherez
func obtainPeers(d *dht.DHT, passphrase []byte, c chan Peer) {
	for r := range d.PeersRequestResults {
		for _, peers := range r {
			for _, x := range peers {
				// A DHT peer for our infohash was found. It
				// needs to be authenticated.
				checkPeer(dht.DecodePeerAddress(x), passphrase, c)
			}
		}
	}
}
コード例 #5
0
ファイル: dht.go プロジェクト: RagnarDanneskjold/skycoin
// Called when the DHT finds a peer
func (self *DHT) ReceivePeers(r map[dht.InfoHash][]string, peers *pex.Pex) {
	for _, results := range r {
		for _, p := range results {
			peer := dht.DecodePeerAddress(p)
			logger.Debug("DHT Peer: %s", peer)
			_, err := peers.AddPeer(peer)
			if err != nil {
				logger.Info("Failed to add DHT peer: %v", err)
			}
		}
	}
}
コード例 #6
0
ファイル: main.go プロジェクト: nhelke/dht
// drainresults loops, printing the address of nodes it has found.
func drainresults(n *dht.DHT) {
	fmt.Println("=========================== DHT")
	l4g.Warn("Note that there are many bad nodes that reply to anything you ask.")
	l4g.Warn("Peers found:")
	for r := range n.PeersRequestResults {
		for _, peers := range r {
			for _, x := range peers {
				l4g.Warn("%v", dht.DecodePeerAddress(x))
			}
		}
	}
}
コード例 #7
0
ファイル: downloader.go プロジェクト: jmwatte/winston
func downloadFile(infoHash dht.InfoHash, peerChannel <-chan string, eventsChannel chan<- downloadEvent) {
	//TODO: implement
	//TODO: get peers from buffered channel, connect to them, download torrent file
	//TODO: add some sure way to detect goroutine finished (defer send to channel?)
	peerCount := 0
	tick := time.Tick(10 * time.Second)
	timeout := time.After(10 * time.Minute)

	for {
		select {
		case newPeer, chanOk := <-peerChannel:
			if !chanOk {
				log.V(2).Infof("WINSTON: Peer channel for %x was closed, probably by torrent timeout. Killing download goroutine...\n", infoHash)
				return
			}

			peerCount++
			peerStr := dht.DecodePeerAddress(newPeer)
			if strings.HasSuffix(peerStr, ":1") {
				log.V(3).Infof("WINSTON: Skipping peer #%d for torrent %x for looking fake: %s\n", peerCount, infoHash, peerStr)
				continue
			}

			log.V(3).Infof("WINSTON: Peer #%d received for torrent %x: %s\n", peerCount, infoHash, peerStr)

			//TODO: run as paralel goroutines
			//TODO: taka care to have N parallel downloaders at all times, if possible
			//TODO: send requests for more peers periodically
			//TODO: gather results and stop everything once a successful result has been found
			torrent := peer.DownloadMetadataFromPeer(peerStr, string(infoHash))
			if torrent != nil {
				log.V(1).Infof("WINSTON: Torrent %x really was downloaded!\n", infoHash)
				err := saveMetaInfo(string(infoHash), torrent)
				if err != nil {
					panic(fmt.Errorf("Could not save a simple file: %s", err))
				}
				eventsChannel <- downloadEvent{infoHash, eventSucessfulDownload}
				return
			}

			log.V(1).Infof("WINSTON: Torrent %x was not downloaded, trying again...\n", infoHash)

		case <-tick:
			log.V(3).Infof("WINSTON: Tick-tack %x...\n", infoHash)

		case <-timeout:
			log.V(3).Infof("WINSTON: Torrent %x timed out...\n", infoHash)
			eventsChannel <- downloadEvent{infoHash, eventTimeout}
			return
		}
	}
}
コード例 #8
0
ファイル: main.go プロジェクト: screscent/dht
// drainresults loops, printing the address of nodes it has found.
func drainresults(n *dht.DHT) {
	fmt.Println("=========================== DHT")
	for r := range n.PeersRequestResults {
		for ih, peers := range r {
			l4g.Warn("Found peer(s) for infohash %x:", ih)
			for _, x := range peers {
				l4g.Warn("==========================> %v", dht.DecodePeerAddress(x))
				l4g.Warn("Note that there are many bad nodes that reply to anything you ask, so don't get too excited.")
				l4g.Warn("==========================")
			}
		}
	}
}
コード例 #9
0
ファイル: test.go プロジェクト: ss23/verdant-octo-spork
func drain(n *dht.DHT) {
	count := 0
	fmt.Println("=========================== DHT")
	fmt.Println("Note that there are many bad nodes that reply to anything you ask.")
	fmt.Println("Peers found:")
	for r := range n.PeersRequestResults {
		for _, peers := range r {
			for _, x := range peers {
				fmt.Printf("%d: %v\n", count, dht.DecodePeerAddress(x))
				count++
			}
		}
	}
}
コード例 #10
0
ファイル: discovery_dht.go プロジェクト: intfrr/meshbird
func (d *DiscoveryDHT) awaitPeers() {
	defer d.waitGroup.Done()
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for d.Status() != StatusStopping {
		select {
		case r := <-d.node.PeersRequestResults:
			for _, peers := range r {
				for _, x := range peers {
					host := dht.DecodePeerAddress(x)
					d.addPeer(host)
				}
			}
		case <-ticker.C:
		}
	}
}
コード例 #11
0
ファイル: torrentLoop.go プロジェクト: jackpal/Taipei-Torrent
func RunTorrents(flags *TorrentFlags, torrentFiles []string) (err error) {
	conChan, listenPort, err := ListenForPeerConnections(flags)
	if err != nil {
		log.Println("Couldn't listen for peers connection: ", err)
		return
	}
	quitChan := listenSigInt()

	createChan := make(chan string, flags.MaxActive)
	startChan := make(chan *TorrentSession, 1)
	doneChan := make(chan *TorrentSession, 1)

	var dhtNode dht.DHT
	if flags.UseDHT {
		dhtNode = *startDHT(flags.Port)
	}

	torrentSessions := make(map[string]*TorrentSession)

	go func() {
		for torrentFile := range createChan {
			ts, err := NewTorrentSession(flags, torrentFile, uint16(listenPort))
			if err != nil {
				log.Println("Couldn't create torrent session for "+torrentFile+" .", err)
				doneChan <- &TorrentSession{}
			} else {
				log.Printf("Created torrent session for %s", ts.M.Info.Name)
				startChan <- ts
			}
		}
	}()

	torrentQueue := []string{}
	if len(torrentFiles) > flags.MaxActive {
		torrentQueue = torrentFiles[flags.MaxActive:]
	}

	for i, torrentFile := range torrentFiles {
		if i < flags.MaxActive {
			createChan <- torrentFile
		} else {
			break
		}
	}

	lpd := &Announcer{}
	if flags.UseLPD {
		lpd, err = NewAnnouncer(uint16(listenPort))
		if err != nil {
			log.Println("Couldn't listen for Local Peer Discoveries: ", err)
			flags.UseLPD = false
		}
	}

	theWorldisEnding := false
mainLoop:
	for {
		select {
		case ts := <-startChan:
			if !theWorldisEnding {
				ts.dht = &dhtNode
				if flags.UseLPD {
					lpd.Announce(ts.M.InfoHash)
				}
				torrentSessions[ts.M.InfoHash] = ts
				log.Printf("Starting torrent session for %s", ts.M.Info.Name)
				go func(t *TorrentSession) {
					t.DoTorrent()
					doneChan <- t
				}(ts)
			}
		case ts := <-doneChan:
			if ts.M != nil {
				delete(torrentSessions, ts.M.InfoHash)
				if flags.UseLPD {
					lpd.StopAnnouncing(ts.M.InfoHash)
				}
			}
			if !theWorldisEnding && len(torrentQueue) > 0 {
				createChan <- torrentQueue[0]
				torrentQueue = torrentQueue[1:]
				continue mainLoop
			}

			if len(torrentSessions) == 0 {
				break mainLoop
			}
		case <-quitChan:
			theWorldisEnding = true
			for _, ts := range torrentSessions {
				go ts.Quit()
			}
		case c := <-conChan:
			//	log.Printf("New bt connection for ih %x", c.Infohash)
			if ts, ok := torrentSessions[c.Infohash]; ok {
				ts.AcceptNewPeer(c)
			}
		case dhtPeers := <-dhtNode.PeersRequestResults:
			for key, peers := range dhtPeers {
				if ts, ok := torrentSessions[string(key)]; ok {
					// log.Printf("Received %d DHT peers for torrent session %x\n", len(peers), []byte(key))
					for _, peer := range peers {
						peer = dht.DecodePeerAddress(peer)
						ts.HintNewPeer(peer)
					}
				} else {
					log.Printf("Received DHT peer for an unknown torrent session %x\n", []byte(key))
				}
			}
		case announce := <-lpd.Announces:
			hexhash, err := hex.DecodeString(announce.Infohash)
			if err != nil {
				log.Println("Err with hex-decoding:", err)
			}
			if ts, ok := torrentSessions[string(hexhash)]; ok {
				// log.Printf("Received LPD announce for ih %s", announce.Infohash)
				ts.HintNewPeer(announce.Peer)
			}
		}
	}
	if flags.UseDHT {
		dhtNode.Stop()
	}
	return
}
コード例 #12
0
ファイル: torrent.go プロジェクト: screscent/Taipei-Torrent
func (t *TorrentSession) DoTorrent() (err error) {
	t.lastHeartBeat = time.Now()
	go t.deadlockDetector()
	log.Println("Fetching torrent.")
	rechokeChan := time.Tick(1 * time.Second)
	// Start out polling tracker every 20 seconds untill we get a response.
	// Maybe be exponential backoff here?
	retrackerChan := time.Tick(20 * time.Second)
	keepAliveChan := time.Tick(60 * time.Second)
	t.trackerInfoChan = make(chan *TrackerResponse)

	conChan := make(chan net.Conn)
	if useProxy() {
		// Only listen for peer connections if not using a proxy.
		t.listenForPeerConnections(conChan)
	}

	if t.m.Info.Private != 1 && useDHT {
		t.dht.PeersRequest(t.m.InfoHash, true)
	}

	t.fetchTrackerInfo("started")

	for {
		select {
		case _ = <-retrackerChan:
			if !trackerLessMode {
				t.fetchTrackerInfo("")
			}
		case dhtInfoHashPeers := <-t.dht.PeersRequestResults:
			newPeerCount := 0
			// key = infoHash. The torrent client currently only
			// supports one download at a time, so let's assume
			// it's the case.
			for _, peers := range dhtInfoHashPeers {
				for _, peer := range peers {
					peer = dht.DecodePeerAddress(peer)
					if _, ok := t.peers[peer]; !ok {
						newPeerCount++
						go connectToPeer(peer, conChan)
					}
				}
			}
			// log.Println("Contacting", newPeerCount, "new peers (thanks DHT!)")
		case ti := <-t.trackerInfoChan:
			t.ti = ti
			log.Println("Torrent has", t.ti.Complete, "seeders and", t.ti.Incomplete, "leachers.")
			if !trackerLessMode {
				peers := t.ti.Peers
				log.Println("Tracker gave us", len(peers)/6, "peers")
				newPeerCount := 0
				for i := 0; i < len(peers); i += 6 {
					peer := nettools.BinaryToDottedPort(peers[i : i+6])
					if _, ok := t.peers[peer]; !ok {
						newPeerCount++
						go connectToPeer(peer, conChan)
					}
				}
				log.Println("Contacting", newPeerCount, "new peers")
				interval := t.ti.Interval
				if interval < 120 {
					interval = 120
				} else if interval > 24*3600 {
					interval = 24 * 3600
				}
				log.Println("..checking again in", interval, "seconds.")
				retrackerChan = time.Tick(interval * time.Second)
				log.Println("Contacting", newPeerCount, "new peers")
			}
			interval := t.ti.Interval
			if interval < 120 {
				interval = 120
			} else if interval > 24*3600 {
				interval = 24 * 3600
			}
			log.Println("..checking again in", interval.String())
			retrackerChan = time.Tick(interval * time.Second)

		case pm := <-t.peerMessageChan:
			peer, message := pm.peer, pm.message
			peer.lastReadTime = time.Now()
			err2 := t.DoMessage(peer, message)
			if err2 != nil {
				if err2 != io.EOF {
					log.Println("Closing peer", peer.address, "because", err2)
				}
				t.ClosePeer(peer)
			}
		case conn := <-conChan:
			t.AddPeer(conn)
		case _ = <-rechokeChan:
			// TODO: recalculate who to choke / unchoke
			t.lastHeartBeat = time.Now()
			ratio := float64(0.0)
			if t.si.Downloaded > 0 {
				ratio = float64(t.si.Uploaded) / float64(t.si.Downloaded)
			}
			log.Println("Peers:", len(t.peers), "downloaded:", t.si.Downloaded,
				"uploaded:", t.si.Uploaded, "ratio", ratio)
			log.Println("good, total", t.goodPieces, t.totalPieces)
			if len(t.peers) < TARGET_NUM_PEERS && t.goodPieces < t.totalPieces {
				if t.m.Info.Private != 1 && useDHT {
					go t.dht.PeersRequest(t.m.InfoHash, true)
				}
				if !trackerLessMode {
					if t.ti == nil || t.ti.Complete > 100 {
						t.fetchTrackerInfo("")
					}
				}
			}
		case _ = <-keepAliveChan:
			now := time.Now()
			for _, peer := range t.peers {
				if peer.lastReadTime.Second() != 0 && now.Sub(peer.lastReadTime) > 3*time.Minute {
					// log.Println("Closing peer", peer.address, "because timed out.")
					t.ClosePeer(peer)
					continue
				}
				err2 := t.doCheckRequests(peer)
				if err2 != nil {
					if err2 != io.EOF {
						log.Println("Closing peer", peer.address, "because", err2)
					}
					t.ClosePeer(peer)
					continue
				}
				peer.keepAlive(now)
			}
		}
	}
	return
}
コード例 #13
0
ファイル: torrent.go プロジェクト: postfix/Taipei-Torrent
func (t *TorrentSession) DoTorrent() {
	t.heartbeat = make(chan bool, 1)
	if t.flags.UseDeadlockDetector {
		go t.deadlockDetector()
	}
	log.Println("Fetching torrent.")
	heartbeatChan := time.Tick(1 * time.Second)
	keepAliveChan := time.Tick(60 * time.Second)
	var retrackerChan <-chan time.Time
	t.hintNewPeerChan = make(chan string)
	t.addPeerChan = make(chan *btConn)
	if !t.trackerLessMode {
		// Start out polling tracker every 20 seconds until we get a response.
		// Maybe be exponential backoff here?
		retrackerChan = time.Tick(20 * time.Second)
		t.trackerInfoChan = make(chan *TrackerResponse)
		t.trackerReportChan = make(chan ClientStatusReport)
		startTrackerClient(t.flags.Dial, t.M.Announce, t.M.AnnounceList, t.trackerInfoChan, t.trackerReportChan)
	}

	if t.si.UseDHT {
		t.dht.PeersRequest(t.M.InfoHash, true)
	}

	if !t.trackerLessMode && t.si.HaveTorrent {
		t.fetchTrackerInfo("started")
	}

	defer t.Shutdown()

	for {
		var peersRequestResults chan map[dht.InfoHash][]string
		peersRequestResults = nil
		if t.dht != nil {
			peersRequestResults = t.dht.PeersRequestResults
		}
		select {
		case <-t.chokePolicyHeartbeat:
			t.chokePeers()
		case hintNewPeer := <-t.hintNewPeerChan:
			t.hintNewPeerImp(hintNewPeer)
		case btconn := <-t.addPeerChan:
			t.addPeerImp(btconn)
		case <-retrackerChan:
			if !t.trackerLessMode {
				t.fetchTrackerInfo("")
			}
		case dhtInfoHashPeers := <-peersRequestResults:
			newPeerCount := 0
			// key = infoHash. The torrent client currently only
			// supports one download at a time, so let's assume
			// it's the case.
			for _, peers := range dhtInfoHashPeers {
				for _, peer := range peers {
					peer = dht.DecodePeerAddress(peer)
					if t.mightAcceptPeer(peer) {
						newPeerCount++
						if t.si.HaveTorrent {
							go t.connectToPeer(peer)
						}
					}
				}
			}
			// log.Println("Contacting", newPeerCount, "new peers (thanks DHT!)")
		case ti := <-t.trackerInfoChan:
			t.ti = ti
			log.Println("Torrent has", t.ti.Complete, "seeders and", t.ti.Incomplete, "leachers.")
			if !t.trackerLessMode {
				newPeerCount := 0
				{
					peers := t.ti.Peers
					if len(peers) > 0 {
						const peerLen = 6
						log.Println("Tracker gave us", len(peers)/peerLen, "peers")
						for i := 0; i < len(peers); i += peerLen {
							peer := nettools.BinaryToDottedPort(peers[i : i+peerLen])
							if t.mightAcceptPeer(peer) {
								newPeerCount++
								go t.connectToPeer(peer)
							}
						}
					}
				}
				{
					peers6 := t.ti.Peers6
					if len(peers6) > 0 {
						const peerLen = 18
						log.Println("Tracker gave us", len(peers6)/peerLen, "IPv6 peers")
						for i := 0; i < len(peers6); i += peerLen {
							peerEntry := peers6[i : i+peerLen]
							host := net.IP(peerEntry[0:16])
							port := int((uint(peerEntry[16]) << 8) | uint(peerEntry[17]))
							peer := net.JoinHostPort(host.String(), strconv.Itoa(port))
							if t.mightAcceptPeer(peer) {
								newPeerCount++
								go t.connectToPeer(peer)
							}
						}
					}
				}
				log.Println("Contacting", newPeerCount, "new peers")
			}

			interval := t.ti.Interval
			minInterval := uint(120)
			maxInterval := uint(24 * 3600)
			if interval < minInterval {
				interval = minInterval
			} else if interval > maxInterval {
				interval = maxInterval
			}
			log.Println("..checking again in", interval, "seconds.")
			retrackerChan = time.Tick(time.Duration(interval) * time.Second)

		case pm := <-t.peerMessageChan:
			peer, message := pm.peer, pm.message
			peer.lastReadTime = time.Now()
			err2 := t.DoMessage(peer, message)
			if err2 != nil {
				if err2 != io.EOF {
					log.Println("Closing peer", peer.address, "because", err2)
				}
				t.ClosePeer(peer)
			}
		case <-heartbeatChan:
			if t.flags.UseDeadlockDetector {
				t.heartbeat <- true
			}
			ratio := float64(0.0)
			if t.si.Downloaded > 0 {
				ratio = float64(t.si.Uploaded) / float64(t.si.Downloaded)
			}
			log.Println("Peers:", len(t.peers), "downloaded:", t.si.Downloaded,
				"uploaded:", t.si.Uploaded, "ratio", ratio)
			log.Println("good, total", t.goodPieces, t.totalPieces)
			if t.goodPieces == t.totalPieces && ratio >= t.flags.SeedRatio {
				log.Println("Achieved target seed ratio", t.flags.SeedRatio)
				return
			}
			if len(t.peers) < TARGET_NUM_PEERS && (t.totalPieces == 0 || t.goodPieces < t.totalPieces) {
				if t.si.UseDHT {
					go t.dht.PeersRequest(t.M.InfoHash, true)
				}
				if !t.trackerLessMode {
					if t.ti == nil || t.ti.Complete > 100 {
						t.fetchTrackerInfo("")
					}
				}
			}
		case <-keepAliveChan:
			now := time.Now()
			for _, peer := range t.peers {
				if peer.lastReadTime.Second() != 0 && now.Sub(peer.lastReadTime) > 3*time.Minute {
					// log.Println("Closing peer", peer.address, "because timed out.")
					t.ClosePeer(peer)
					continue
				}
				err2 := t.doCheckRequests(peer)
				if err2 != nil {
					if err2 != io.EOF {
						log.Println("Closing peer", peer.address, "because", err2)
					}
					t.ClosePeer(peer)
					continue
				}
				peer.keepAlive(now)
			}

		case <-t.quit:
			log.Println("Quitting torrent session")
			return
		}
	}
}
コード例 #14
0
ファイル: control.go プロジェクト: rakoo/rakoshare
func (cs *ControlSession) Run() {
	// deadlock
	heartbeat := make(chan struct{}, 1)
	quitDeadlock := make(chan struct{})
	go cs.deadlockDetector(heartbeat, quitDeadlock)

	rechokeChan := time.Tick(10 * time.Second)
	verboseChan := time.Tick(10 * time.Minute)
	keepAliveChan := time.Tick(60 * time.Second)

	// Start out polling tracker every 20 seconds until we get a response.
	// Maybe be exponential backoff here?
	retrackerChan := time.Tick(20 * time.Second)
	trackerInfoChan := make(chan *TrackerResponse)

	trackerClient := NewTrackerClient("", [][]string{cs.trackers})
	trackerClient.Announce(cs.makeClientStatusReport("started"))

	for {
		select {
		case <-retrackerChan:
			trackerClient.Announce(cs.makeClientStatusReport(""))
		case dhtInfoHashPeers := <-cs.dht.PeersRequestResults:
			newPeerCount := 0
			// key = infoHash. The torrent client currently only
			// supports one download at a time, so let's assume
			// it's the case.
			for _, peers := range dhtInfoHashPeers {
				for _, peer := range peers {
					peer = dht.DecodePeerAddress(peer)
					if cs.hintNewPeer(peer) {
						newPeerCount++
					}
				}
			}
		case ti := <-trackerInfoChan:
			cs.logf("Got response from tracker: %#v\n", ti)
			newPeerCount := 0
			for _, peer := range ti.Peers {
				if cs.hintNewPeer(peer) {
					newPeerCount++
				}
			}
			for _, peer6 := range ti.Peers6 {
				if cs.hintNewPeer(peer6) {
					newPeerCount++
				}
			}

			cs.log("Contacting", newPeerCount, "new peers")
			interval := ti.Interval
			if interval < 120 {
				interval = 120
			} else if interval > 24*3600 {
				interval = 24 * 3600
			}
			cs.log("..checking again in", interval, "seconds.")
			retrackerChan = time.Tick(interval * time.Second)
			cs.log("Contacting", newPeerCount, "new peers")

		case pm := <-cs.peerMessageChan:
			peer, message := pm.peer, pm.message
			peer.lastReadTime = time.Now()
			err2 := cs.DoMessage(peer, message)
			if err2 != nil {
				if err2 != io.EOF {
					cs.log("Closing peer", peer.address, "because", err2)
				}
				cs.ClosePeer(peer)
			}
		case <-rechokeChan:
			// TODO: recalculate who to choke / unchoke
			heartbeat <- struct{}{}
			if cs.peers.Len() < TARGET_NUM_PEERS {
				go cs.dht.PeersRequest(string(cs.ID.Infohash), true)
			}
		case <-verboseChan:
			cs.log("Peers:", cs.peers.Len())
		case <-keepAliveChan:
			now := time.Now()

			for _, peer := range cs.peers.All() {
				if peer.lastReadTime.Second() != 0 && now.Sub(peer.lastReadTime) > 3*time.Minute {
					// log.Println("Closing peer", peer.address, "because timed out.")
					cs.ClosePeer(peer)
					continue
				}
				go peer.keepAlive(now)
			}

		case <-cs.quit:
			cs.log("Quitting torrent session")
			quitDeadlock <- struct{}{}
			return
		}
	}

}