etgsd 发表于 2016-8-19 10:30:31

自己动手写一个FTP客户端

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# -*- 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)
    #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:   # PORT mode
      if daddr.find(':')!=-1:    #IPV6
            sc.send('EPRT |2|%s|%s|\r\n'%(actions, actions))
      elif daddr.find(':')==-1:   #ipv4
            sc.send('PORT %s\r\n'%handle_PORT(actions, actions))
      print "<--recive: ",sc.recv(1024)
      #step 5 build sub connection
      th_1=threading.Thread(target=sub_bindSock, args=(actions, actions))
      th_1.start()
      sc.send(actions)    #send LIST command
      print "<--recive: ",sc.recv(1024)
      th_1.join()
    elif actions==0:   #PASV mode
      if daddr.find(':')!=-1:   #ipv6
            sc.send('EPSV\r\n')
            recv=sc.recv(1024)
            subDport=int(recv)
      elif daddr.find(':')==-1:#ipv4
            sc.send('PASV\r\n')
            recv=sc.resv(1024)
            subDport=handle_PASV(recvStr)
      subDaddr=(daddr, subDport)
      #step 5 build sub connection
      th_2=threading.Thread(target=sub_sock, args=(subDaddr, ))
      th_2.start()
      sc.send(actions)
      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)
    recv=sock.recv(1024)
    print "send--> username ok!\n<--recive: ",recv
    sock.send('PASS %s\r\n'%loginInfo)
    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)+','+str(temp)
    return addrStr+','+portStr
def handle_PASV(recvStr):
    '''针对IPV4,将受到的子连接端口字符串信息转换为真正的端口,并返回'''
    temp=recvStr
    a=temp.split(',')
    b=temp.split(',')
    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='IE@126.com'
    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为主动模式时,开启一个端口监听,等待服务器主动过来连接。

好了,代码中都有注释。

页: [1]
查看完整版本: 自己动手写一个FTP客户端