android 实现FTPServices 文件传输,支持IPV6

import java.net.InetAddress;

import com.base.module.util.SessionThread;

import android.util.Log;
/**
*这个类 跟 PASV 模式的类中基本一样,只是StringBuilder 中的内容不同。可以去下载完整的FTP 源码看一下CmdPASV
*类 当然还需要在FtpCmd 类中 protected static CmdMap[] cmdClasses  中加上new CmdMap("EPSV", CmdEPSV.class)
**/


public class CmdEPSV extends FtpCmd implements Runnable {
    //public static final String message = "TEMPLATE!!";
    String input;

    public CmdEPSV(SessionThread sessionThread, String input) {
        super(sessionThread, CmdEPSV.class.toString());
        this.input = input;
    }
    public CmdEPSV() {
        super(null, CmdEPSV.class.toString());
    }
    public void run() {
        myLog.l(Log.DEBUG, "EPSV running");
        String cantOpen = "502 Couldn't open a port\r\n";
        int port;
        if((port = sessionThread.onPasv()) == 0) {
            // There was a problem opening a port
            myLog.l(Log.ERROR, "Couldn't open a port for EPSV");
            sessionThread.writeString(cantOpen);
            return;
        }
        InetAddress addr = sessionThread.getDataSocketPasvIp();

        if(addr == null) {
            myLog.l(Log.ERROR, "EPSV IP string invalid");
            sessionThread.writeString(cantOpen);
            return;
        }
        myLog.d("EPSV sending IP: " + addr.getHostAddress());
        if(port < 1) {
            myLog.l(Log.ERROR, "EPSV port number invalid");
            sessionThread.writeString(cantOpen);
            return;
        }
        //上面一堆的判断只是为了确认地址是否有效等,下面才是关键
        //最主要的就是   "229 Entering Extended Passive Mode (|||" 这句,具体网络协议我也不是很懂,
        //但是自己参考 http://my.oschina.net/u/572632/blog/283460 

        StringBuilder response = new StringBuilder(
                "229 Entering Extended Passive Mode (|||");
        response.append(port);
        response.append("|).\r\n");
        String responseString = response.toString();
        sessionThread.writeString(responseString);
        myLog.l(Log.DEBUG, "EPSV completed, sent: " + responseString);
    }
}

主要参考了下面网址,

http://www.codeweblog.com/400%E5%A4%9A%E8%A1%8Cpython%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0%E4%BA%86%E4%B8%80%E4%B8%AAftp%E6%9C%8D%E5%8A%A1%E5%99%A8/

看完上面链接以后再回顾一下

http://my.oschina.net/u/572632/blog/283460

的讲解,基本能理通一些知识了,深入研究还需要时间。。。

下面是上面第一个链接里的代码,可以搜索 EPSV 的书写方式,在我们JAVA 里尝试使用,还真的行的通,所以代码语言都是相通的,要大胆尝试。。o(∩∩)o...哈哈  下面代码中的判断之后就写了

if cmd == "EPSV":
self.message(229, "Entering Extended Passive Mode (|||" + str(port) + "|)")


import socket, threading, os, sys, timeimport hashlib, platform, stat 

