自己动手写一个FTP客户端

自己用socket写一个FTP客户端,模拟主动被动模式。(先支持LIST命令

# -*- coding: utf-8 -*-

import socket, sys, thread, threading

def main_sock(daddr, actions, saddr=()):
    if saddr:
        try:
            sc=socket.create_connection(daddr, 3, saddr)
            #print "Now %s connecting to %s ... ..."%(saddr, daddr)
        except socket.error:
            print ‘TCP socket connect %s => %s failed‘%(saddr, daddr)
            return
    else:
        try:
            sc=socket.create_connection(daddr, 3)
        except socket.error:
            print ‘TCP socket connect %s failed‘%(daddr)
            return
    #step 1 : print welcome informations
    print "<--recive: ",sc.recv(1024)
    #step 2 login
    login(sc, actions[0])
    #step 3 use ASCII TYPE send and recv data
    sc.send(‘TYPE A\r\n‘)
    recv=sc.recv(1024)
    print "send--> TYPE A\n <---recive: ",recv
    #step 4 use mode PORT OR PASV
    if actions[1][0]==1:     # PORT mode
        if daddr[0].find(‘:‘)!=-1:    #IPV6
            sc.send(‘EPRT |2|%s|%s|\r\n‘%(actions[1][1], actions[1][2]))
        elif daddr[0].find(‘:‘)==-1:   #ipv4
            sc.send(‘PORT %s\r\n‘%handle_PORT(actions[1][1], actions[1][2]))
        print "<--recive: ",sc.recv(1024)
        #step 5 build sub connection
        th_1=threading.Thread(target=sub_bindSock, args=(actions[1][1], actions[1][2]))
        th_1.start()
        sc.send(actions[2])    #send LIST command
        print "<--recive: ",sc.recv(1024)
        th_1.join()
    elif actions[1][0]==0:   #PASV mode
        if daddr[0].find(‘:‘)!=-1:   #ipv6
            sc.send(‘EPSV\r\n‘)
            recv=sc.recv(1024)
            subDport=int(recv[recv.find(‘(|||‘)+4:recv.find(‘|)‘)])
        elif daddr[0].find(‘:‘)==-1:  #ipv4
            sc.send(‘PASV\r\n‘)
            recv=sc.resv(1024)
            subDport=handle_PASV(recvStr)
        subDaddr=(daddr[0], subDport)
        #step 5 build sub connection
        th_2=threading.Thread(target=sub_sock, args=(subDaddr, ))
        th_2.start()
        sc.send(actions[2])
        th_2.join()
    while 1:
        try:
            data=sc.recv(1024)
            if not len(data):
                break
            else:
                print "<--recived: ",data
        except:
            print "timeout!"
            break
    sc.close()
def sub_sock(daddr, saddr=()):
    try:
        subSc=socket.create_connection(daddr, 3, saddr)
        #print "Now %s connecting to %s ... ..."%(saddr, daddr)
    except socket.error:
        print ‘TCP socket connect %s => %s failed‘%(saddr, daddr)
    while 1:
        try:
            data=subSc.recv(2048)
            if not len(data):
                break
            else:
                print "<--subCon recived:",data
        except:
            print "timeout!"
            break
    subSc.close()
def sub_bindSock(saddr, sport):
    if saddr.find(‘:‘)==-1:
        sc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    else:
        sc = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
    try:
        sc.bind((saddr, sport))
        sc.listen(5)
        sc.settimeout(3)
    except:
        print "bind %s:%s failed!"%(sip, sport)
        return -1
    peerSock, peerAddr=sc.accept()
    print "sub connect is builded\r\n%s<------>%s"%((saddr, sport), peerAddr)
    while 1:
        try:
            data=peerSock.recv(2048)
            if not len(data):
                break
            else:
                print "<--subCon recived:",data
        except:
            print "timeout!"
            break
    sc.close()
def login(sock, loginInfo):
    sock.send(‘USER %s\r\n‘%loginInfo[0])
    recv=sock.recv(1024)
    print "send--> username ok!\n<--recive: ",recv
    sock.send(‘PASS %s\r\n‘%loginInfo[1])
    recv=sock.recv(1024)
    print "send--> password ok!\n<--recive: ",recv
def handle_PORT(saddr, sport):
    ‘‘‘主动模式时,如果地址是IPV4,将地址和端口的格式进行转换,然后放到Port 命令中发送 ‘‘‘
    addrStr=‘,‘.join(saddr.split(‘.‘))
    temp=divmod(sport, 256)
    portStr=str(temp[0])+‘,‘+str(temp[1])
    return addrStr+‘,‘+portStr
def handle_PASV(recvStr):
    ‘‘‘针对IPV4,将受到的子连接端口字符串信息转换为真正的端口,并返回‘‘‘
    temp=recvStr[recvStr.find(‘Mode (‘)-1:recvStr.find(‘)‘)]
    a=temp.split(‘,‘)[4]
    b=temp.split(‘,‘)[5]
    subPort=int(a)*256+int(b)
    return subPort

if __name__==‘__main__‘:
    #target=‘192.168.10.112‘
    target=‘4001::112‘
    port=21
    Daddr=(target, port)
    username=‘anonymous‘
    passwd=‘[email protected]‘
    loginInfo=(username, passwd)
    FTPmode=1 # 1 means PORT mode, 0 means PASV mode
    #saddr=‘192.168.10.102‘
    saddr=‘2000::5d80:b20f:8631:9782‘
    sport=11178
    PASV_or_PORT=(FTPmode, saddr, sport)
    LIST=‘LIST\r\n‘
    actions=(loginInfo, PASV_or_PORT, LIST)
    main_sock(Daddr, actions)

FTP协议是典型的多连接协议。通信的时候会建立两个通道:主连接和子连接。主连接传输控制信令(例如上传下载列出目录等),当需要传输数据的时候,会在主连接中使用被动模式或者主动模式协商好端口,然后打开一个子连接,传输完后,子连接就立即被拆掉了。

通过自己动手实现一个FTP客户端,可以加深对FTP协议的理解。

以上,main_sock为FTP的主连接处理,sub_sock为子连接的处理函数,sub_bindSock为主动模式时,开启一个端口监听,等待服务器主动过来连接。

好了,代码中都有注释。

时间: 2025-01-04 16:49:19

自己动手写一个FTP客户端的相关文章

用Python写一个ftp下载脚本

用Python写一个ftp下载脚本 ----基于Red Hat Enterprise Linux Server release 6.4 (Santiago):python 2.6.6 Ps:少侠我接触Python半个月以来接到的第一个需求,虽然如此简单的一个脚本,少侠我磕磕绊绊却用了将近一天半的时间才写出来,但还是很开心,毕竟也粗来了,废话不多说,切入正题.因为一开始没有用过ftplib模块,所以各种谷歌度娘一堆资料杂乱不堪,话不清,理不乱的,本文实现的功能简单,下面介绍一下,以免误导读者. 需

利于Wininet创建一个FTP客户端的步骤

Wininet是Win32关于网络的API,MFC也有对于Wininet的封装,可以利用这组API实现FTP和HTTP通信. Wininet API的头文件:Wininet.下面是Wininet建立FTP客户端的一般步骤.第一步:初始话Wininet,实际上就是设置一些关于是否使用代理,访问方式等的参数.第二步:建立一个FTP链接.第三步:操作ftp服务器上的文件.第四步:关闭各种句柄. 作用 函数原型 说明 初始Wininet函数 HINTERNET InternetOpen( LPCTSTR

动手写一个Remoting测试工具

基于.NET开发分布式系统,经常用到Remoting技术.在测试驱动开发流行的今天,如果针对分布式系统中的每个Remoting接口的每个方法都要写详细的测试脚本,无疑非常浪费时间.所以,我想写一个能自动测试remoting接口的小工具InterfaceTester.而且,当分布式系统中的某个remoting接口出现bug时,该小工具可以提交需要模拟的数据,以便在调试remoting服务的环境中,快速定位和解决bug. InterfaceTester运行起来后的效果如下图: 1.如何使用 (1)首

模拟spring - 动手写一个spring AOP

一.前言 AOP (Aspect Oriented Programing) - 面向切面编程,它主要用于日志记录.性能分析.安全控制.事务处理.异常处理等方面. AOP主要使用JDK的反射和动态代理,AOP代理其实是由AOP框架动态生成的一个对象,该对象可作为目标对象使用,AOP代理包含了目标对象的全部方法,但AOP代理的方法与目标对象的方法存在差异:AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法. 动态代理的文章请参考:http://blog.csdn.net/zdp072/ar

死磕 java线程系列之自己动手写一个线程池

欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写的线程池如何测试? 简介 线程池是Java并发编程中经常使用到的技术,那么自己如何动手写一个线程池呢?本文彤哥将手把手带你写一个可用的线程池. 属性分析 线程池,顾名思义它首先是一个"池",这个池里面放的是线程,线程是用来执行任务的. 首先,线程池中的线程应该是有类别的,有的是核心线程,有

写一个ftp服务器(1)--需求分析

一个ftp服务器要有什么功能,拿vsftpd举例来说,vsftpd除了实现基本的ftp命令,还支持控制连接,参数可配置,断点续传等. vsftpd的大部分配置及功能可以在其配置文件中看到 Linux下执行 man 5 vsftpd.conf查看. 假如自己实现一个ftp服务器,要实现的功能如下: 1.标准ftp命令 基本的ftp文件传输协议,可以参考RFC 这里收录了一个中文简化版本:https://github.com/zilandy/zFTP 下doc 2.参数配置 通过配置文件改变执行参数

死磕 java线程系列之自己动手写一个线程池(续)

(手机横屏看源码更方便) 问题 (1)自己动手写的线程池如何支持带返回值的任务呢? (2)如果任务执行的过程中抛出异常了该怎么处理呢? 简介 上一章我们自己动手写了一个线程池,但是它是不支持带返回值的任务的,那么,我们自己能否实现呢?必须可以,今天我们就一起来实现带返回值任务的线程池. 前情回顾 首先,让我们先回顾一下上一章写的线程池: (1)它包含四个要素:核心线程数.最大线程数.任务队列.拒绝策略: (2)它具有执行无返回值任务的能力: (3)它无法处理有返回值的任务: (4)它无法处理任务

操刀 requirejs,自己动手写一个

前沿 写在文章的最前面 这篇文章讲的是,我怎么去写一个 requirejs . 去 github 上fork一下,顺便star~ requirejs,众所周知,是一个非常出名的js模块化工具,可以让你使用模块化的方式组织代码,并异步加载你所需要的部分.balabala 等等好处不计其数. 之所以写这篇文章,是做一个总结.目前打算动一动,换一份工作.感谢 一线码农 大大帮忙推了携程,得到了面试的机会. 面试的时候,聊着聊着感觉问题都问在了自己的“点”上,应答都挺顺利,于是就慢慢膨胀了.在说到模块化

自己动手写一个iOS 网络请求库的三部曲[转]

代码示例:https://github.com/johnlui/Swift-On-iOS/blob/master/BuildYourHTTPRequestLibrary 开源项目:Pitaya,适合大文件上传的 HTTP 请求库:https://github.com/johnlui/Pitaya 本系列文章中,我们将尝试使用 NSURLSession 技术构建一个自己的网络请求库. NSURLSession 简介 NSURLSession 是 iOS7 引入的新网络请求接口,在 WWDC2013