Python之FTP实现

Python之FTP实现

   上传下载:

  1 import socket
  2 import struct
  3 import json
  4 import subprocess
  5 import os
  6 
  7 class MYTCPServer:
  8     address_family = socket.AF_INET
  9 
 10     socket_type = socket.SOCK_STREAM
 11 
 12     allow_reuse_address = False
 13 
 14     max_packet_size = 8192
 15 
 16     coding='utf-8'
 17 
 18     request_queue_size = 5
 19 
 20     server_dir='file_upload'
 21 
 22     def __init__(self, server_address, bind_and_activate=True):
 23         """Constructor.  May be extended, do not override."""
 24         self.server_address=server_address
 25         self.socket = socket.socket(self.address_family,
 26                                     self.socket_type)
 27         if bind_and_activate:
 28             try:
 29                 self.server_bind()
 30                 self.server_activate()
 31             except:
 32                 self.server_close()
 33                 raise
 34 
 35     def server_bind(self):
 36         """Called by constructor to bind the socket.
 37         """
 38         if self.allow_reuse_address:
 39             self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 40         self.socket.bind(self.server_address)
 41         self.server_address = self.socket.getsockname()
 42 
 43     def server_activate(self):
 44         """Called by constructor to activate the server.
 45         """
 46         self.socket.listen(self.request_queue_size)
 47 
 48     def server_close(self):
 49         """Called to clean-up the server.
 50         """
 51         self.socket.close()
 52 
 53     def get_request(self):
 54         """Get the request and client address from the socket.
 55         """
 56         return self.socket.accept()
 57 
 58     def close_request(self, request):
 59         """Called to clean up an individual request."""
 60         request.close()
 61 
 62     def run(self):
 63         while True:
 64             self.conn,self.client_addr=self.get_request()
 65             print('from client ',self.client_addr)
 66             while True:
 67                 try:
 68                     head_struct = self.conn.recv(4)
 69                     if not head_struct:break
 70 
 71                     head_len = struct.unpack('i', head_struct)[0]
 72                     head_json = self.conn.recv(head_len).decode(self.coding)
 73                     head_dic = json.loads(head_json)
 74 
 75                     print(head_dic)
 76                     #head_dic={'cmd':'put','filename':'a.txt','filesize':123123}
 77                     cmd=head_dic['cmd']
 78                     if hasattr(self,cmd):
 79                         func=getattr(self,cmd)
 80                         func(head_dic)
 81                 except Exception:
 82                     break
 83 
 84     def put(self,args):
 85         file_path=os.path.normpath(os.path.join(
 86             self.server_dir,
 87             args['filename']
 88         ))
 89 
 90         filesize=args['filesize']
 91         recv_size=0
 92         print('----->',file_path)
 93         with open(file_path,'wb') as f:
 94             while recv_size < filesize:
 95                 recv_data=self.conn.recv(self.max_packet_size)
 96                 f.write(recv_data)
 97                 recv_size+=len(recv_data)
 98                 print('recvsize:%s filesize:%s' %(recv_size,filesize))
 99 
100 
101 tcpserver1=MYTCPServer(('127.0.0.1',8080))
102 
103 tcpserver1.run()
104 
105 
106 
107 
108 
109 
110 #下列代码与本题无关
111 class MYUDPServer:
112 
113     """UDP server class."""
114     address_family = socket.AF_INET
115 
116     socket_type = socket.SOCK_DGRAM
117 
118     allow_reuse_address = False
119 
120     max_packet_size = 8192
121 
122     coding='utf-8'
123 
124     def get_request(self):
125         data, client_addr = self.socket.recvfrom(self.max_packet_size)
126         return (data, self.socket), client_addr
127 
128     def server_activate(self):
129         # No need to call listen() for UDP.
130         pass
131 
132     def shutdown_request(self, request):
133         # No need to shutdown anything.
134         self.close_request(request)
135 
136     def close_request(self, request):
137         # No need to close anything.
138         pass
服务端
 1 import socket
 2 import struct
 3 import json
 4 import os
 5 
 6 
 7 class MYTCPClient:
 8     address_family = socket.AF_INET
 9 