listen_ip = "localhost"listen_port = 21conn_list = []
root_dir = "./home"max_connections = 500conn_timeout = 120 class FtpConnection(threading.Thread):def __init__(self, fd):threading.Thread.__init__(self)
self.fd = fd
self.running = Trueself.setDaemon(True)
self.alive_time = time.time()
self.option_utf8 = Falseself.identified = Falseself.option_pasv = Trueself.username = ""def process(self, cmd, arg):cmd = cmd.upper();if self.option_utf8:
arg = unicode(arg, "utf8").encode(sys.getfilesystemencoding())print "<<", cmd, arg, self.fd# Ftp Commandif cmd == "BYE" or cmd == "QUIT":if os.path.exists(root_dir + "/xxftp.goodbye"):
self.message(221, open(root_dir + "/xxftp.goodbye").read())else:
self.message(221, "Bye!")
self.running = Falsereturnelif cmd == "USER":# Set Anonymous Userif arg == "": arg = "anonymous"for c in arg:if not c.isalpha() and not c.isdigit() and c!="_":
self.message(530, "Incorrect username.")returnself.username = arg
self.home_dir = root_dir + "/" + self.username
self.curr_dir = "/"self.curr_dir, self.full_path, permission, self.vdir_list, \
limit_size, is_virtual = self.parse_path("/")if not os.path.isdir(self.home_dir):
self.message(530, "User " + self.username + " not exists.")returnself.pass_path = self.home_dir + "/.xxftp/password"if os.path.isfile(self.pass_path):
self.message(331, "Password required for " + self.username)else:
self.message(230, "Identified!")
self.identified = Truereturnelif cmd == "PASS":if open(self.pass_path).read() == hashlib.md5(arg).hexdigest():
self.message(230, "Identified!")
self.identified = Trueelse:
self.message(530, "Not identified!")
self.identified = Falsereturnelif not self.identified:
self.message(530, "Please login with USER and PASS.")return self.alive_time = time.time()
finish = Trueif cmd == "NOOP":
self.message(200, "ok")elif cmd == "TYPE":
self.message(200, "ok")elif cmd == "SYST":
self.message(200, "UNIX")elif cmd == "EPSV" or cmd == "PASV":
self.option_pasv = Truetry:
self.data_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.data_fd.bind((listen_ip, 0))
self.data_fd.listen(1)
ip, port = self.data_fd.getsockname()if cmd == "EPSV":
self.message(229, "Entering Extended Passive Mode (|||" + str(port) + "|)")else:
ipnum = socket.inet_aton(ip)
self.message(227, "Entering Passive Mode (%s,%u,%u)." %
(",".join(ip.split(".")), (port>>8&0xff), (port&0xff)))except:
self.message(500, "failed to create data socket.")elif cmd == "EPRT":
self.message(500, "implement EPRT later...")elif cmd == "PORT":
self.option_pasv = Falseself.data_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = arg.split(",")
self.data_ip = ".".join(s[:4])
self.data_port = int(s[4])*256 + int(s[5])
self.message(200, "ok")elif cmd == "PWD" or cmd == "XPWD":if self.curr_dir == "": self.curr_dir = "/"self.message(257, '"' + self.curr_dir + '"')elif cmd == "LIST" or cmd == "NLST":if arg != "" and arg[0] == "-": arg = "" # omit parametersremote, local, perm, vdir_list, limit_size, is_virtual = self.parse_path(arg)if not os.path.exists(local):
self.message(550, "failed.")returnif not self.establish(): returnself.message(150, "ok")for v in vdir_list:
f = v[0]if self.option_utf8:
f = unicode(f, sys.getfilesystemencoding()).encode("utf8")if cmd == "NLST":
info = f + "\r\n"else:
info = "d%s%s------- %04u %8s %8s %8lu %s %s\r\n" % ("r" if "read" in perm else "-","w" if "write" in perm else "-",1, "0", "0", 0,
time.strftime("%b %d %Y", time.localtime(time.time())),
f)
self.data_fd.send(info)for f in os.listdir(local):if f[0] == ".": continuepath = local + "/" + fif self.option_utf8:
f = unicode(f, sys.getfilesystemencoding()).encode("utf8")if cmd == "NLST":
info = f + "\r\n"else:
st = os.stat(path)
info = "%s%s%s------- %04u %8s %8s %8lu %s %s\r\n" % ("-" if os.path.isfile(path) else "d","r" if "read" in perm else "-","w" if "write" in perm else "-",1, "0", "0", st[stat.ST_SIZE],
time.strftime("%b %d %Y", time.localtime(st[stat.ST_MTIME])),
f)
self.data_fd.send(info)
self.message(226, "Limit size: " + str(limit_size))
self.data_fd.close()
self.data_fd = 0elif cmd == "REST":
self.file_pos = int(arg)
self.message(250, "ok")elif cmd == "FEAT":
features = "211-Features:\r\nSITES\r\nEPRT\r\nEPSV\r\nMDTM\r\nPASV\r\n"\"REST STREAM\r\nSIZE\r\nUTF8\r\n211 End\r\n"self.fd.send(features)elif cmd == "OPTS":
arg = arg.upper()if arg == "UTF8 ON":
self.option_utf8 = Trueself.message(200, "ok")elif arg == "UTF8 OFF":
self.option_utf8 = Falseself.message(200, "ok")else:
self.message(500, "unrecognized option")elif cmd == "CDUP":
finish = Falsearg = ".."else:
finish = Falseif finish: return# Parse argument ( It's a path )if arg == "":
self.message(500, "where's my argument?")returnremote, local, permission, vdir_list, limit_size, is_virtual = \
self.parse_path(arg)# can not do anything to virtual directoryif is_virtual: permission = "none"can_read, can_write, can_modify = "read" in permission, "write" in permission, "modify" in permission
newpath = localtry:if cmd == "CWD":if(os.path.isdir(newpath)):
self.curr_dir = remote
self.full_path = newpath
self.message(250, '"' + remote + '"')else:
self.message(550, "failed")elif cmd == "MDTM":if os.path.exists(newpath):
self.message(213, time.strftime("%Y%m%d%I%M%S", time.localtime(
os.path.getmtime(newpath))))else:
self.message(550, "failed")elif cmd == "SIZE":
self.message(231, os.path.getsize(newpath))elif cmd == "XMKD" or cmd == "MKD":if not can_modify:
self.message(550, "permission denied.")returnos.mkdir(newpath)
self.message(250, "ok")elif cmd == "RNFR":if not can_modify:
self.message(550, "permission denied.")returnself.temp_path = newpath
self.message(350, "rename from " + remote)elif cmd == "RNTO":
os.rename(self.temp_path, newpath)
self.message(250, "RNTO to " + remote)elif cmd == "XRMD" or cmd == "RMD":if not can_modify:
self.message(550, "permission denied.")returnos.rmdir(newpath)
self.message(250, "ok")elif cmd == "DELE":if not can_modify:
self.message(550, "permission denied.")returnos.remove(newpath)
self.message(250, "ok")elif cmd == "RETR":if not os.path.isfile(newpath):
self.message(550, "failed")returnif not can_read:
self.message(550, "permission denied.")returnif not self.establish(): returnself.message(150, "ok")
f = open(newpath, "rb")while self.running:
self.alive_time = time.time()
data = f.read(8192)if len(data) == 0: breakself.data_fd.send(data)
f.close()
self.data_fd.close()
self.data_fd = 0self.message(226, "ok")elif cmd == "STOR" or cmd == "APPE":if not can_write:
self.message(550, "permission denied.")returnif os.path.exists(newpath) and not can_modify:
self.message(550, "permission denied.")return# Check space size remained!used_size = 0if limit_size > 0:
used_size = self.get_dir_size(os.path.dirname(newpath))if not self.establish(): returnself.message(150, "ok")
f = open(newpath, ("ab" if cmd == "APPE" else "wb") )while self.running:
self.alive_time = time.time()
data = self.data_fd.recv(8192)if len(data) == 0: breakif limit_size > 0:
used_size = used_size + len(data)if used_size > limit_size: breakf.write(data)
f.close()
self.data_fd.close()
self.data_fd = 0if limit_size > 0 and used_size > limit_size:
self.message(550, "Exceeding user space limit: " + str(limit_size) + " bytes")else:
self.message(226, "ok")else:
self.message(500, cmd + " not implemented")except:
self.message(550, "failed.") 

