socket网络编程ftp

这是一个使用Python编写的FTP客户端和服务器程序。客户端支持下载(GET_FILE)和上传(PUT_FILE)文件,服务器端实现了基本的FTP命令处理,包括用户认证、被动模式等。通过多线程处理数据传输,确保并发操作的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#!/usr/bin/env python
#-*-coding:utf-8-*-
#ftp客户端
import os 
import socket
import threading
import socketserver

#下载文件
def get_file(host,port,filepath):
    s=socket.socket()
    s.connect((host,port))
    filepath=os.path.join('.','bakc','filepath')
    f=open(filepath,'wb')
    data=True
    while data:
        data=s.recv(1024)
        if data:
            f.write(data)

    s.close()
    f.close()
    #上传文件
def put_file(host,port,filepath):
    s=socket.socket()
    s.connect((host,port))
    f=open(filepath,'rb')
    while True:
        data=f.read(1024)
        if data:
            s.sendall(data)
        else:
            break
    s.close()
    f.close()

class FtpClient:
    def __init__(self,host='localhost',port=21):
        self.host=host
        self.port=port
        self.cmds=('QUIT','USER','NOOP','TYPE','PASV','PORT','RETR','STOR')
        self.linesep='\n'
        self.data_port=None
        self.loged=False
        self.sock=None
        self.pasv_mode=None
        self.pasv_host=None
        self.pasv_port=None

    def cmd_connect(self):
        self.sock=socket.socket()
        self.sock.connect((self.host,self.port))
        self.data_port=self.sock.getsockname()[0]

    def start(self):
        print('支持的命令: ',self.cmds)
        self.cmd_connect()
        self.login()
        while True:
            cmd=input('请输入FTP命令: ')
            if not cmd:
                print('FTP命令不能为空')
                continue
            cmd,args=self.split_args(cmd)
            if not self.send_cmd(cmd,args):
                continue
            res=self.readline(self.sock)
            print(res)
            if cmd.startswith('PASV') and res.startswith('227'):
                self.pasv_mode=True
                servinfo=res[res.index('(')+1:res.index(')')]
                self.pasv_host='.'.join(servinfo.split(',')[:4])
                servinfo=servinfo.split(',')[-2:]
                self.pasv_port=256*int(servinfo[0])+int(servinfo[1])
            if cmd.startswith('RETR'):
                if self.pasv_mode:
                    threading.Thread(target=get_file,args=(self.pasv_host,self.pasv_port,args)).start()
            if cmd.startswitch('STOR'):
                if self.pasv_mode:
                    threading.Thread(target=put_file,args=(self.pasv_host,self.pasv_port,args)).start()
            if cmd.startswitch('QUIT'):
                break
        self.sock.close()
        self.sock=None

    def login(self):
        if self.sock:
            self.send_cmd('USER')
            res=self.readline(self.sock)
            if res.startswitch('230'):
                print('登录成功!')
                self.loged=True

    def readline(self,sock):
        data=''
        while not data.endswith(self.linesep):
            d=sock.recv(1)
            data+=d.decode('utf-8')
        return data

    def split_args(self,cmds):
        if ' ' in cmds:
            cmdlsts=cmds.split(' ')
            cmd=cmdlsts[0]
            args=' '.join(cmdlists[1:])
        else:
            cmd=cmds
            args=''
        return cmd.upper(),args
    def send_cmd(self,cmd,args=''):
        if self.sock:
            if args:
                cmd=' '.join((cmd,args))
            if cmd.startswith('RETR') or cmd.startswith('STOR'):
                if self.pasv_mode is None:
                    print('Please appoint port or stor mode.')
                    return False
                if not args:
                    return False
            if cmd.startswith('STOR'):
                if args:
                    if not os.path.exists(args):
                        print('File is not exists')
                        return False
            cmd+=self.lineseps
            self.sock.sendall(cmd.encode('utf-8'))
            return True

if __name__=='__main__':
    fc=FtpClient()
    fc.start()

#!/usr/bin/env python
#-*-coding:utf-8-*-
#ftp服务器的定义
import  socket
import socketserver
import time
import threading
import os

