version 0.2.4, peerPing and peerGetFile commands, old content update bugfix, new network code and protocol, connection share between sites, connection reuse, dont retry bad file more than 3 times in 20 min, multi threaded include file download, shuffle peers before publish, simple internal stats page, dont retry on failed peers, more than 10 peers publish bugfix

This commit is contained in:
HelloZeroNet 2015-02-23 23:33:31 +01:00
parent 531bf68ddd
commit 31d4609a3b
18 changed files with 790 additions and 130 deletions

View file

@ -1,21 +1,20 @@
import os, logging, gevent, time, msgpack
import os, logging, gevent, time, msgpack, sys
import zmq.green as zmq
from cStringIO import StringIO
from Config import config
from Debug import Debug
context = zmq.Context()
# Communicate remote peers
class Peer:
def __init__(self, ip, port, site):
def __init__(self, ip, port, site=None):
self.ip = ip
self.port = port
self.site = site
self.key = "%s:%s" % (ip, port)
self.log = None
self.connection_server = sys.modules["src.main"].file_server
self.socket = None
self.connection = None
self.last_found = None # Time of last found in the torrent tracker
self.last_response = None # Time of last successfull response from peer
self.last_ping = None # Last response time for ping
@ -29,19 +28,22 @@ class Peer:
# Connect to host
def connect(self):
if self.connection: self.connection.close()
self.connection = None
if not self.log: self.log = logging.getLogger("Peer:%s:%s" % (self.ip, self.port))
if self.socket: self.socket.close()
self.socket = context.socket(zmq.REQ)
self.socket.setsockopt(zmq.RCVTIMEO, 50000) # Wait for data arrive
self.socket.setsockopt(zmq.SNDTIMEO, 5000) # Wait for data send
self.socket.setsockopt(zmq.LINGER, 500) # Wait for socket close
# self.socket.setsockopt(zmq.TCP_KEEPALIVE, 1) # Enable keepalive
# self.socket.setsockopt(zmq.TCP_KEEPALIVE_IDLE, 4*60) # Send after 4 minute idle
# self.socket.setsockopt(zmq.TCP_KEEPALIVE_INTVL, 15) # Wait 15 sec to response
# self.socket.setsockopt(zmq.TCP_KEEPALIVE_CNT, 4) # 4 Probes
self.socket.connect('tcp://%s:%s' % (self.ip, self.port))
self.log.debug("Connecting...")
try:
self.connection = self.connection_server.connect(self.ip, self.port)
except Exception, err:
self.log.debug("Connecting error: %s" % Debug.formatException(err))
self.onConnectionError()
def __str__(self):
return "Peer %-12s" % self.ip
def __repr__(self):
return "<%s>" % self.__str__()
# Found a peer on tracker
def found(self):
@ -49,18 +51,20 @@ class Peer:
# Send a command to peer
def sendCmd(self, cmd, params = {}):
if not self.socket: self.connect()
def request(self, cmd, params = {}):
if not self.connection or self.connection.closed:
self.connect()
if not self.connection: return None # Connection failed
if cmd != "ping" and self.last_response and time.time() - self.last_response > 20*60: # If last response if older than 20 minute, ping first to see if still alive
if not self.ping(): return None
for retry in range(1,3): # Retry 3 times
if config.debug_socket: self.log.debug("sendCmd: %s %s" % (cmd, params.get("inner_path")))
#if config.debug_socket: self.log.debug("sendCmd: %s %s" % (cmd, params.get("inner_path")))
try:
self.socket.send(msgpack.packb({"cmd": cmd, "params": params}, use_bin_type=True))
if config.debug_socket: self.log.debug("Sent command: %s" % cmd)
response = msgpack.unpackb(self.socket.recv())
if config.debug_socket: self.log.debug("Got response to: %s" % cmd)
response = self.connection.request(cmd, params)
if not response: raise Exception("Send error")
#if config.debug_socket: self.log.debug("Got response to: %s" % cmd)
if "error" in response:
self.log.debug("%s error: %s" % (cmd, response["error"]))
self.onConnectionError()
@ -69,13 +73,14 @@ class Peer:
self.last_response = time.time()
return response
except Exception, err:
self.onConnectionError()
self.log.debug("%s (connection_error: %s, hash_failed: %s, retry: %s)" % (Debug.formatException(err), self.connection_error, self.hash_failed, retry))
time.sleep(1*retry)
self.connect()
if type(err).__name__ == "Notify" and err.message == "Worker stopped": # Greenlet kill by worker
self.log.debug("Peer worker got killed, aborting cmd: %s" % cmd)
if type(err).__name__ == "Notify": # Greenlet kill by worker
self.log.debug("Peer worker got killed: %s, aborting cmd: %s" % (err.message, cmd))
break
else:
self.onConnectionError()
self.log.debug("%s (connection_error: %s, hash_failed: %s, retry: %s)" % (Debug.formatException(err), self.connection_error, self.hash_failed, retry))
time.sleep(1*retry)
self.connect()
return None # Failed after 4 retry
@ -85,7 +90,7 @@ class Peer:
buff = StringIO()
s = time.time()
while 1: # Read in 512k parts
back = self.sendCmd("getFile", {"site": site, "inner_path": inner_path, "location": location}) # Get file content from last location
back = self.request("getFile", {"site": site, "inner_path": inner_path, "location": location}) # Get file content from last location
if not back or "body" not in back: # Error
return False
@ -106,7 +111,8 @@ class Peer:
for retry in range(1,3): # Retry 3 times
s = time.time()
with gevent.Timeout(10.0, False): # 10 sec timeout, dont raise exception
response = self.sendCmd("ping")
response = self.request("ping")
if response and "body" in response and response["body"] == "Pong!":
response_time = time.time()-s
break # All fine, exit from for loop
@ -126,7 +132,8 @@ class Peer:
def remove(self):
self.log.debug("Removing peer...Connection error: %s, Hash failed: %s" % (self.connection_error, self.hash_failed))
if self.key in self.site.peers: del(self.site.peers[self.key])
self.socket.close()
if self.connection:
self.connection.close()
# - EVENTS -