10     socket_type = socket.SOCK_STREAM
11 
12     allow_reuse_address = False
13 
14     max_packet_size = 8192
15 
16     coding='utf-8'
17 
18     request_queue_size = 5
19 
20     def __init__(self, server_address, connect=True):
21         self.server_address=server_address
22         self.socket = socket.socket(self.address_family,
23                                     self.socket_type)
24         if connect:
25             try:
26                 self.client_connect()
27             except:
28                 self.client_close()
29                 raise
30 
31     def client_connect(self):
32         self.socket.connect(self.server_address)
33 
34     def client_close(self):
35         self.socket.close()
36 
37     def run(self):
38         while True:
39             inp=input(">>: ").strip()
40             if not inp:continue
41             l=inp.split()
42             cmd=l[0]
43             if hasattr(self,cmd):
44                 func=getattr(self,cmd)
45                 func(l)
46 
47 
48     def put(self,args):
49         cmd=args[0]
50         filename=args[1]
51         if not os.path.isfile(filename):
52             print('file:%s is not exists' %filename)
53             return
54         else:
55             filesize=os.path.getsize(filename)
56 
57         head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}
58         print(head_dic)
59         head_json=json.dumps(head_dic)
60         head_json_bytes=bytes(head_json,encoding=self.coding)
61 
62         head_struct=struct.pack('i',len(head_json_bytes))
63         self.socket.send(head_struct)
64         self.socket.send(head_json_bytes)
65         send_size=0
66         with open(filename,'rb') as f:
67             for line in f:
68                 self.socket.send(line)
69                 send_size+=len(line)
70                 print(send_size)
71             else:
72                 print('upload successful')
73 
74 
75 
76 client=MYTCPClient(('127.0.0.1',8080))
77 
78 client.run()
客户端

   

  认证客户端的链接合法性

  如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现

 1 #_*_coding:utf-8_*_
 2 __author__ = 'Linhaifeng'
 3 from socket import *
 4 import hmac,os
 5 
 6 secret_key=b'linhaifeng bang bang bang'
 7 def conn_auth(conn):
 8     '''
 9     认证客户端链接
10     :param conn:
11     :return:
12     '''
13     print('开始验证新链接的合法性')
14     msg=os.urandom(32)
15     conn.sendall(msg)
16     h=hmac.new(secret_key,msg)
17     digest=h.digest()
18     respone=conn.recv(len(digest))
19     return hmac.compare_digest(respone,digest)
20 
21 def data_handler(conn,bufsize=1024):
22     if not conn_auth(conn):
23         print('该链接不合法,关闭')
24         conn.close()
25         return
26     print('链接合法,开始通信')
27     while True:
28         data=conn.recv(bufsize)
29         if not data:break
30         conn.sendall(data.upper())
31 
32 def server_handler(ip_port,bufsize,backlog=5):
33     '''
34     只处理链接
35     :param ip_port:
36     :return:
37     '''
38     tcp_socket_server=socket(AF_INET,SOCK_STREAM)
39     tcp_socket_server.bind(ip_port)
40     tcp_socket_server.listen(backlog)
41     while True:
42         conn,addr=tcp_socket_server.accept()
43         print('新连接[%s:%s]' %(addr[0],addr[1]))
44         data_handler(conn,bufsize)
45 
46 if __name__ == '__main__':
47     ip_port=('127.0.0.1',9999)
48     bufsize=1024
49     server_handler(ip_port,bufsize)
服务端
 1 #_*_coding:utf-8_*_
 2 __author__ = 'Linhaifeng'
 3 from socket import *
 4 import hmac,os
 5 
 6 secret_key=b'linhaifeng bang bang bang'
 7 def conn_auth(conn):
 8     '''
 9     验证客户端到服务器的链接
10     :param conn:
11     :return:
12     '''
13     msg=conn.recv(32)
14     h=hmac.new(secret_key,msg)
15     digest=h.digest()
16     conn.sendall(digest)
17 
18 def client_handler(ip_port,bufsize=1024):
19     tcp_socket_client=socket(AF_INET,SOCK_STREAM)
20     tcp_socket_client.connect(ip_port)
21 
22     conn_auth(tcp_socket_client)
23 
24     while True:
25         data=input('>>: ').strip()
26         if not data:continue
27         if data == 'quit':break
28 
29         tcp_socket_client.sendall(data.encode('utf-8'))
30         respone=tcp_socket_client.recv(bufsize)
31         print(respone.decode('utf-8'))
32     tcp_socket_client.close()
33 
34 if __name__ == '__main__':
35     ip_port=('127.0.0.1',9999)
36     bufsize=1024
37     client_handler(ip_port,bufsize)
客户端(合法)
 1 #_*_coding:utf-8_*_
 2 __author__ = 'Linhaifeng'
 3 from socket import *
 4 
 5 def client_handler(ip_port,bufsize=1024):
 6     tcp_socket_client=socket(AF_INET,SOCK_STREAM)
 7     tcp_socket_client.connect(ip_port)
 8 
 9     while True:
