自己用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=‘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为主动模式时,开启一个端口监听,等待服务器主动过来连接。
好了,代码中都有注释。
本文出自 “Dalon 技术博客” 博客,转载请与作者联系!
原文地址:http://ybtest.blog.51cto.com/1941531/1840025