def establish(self):if self.data_fd == 0:
self.message(500, "no data connection")return Falseif self.option_pasv:
fd = self.data_fd.accept()[0]
self.data_fd.close()
self.data_fd = fdelse:try:
self.data_fd.connect((self.data_ip, self.data_port))except:
self.message(500, "failed to establish data connection")return Falsereturn True def read_virtual(self, path):vdir_list = []
path = path + "/.xxftp/virtual"if os.path.isfile(path):for v in open(path, "r").readlines():
items = v.split()
items[1] = items[1].replace("$root", root_dir)
vdir_list.append(items)return vdir_list 

def get_dir_size(self, folder):size = 0for path, dirs, files in os.walk(folder):for f in files:
size += os.path.getsize(os.path.join(path, f))return size 

def read_size(self, path):size = 0path = path + "/.xxftp/size"if os.path.isfile(path):
size = int(open(path, "r").readline())return size 

def read_permission(self, path):permission = "read,write,modify"path = path + "/.xxftp/permission"if os.path.isfile(path):
permission = open(path, "r").readline()return permission 

def parse_path(self, path):if path == "": path = "."if path[0] != "/":
path = self.curr_dir + "/" + path
s = os.path.normpath(path).replace("\\", "/").split("/")
local = self.home_dir# reset directory permissionvdir_list = self.read_virtual(local)
limit_size = self.read_size(local)
permission = self.read_permission(local)
remote = ""is_virtual = Falsefor name in s:
name = name.lstrip(".")if name == "": continueremote = remote + "/" + name
is_virtual = Falsefor v in vdir_list:if v[0] == name:
permission = v[2]
local = v[1]
limit_size = self.read_size(local)
is_virtual = Trueif not is_virtual: local = local + "/" + name
vdir_list = self.read_virtual(local)return (remote, local, permission, vdir_list, limit_size, is_virtual) 