10         data=input('>>: ').strip()
11         if not data:continue
12         if data == 'quit':break
13 
14         tcp_socket_client.sendall(data.encode('utf-8'))
15         respone=tcp_socket_client.recv(bufsize)
16         print(respone.decode('utf-8'))
17     tcp_socket_client.close()
18 
19 if __name__ == '__main__':
20     ip_port=('127.0.0.1',9999)
21     bufsize=1024
22     client_handler(ip_port,bufsize)
客户端(非法:不知道加密方式)
 1 #_*_coding:utf-8_*_
 2 __author__ = 'Linhaifeng'
 3 from socket import *
 4 import hmac,os
 5 
 6 secret_key=b'linhaifeng bang bang bang1111'
 7 def conn_auth(conn):
 8     '''
 9     验证客户端到服务器的链接
10     :param conn:
11     :return:
12     '''
13     msg=conn.recv(32)
14     h=hmac.new(secret_key,msg)
15     digest=h.digest()
16     conn.sendall(digest)
17 
18 def client_handler(ip_port,bufsize=1024):
19     tcp_socket_client=socket(AF_INET,SOCK_STREAM)
20     tcp_socket_client.connect(ip_port)
21 
22     conn_auth(tcp_socket_client)
23 
24     while True:
25         data=input('>>: ').strip()
26         if not data:continue
27         if data == 'quit':break
28 
29         tcp_socket_client.sendall(data.encode('utf-8'))
30         respone=tcp_socket_client.recv(bufsize)
31         print(respone.decode('utf-8'))
32     tcp_socket_client.close()
33 
34 if __name__ == '__main__':
35     ip_port=('127.0.0.1',9999)
36     bufsize=1024
37     client_handler(ip_port,bufsize)
客户端(非法:不知道secret_key)

   

  socketserver实现并发

  基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环

  socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题)

  server类:

        

  request类:

        

      

  继承关系:

      

      

  

      

   

        

  以下述代码为例,分析socketserver源码:

    ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer)
    ftpserver.serve_forever()

  查找属性的顺序:ThreadingTCPServer->ThreadingMixIn->TCPServer->BaseServer

  1. 实例化得到ftpserver,先找类ThreadingTCPServer的__init__,在TCPServer中找到,进而执行server_bind,server_active
  2. 找ftpserver下的serve_forever,在BaseServer中找到,进而执行self._handle_request_noblock(),该方法同样是在BaseServer中
  3. 执行self._handle_request_noblock()进而执行request, client_address = self.get_request()(就是TCPServer中的self.socket.accept()),然后执行self.process_request(request, client_address)
  4. 在ThreadingMixIn中找到process_request,开启多线程应对并发,进而执行process_request_thread,执行self.finish_request(request, client_address)
  5. 上述四部分完成了链接循环,本部分开始进入处理通讯部分,在BaseServer中找到finish_request,触发我们自己定义的类的实例化,去找__init__方法,而我们自己定义的类没有该方法,则去它的父类也就是BaseRequestHandler中找....

  源码分析总结:

  基于tcp的socketserver我们自己定义的类中的

  1. self.server即套接字对象
  2. self.request即一个链接
  3. self.client_address即客户端地址

  基于udp的socketserver我们自己定义的类中的

  1. self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象),如(b'adsf', <socket.socket fd=200, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 8080)>)
  2. self.client_address即客户端地址
 1 import socketserver
 2 import struct
 3 import json
 4 import os
 5 class FtpServer(socketserver.BaseRequestHandler):
 6     coding='utf-8'
 7     server_dir='file_upload'
 8     max_packet_size=1024
 9     BASE_DIR=os.path.dirname(os.path.abspath(__file__))
