'Downloads a file from a remote host.
:param file_name: The file name of the file to be downloaded.
:param ip:        The IP of the remote host.
:param port:      The port of the remote host.
:param success_callback: The success callback.
:param failure_callback: The failure callback.'

@call_on_reactor_thread
def download_file(self, file_name, ip, port, extra_info=None, success_callback=None, failure_callback=None):

    if (not self._is_running):
        return
    target_ip = unpack('!L', inet_aton(ip))[0]
    target_port = port
    (self_ip, self_port) = self.session.lm.dispersy.wan_address
    self_ip = unpack('!L', inet_aton(self_ip))[0]
    if (target_ip > self_ip):
        generate_session = (lambda : (randint(0, MAX_INT16) & 65520))
    elif (target_ip < self_ip):
        generate_session = (lambda : (randint(0, MAX_INT16) | 1))
    elif (target_port > self_port):
        generate_session = (lambda : (randint(0, MAX_INT16) & 65520))
    elif (target_port < self_port):
        generate_session = (lambda : (randint(0, MAX_INT16) | 1))
    else:
        self._logger.critical(u'communicating    to    myself    %s:%s', ip, port)
        generate_session = (lambda : randint(0, MAX_INT16))
    session_id = generate_session()
    while ((ip, port, session_id) in self._session_dict):
        session_id = generate_session()
    assert (session_id is not None), (u'session_id    =    %s' % session_id)
    self._logger.debug(u'start    downloading    %s    from    %s:%s,    sid    =    %s', file_name, ip, port, session_id)
    session = Session(True, session_id, (ip, port), OPCODE_RRQ, file_name, '', None, None, extra_info=extra_info, block_size=self._block_size, timeout=self._timeout, success_callback=success_callback, failure_callback=failure_callback)
    self._add_new_session(session)
    self._send_request_packet(session)
    self._logger.info(u'%s    started', session)