def run(self):''' Connection Process '''try:if len(conn_list) > max_connections:
self.message(500, "too many connections!")
self.fd.close()
self.running = Falsereturn# Welcome Messageif os.path.exists(root_dir + "/xxftp.welcome"):
self.message(220, open(root_dir + "/xxftp.welcome").read())else:
self.message(220, "xxftp(Python) www.xiaoxia.org")# Command Loopline = ""while self.running:
data = self.fd.recv(4096)if len(data) == 0: breakline += dataif line[-2:] != "\r\n": continueline = line[:-2]
space = line.find(" ")if space == -1:
self.process(line, "")else:
self.process(line[:space], line[space+1:])
line = ""except:print "error", sys.exc_info()
self.running = Falseself.fd.close()print "connection end", self.fd, "user", self.username 

def message(self, code, s):''' Send Ftp Message '''s = str(s).replace("\r", "")
ss = s.split("\n")if len(ss) > 1:
r = (str(code) + "-") + ("\r\n" + str(code) + "-").join(ss[:-1])
r += "\r\n" + str(code) + " " + ss[-1] + "\r\n"else:
r = str(code) + " " + ss[0] + "\r\n"if self.option_utf8:
r = unicode(r, sys.getfilesystemencoding()).encode("utf8")
self.fd.send(r) 

def server_listen():global conn_list
listen_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_fd.bind((listen_ip, listen_port))
listen_fd.listen(1024)
conn_lock = threading.Lock()print "ftpd is listening on ", listen_ip + ":" + str(listen_port) 

while True:
conn_fd, remote_addr = listen_fd.accept()print "connection from ", remote_addr, "conn_list", len(conn_list)
conn = FtpConnection(conn_fd)
conn.start() 

conn_lock.acquire()
conn_list.append(conn)# check timeouttry:
curr_time = time.time()for conn in conn_list:if int(curr_time - conn.alive_time) > conn_timeout:if conn.running == True:
conn.fd.shutdown(socket.SHUT_RDWR)
conn.running = Falseconn_list = [conn for conn in conn_list if conn.running]except:print sys.exc_info()
conn_lock.release() 

def main():server_listen() 

if __name__ == "__main__":
main()



转载于:https://my.oschina.net/u/563014/blog/646425

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SixMan 这是上次IPv6文件传输的升级版,新特性如下: 1.加入了即时通信,也许只是鸡肋,大家别BS我啊 2.支持文件夹传输 3.解决上一版中,有些情况下只能单向传文件的重大缺陷,现在只要双方建立了连接,无论哪方发起的连接,只要连接建立就能互传文件... 使用方法: 点击 添加用户-> 双击“新加入的用户” -> 在弹出的聊天窗口 输入 对方(不是你自己的) IP(v6) -> 点击 连接 -> 等待连接成功后 即可 发送文件时 只需将待发送的文件(夹)拖拽的窗口即可 点击 查看本机IP 按键可以查看本机的IP地址信息,其结果取自在cmd窗口运行ipconfig命令的结果 设置按钮里 可以修改昵称 有朋友说还要知道对方IP才能连接,太麻烦了... 问这个问题的朋友可能不太了解现在的网络协议等相关知识,没有网络地址是无法通信的,两个点要想通信必须要知道对方网络地址才行, 可能有朋友不服气,说为什么QQ就不用,这个问题只要懂一点计算机知识的人都知道,人家腾讯有钱,把用户登录服务放在一个有着固定公网 IP的服务器上,这个IP永不会变,你的QQ客户端内部是集成了这个IP的,所以你登陆时不需要你输入IP,而且所有QQ用户都是通过这一个服务器登录, 腾讯的数据库保存着所有QQ用户的个人信息和好友列表... 我可没有钱去置办那么庞大的服务器和数据库,而且我的软件不是QQ那样的商业软件,不过是为了解决平时一些应用而开发的一款小工具,应用范围是不同于 QQ的,也根本不是用来代替QQ的.... 注意: 假设A和B通信 那么只要A连接到B 或者 B连接到A 即可,无需双向连接,连接后即可双向传文件 有时连接不成功可以换一个IP(v6)地址,一般情况下会有多个IPv6地址,一个不行换一个试试,再不行要关闭防火墙。 遇到问题可以找客服 QQ:191730977 Email: acmtiger@gmail.com 感谢大家的支持
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值