10     def handle(self):
11         print(self.request)
12         while True:
13             data=self.request.recv(4)
14             data_len=struct.unpack('i',data)[0]
15             head_json=self.request.recv(data_len).decode(self.coding)
16             head_dic=json.loads(head_json)
17             # print(head_dic)
18             cmd=head_dic['cmd']
19             if hasattr(self,cmd):
20                 func=getattr(self,cmd)
21                 func(head_dic)
22     def put(self,args):
23         file_path = os.path.normpath(os.path.join(
24             self.BASE_DIR,
25             self.server_dir,
26             args['filename']
27         ))
28 
29         filesize = args['filesize']
30         recv_size = 0
31         print('----->', file_path)
32         with open(file_path, 'wb') as f:
33             while recv_size < filesize:
34                 recv_data = self.request.recv(self.max_packet_size)
35                 f.write(recv_data)
36                 recv_size += len(recv_data)
37                 print('recvsize:%s filesize:%s' % (recv_size, filesize))
38 
39 
40 ftpserver=socketserver.ThreadingTCPServer(('127.0.0.1',8080),FtpServer)
41 ftpserver.serve_forever()
FtpServer
 1 import socket
 2 import struct
 3 import json
 4 import os
 5 
 6 
 7 
 8 class MYTCPClient:
 9     address_family = socket.AF_INET
10 
11     socket_type = socket.SOCK_STREAM
12 
13     allow_reuse_address = False
14 
15     max_packet_size = 8192
16 
17     coding='utf-8'
18 
19     request_queue_size = 5
20 
21     def __init__(self, server_address, connect=True):
22         self.server_address=server_address
23         self.socket = socket.socket(self.address_family,
24                                     self.socket_type)
25         if connect:
26             try:
27                 self.client_connect()
28             except:
29                 self.client_close()
30                 raise
31 
32     def client_connect(self):
33         self.socket.connect(self.server_address)
34 
35     def client_close(self):
36         self.socket.close()
37 
38     def run(self):
39         while True:
40             inp=input(">>: ").strip()
41             if not inp:continue
42             l=inp.split()
43             cmd=l[0]
44             if hasattr(self,cmd):
45                 func=getattr(self,cmd)
46                 func(l)
47 
48 
49     def put(self,args):
50         cmd=args[0]
51         filename=args[1]
52         if not os.path.isfile(filename):
53             print('file:%s is not exists' %filename)
54             return
55         else:
56             filesize=os.path.getsize(filename)
57 
58         head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}
59         print(head_dic)
60         head_json=json.dumps(head_dic)
61         head_json_bytes=bytes(head_json,encoding=self.coding)
62 
63         head_struct=struct.pack('i',len(head_json_bytes))
64         self.socket.send(head_struct)
65         self.socket.send(head_json_bytes)
66         send_size=0
67         with open(filename,'rb') as f:
68             for line in f:
69                 self.socket.send(line)
70                 send_size+=len(line)
71                 print(send_size)
72             else:
73                 print('upload successful')
74 
75 
76 
77 
78 client=MYTCPClient(('127.0.0.1',8080))
79 
80 client.run()
FtpClient

   作业:

    

猜你喜欢

转载自www.cnblogs.com/george92/p/9318185.html