class FTPHdl(socketserver.StreamRequestHandler):
    def __init__(self,request=None,client_address=None,server=None):
        #实例属性,所有命令
        self.coms_keys=('QUIT','USER','NOOP','TYPE','PASV','PORT','RETR','STOR')
        #命令与方法映射
        self.coms={}
        self.init_coms()
        self.server=server
        #命令端口
        self.cmd_port=21
        #数据端口
        self.data_port=20
        self.pasv_data_ip=None
        self.pasv_data_port=None
        #参数
        self.args=None
        #是否登入
        self.loged=False
        #主动模式,被动模式
        self.pasv_mode=None
        super().__init__(request,client_address,server)
 
    #初始话所有命令
    def init_coms(self):
        for k in self.coms_keys:
            self.coms[k]=getattr(self,'exe_'+k.lower())
    #客户端处理
    def handle(self):
        while True:
            #获得命令
            cmds=self.rfile.readline()
            if not cmds:
                continue
            cmds=cmds.decode('utf-8')
            #命令分析
            cmd=self.deal_args(cmds)
            #命令动词
            if cmd in self.coms_keys:
                self.coms.get(cmd)()
            #不在定义的命令之内
            else:
                self.send(500,'Invaild command.')
            if cmd=='QUIT':
                break

    #处理命令行
    def deal_args(self,cmds):
        #命令动词,命令参数分割
        if ' ' in cmds:
            cmd,args=cmds.aplit(' ')
            args=args.strip('\n').strip()
        #只有命令动词
        else:
            cmd=cmds.strip('\n')
            args=''
            #参数为空
        if args:
            self.args=args
        #返回命令动词
        return cmd.upper()
    #用户退出
    def exe_quit(self):
        self.send(221,'bye')
    #用户登入
    def exe_user(self):
        user=self.args
        #用户名为空或默认
        if user in ('','anonymous'):
            user='anonymous'
            self.loged=True
            #登入已经
            self.send(230,'identified!')
        #没有登入,匿名
        else:
            self.send(530,'Only use anonymous')
    #被动模式
    def exe_pasv(self):
        if not self.loged:
            #要求登入
            self.send(332,'Please login.')
            return 
        #已经进入被动模式
        if self.pasv_mode:
            #发送用户信息。IP端口号发送客户端
            info='entering passive mode (%s)' % self.make_pasv_info()
            self.send(227,info)
            return
        try:
            #进入被动模式
            self.enter_pasv()
            info='entering passive mode (%s)' % self.make_pasv_info()
            self.pasv_mode=True
            self.send(227,info)
        except Exception as e:
            print(e)
            self.send(500,'Failure change to passive mode.')
    #被动模式下数据连接服务器的地址和端口
    def make_pasv_info(self):
        ip_info=self.pasv_data_ip.split('.')
        ip_info=','.join(ip_info)
        porta=str(self.pasv_data_port // 256)
        portb=str(self.pasv_data_port % 256)
        return ','.join((ip_info,porta,portb))
    #进入被动模式
    def enter_pasv(self):
        if self.server.data_server is None:
            self.pasv_data_ip,self.pasv_data_port=self.server.create_data_server()

    #进入主动模式
    def exe_port(self):
        self.send(500,'Do not offer port mode.')

    def exe_noop(self):
        self.send(200,'ok')

    def exe_type(self):
        self.send(200,'ok')

    #下载文件
    def exe_retr(self):
        if not os.path.exists(self.args):
            self.send(550,'File is not exists.')
            return
        client_addr=self.request.getpeername()[0]
        #获得ip地址
        self.add_opr_file(client_addr,('RETR',self.args))
        self.send(150,'ok.')
    #上传文件
    def exe_stor(self):
        client_addr=self.request.getpeername()[0]
        self.add_opr_file(client_addr,('STOR',self.args))
        self.send(150,'ok.')

    def add_opr_file(self,client_addr,item):
        if client_addr in DataHdl.client_opr:
            DataHdl.client_opr[client_addr].append(item)
        else:
            DataHdl.client_opr[client_addr]=[item,]

    def send(self,code,info):
        infos='%d %s\n' % (code,info)
        self.request.sendall(infos.encode('utf-8'))

class MyThrTCPServ(socketserver.ThreadingTCPServer):
    def __init__(self,addr,Hdl):
        self.data_server=None
        super().__init__(addr,Hdl)

    def shutdown(self):
        if self.data_server:
            threading.Thread(target=self.data_server.shutdown).start()
        super().shutdown()

    def create_data_server(self):
        self.data_server=socketserver.ThreadingTCPServer(('127.0.0.1',0),DataHdl)
        pasv_data_ip,pasv_data_port=self.data_server.server_address
        threading.Thread(target=self.data_server.serve_forever).start()
        return pasv_data_ip,pasv_data_port
    
class DataHdl(socketserver.StreamRequestHandler):
    client_opr={}
    def handle(self):
        peerip=self.request.getpeername()[0]
        opr=self.get_opr_args(peerip)
        if opr:
            if opr[0]=='RETR':#下载文件
                self.retr_file(opr[1])
            elif opr[0]=='STOR':#上传文件
                self.stor_file(opr[1])
    def get_opr_args(self,peerip):
        if peerip in self.client_opr:
            opr=self.client_opr[peerip]
            if not self.client_opr[peerip]:
                self.client_opr.pop(peerip)
            return opr
    #发送文件
    def retr_file(self,filepath):
        f=open(filepath,'rb')
        while True:
            data=f.read(1024)
            if data:
                self.request.sendall(data)
            else:
                break
        f.close()

    #接受文件
    def stor_file(self,filepath):
        f=open(os.path.join('.','bakt',filepath),'wb')
        while True:
            data=self.request.recv(1024)
            if data:
                f.write(data)
            else:
                break
        f.close()

if __name__=='__main__':
    server=MyThrTCPServ(('127.0.0.1',21),FTPHdl)
    threading.Thread(target=server.serve_forever).start()
    print('FTP start...')
    time.sleep(30)
    server.shutdown()




        





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值