python--socket(二)粘着バッグ

27253 ワード

パケット問題とは,受信者がメッセージ間の境界を知らず,一度に何バイトのデータを抽出するか分からないためである.
また、送信側による粘着パケットは、TCPプロトコル自体によるものであり、TCPは
高い伝送効率で、送信側は往々にして十分なデータを収集してからTCPを送信する.
セグメント.連続してsendを必要とするデータが少ない場合、通常TCPは最適化に基づいて計算されます.
法はこれらのデータを1つのTCPセグメントを合成した後に一回送信して、このように受信者は受け取ります
データを貼り付けました.
TCP(transport control protocol、トランスポート制御プロトコル)は接続向け
は、ストリーム向けで、高信頼性のサービスを提供します.送受信の両端(クライアントとサーバ側)
)はいずれも1対1のソケットが必要であるため,送信側は複数を受信側に送信するために
のパッケージは、相手により効果的に送信され、最適化方法(Nagleアルゴリズム)を使用して、複数の
サブ間隔が小さく、データ量の少ないデータを大きなブロックに結合し、
封をする.このように、受信側は、見分けがつかず、科学的な分解機を提供しなければならない.
を作成します.すなわち、ストリーム向けの通信は、メッセージ保護境界がない.UDP(user datagram protocol、ユーザーデータレポートプロトコル)は接続されていません
、メッセージ向け、効率的なサービスを提供します.ブロックのマージ最適化アルゴリズムは使用されません.
UDPでは一対多のモードがサポートされているので、受信側のskbuff(ソケットバッファ)
)各到着UDPパケットを記録するためにチェーン構造が採用されており、各UDPパケットには
メッセージヘッダ(メッセージソースアドレス、ポートなどの情報)があり、受信に対して
端的に言えば、区分処理が容易になります.すなわち、メッセージ向けの通信はメッセージ保証付きである
境界を守る.tcpはデータストリームに基づいているため、受信されたメッセージを空にすることはできません.これは、お客様が
エンドとサービスエンドは空のメッセージの処理メカニズムを追加し、プログラムの詰まりを防止し、udpはベースである.
データによると、あなたが空の内容を入力しても(直接車に戻る)、それはそうではありません.
空のメッセージ、udpプロトコルはメッセージヘッダをカプセル化します.
udpのrecvfromはブロックされており、1つのrecvfrom(x)は1つに対して1つでなければならない.
sendinto(y)は、xバイトのデータを受信して完了してもy>xのデータが失われ、
これはudpがパケットにくっつかないことを意味しますが、データが失われ、信頼できないtcpのプロトコルデータが失われず、パケットが受信されず、次回受信し、前回継続します.
受信すると、自己側は常にackを受信するとバッファの内容をクリアします.データは信頼できる
あ、でもべたべたします.
==============================================
FTP
client
import socket,optparse,json,os,shelve
class FtpClient:
    '''Ftp    '''
    MSG_SIZE = 1024  #     1024
    def __init__(self):
        self.username=None
        self.current_dir=None
        self.terminal_display=None
        self.shelve_obj=shelve.open('.luffy_db')
        parser=optparse.OptionParser()
        parser.add_option('-s','--server',dest='server',help='ftp server ip_addr')
        parser.add_option('-P','--port',type='int',dest='port',help='ftp server port')
        parser.add_option("-u", "--username", dest="username", help="username info")
        parser.add_option("-p", "--password", dest="password", help="password info")
        self.options,self.args=parser.parse_args()
        print(self.options,self.args)
        self.argv_verification()
        self.make_connection()
    def argv_verification(self):
        '''      IP PORT'''
        if  not self.options.server or not self.options.port:
            exit('must supply server and port parameters')

    def make_connection(self):
        '''  socket  '''
        self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.connect((self.options.server,self.options.port))
    def get_response(self):
        '''       '''
        data=self.sock.recv(self.MSG_SIZE)
        return json.loads(data.decode())
    def auth(self):
        '''  server'''
        count=0
        while count<3:
            username=input('username: ').strip()
            if not username:continue
            password=input('password: ').strip()
            cmd={
                'action_type':'auth',
                'username':username,
                'password':password
            }
            self.sock.send(json.dumps(cmd).encode('utf8'))
            response=self.get_response()
            if response.get('status_code') == 200:
                self.username=username
                self.terminal_display=self.username
                self.current_dir='\\'
                return True
            else:
                print(response.get('status_msg'))
            count+=1
    def interactive(self):
        '''         '''
        if self.auth():
            self.unfinished_file_check()
            while True:
                user_input =input('[{}]>>: '.format(self.terminal_display)).strip()
                if not user_input:continue
                cmd_list=user_input.split()
                if hasattr(self,'_{}'.format(cmd_list[0])):
                    func=getattr(self,'_{}'.format(cmd_list[0]))
                    func(cmd_list[1:])

    def parameter_checke(self,args,min_args=None,max_args=None,exact_args=None):
        '''       '''
        if min_args:
            if len(args) < min_args:
                print('must provide at least {} parameters but {} receivede'.format(min_args,len(args)))
                return False
        if max_args:
            if len(args) > max_args:
                print("need at most %s parameters but %s received." % (max_args, len(args)))
                return False
        if exact_args:
            if len(args) != exact_args:
                print("need exactly %s parameters but %s received." % (exact_args, len(args)))
                return False
        return True
    def send_msg(self,action_type,**kwargs):
        '''         '''
        msg_data={'action_type':action_type,
                  'fill':""}
        msg_data.update(kwargs)
        bytes_msg=json.dumps(msg_data).encode()
        if len(bytes_msg) < self.MSG_SIZE:
            msg_data['fill'].zfill(self.MSG_SIZE-len(bytes_msg))
            bytes_msg = json.dumps(msg_data).encode()
        self.sock.send(bytes_msg)
    def get_response(self):
        '''           '''
        data=self.sock.recv(self.MSG_SIZE)
        return json.loads(data.decode('utf8'))
    def unfinished_file_check(self):
        '''  shelve db          
                   
            ,                     
            
            '''
        if list(self.shelve_obj.keys()):
            print("-------Unfinished file list -------------")
            for index,abs_file in enumerate(self.shelve_obj.keys()):
                received_file_size=os.path.getsize(self.shelve_obj[abs_file][1])
                print("%s. %s    %s    %s   %s" % (index, abs_file,
                                                   self.shelve_obj[abs_file][0],
                                                   received_file_size,
                                                   received_file_size / self.shelve_obj[abs_file][0] * 100
                                                   ))
            while True:
                choice=input("[select file index to re-download]").strip()
                if not choice:continue
                if choice == 'back': break
                if choice.isdigit():
                    choice=int(choice)
                    if choice >= 0 and choice <= index:
                        selected_file=list(self.shelve_obj.keys())[choice]
                        #self.shelve_obj[file_abs_path]=[file_size,'{}.dawnload'.format(filename)]
                        received_size = os.path.getsize(self.shelve_obj[selected_file][1])
                        self.send_msg('re_get',file_size=self.shelve_obj[selected_file][0],
                                      received_size=received_size,
                                      abs_filename=selected_file)

                        response=self.get_response()
                        if response.get('status_code') == 401:
                            local_file=self.shelve_obj[selected_file][1]
                            f=open(local_file,'ab')
                            total_size=self.shelve_obj[selected_file][0]
                            current_percent=int(received_size/total_size*100)
                            progess_generator=self.progrss_bar(total_size,current_percent=current_percent)
                            progess_generator.__next__()
                            while received_size > total_size:
                                if total_size-received_size<8192:
                                    data=self.sock.recv(total_size-received_size)
                                else:
                                    data=self.sock.recv()
                                f.write(data)
                                received_size+=len(data)
                                progess_generator.send(received_size)
                            else:
                                print("file re-get done")
                                f.close()
                        else:
                            print(response.get('status_code'))


    def _get(self,cmd_args):
        '''download file from server

        1.     
        2.      
        3.       
          3.1      ,      
            3.1.1     
          3.2       
              print status_code
              '''
        if self.parameter_checke(cmd_args,min_args=1):
            filename=cmd_args[0]
            self.send_msg(filename=filename,action_type='get')
            response=self.get_response()
            if response.get('status_code') == 301:#    
                file_size=response.get('file_size')
                received_size=0
                progress_generator = self.progress_bar(file_size)#   
                progress_generator.__next__()
                file_abs_path=os.path.join(self.current_dir,filename)
                self.shelve_obj[file_abs_path]=[file_size,'{}.dawnload'.format(filename)]

                f=open('{}.dawnload'.format(filename),'wb')
                while received_size < file_size:
                    if file_size-received_size<8192:
                        data=self.sock.recv(file_size-received_size)
                    else:
                        data=self.sock.recv(8192)
                    f.write(data)
                    progress_generator.send(received_size)
                else:
                    print("---file [%s] recv done,received size [%s]----" % (filename, file_size))
                    del self.shelve_obj[file_abs_path]
                    f.close()
                    os.rename('{}.dawnload'.format(filename),filename)
            else:
                print(response.get('status_msg'))

    def _ls(self,data):
        '''           
            ,    '''
        self.send_msg(action_type='ls')
        response=self.get_response()
        if response.get('status_code') == 302:#    long  
            cmd_result_size=response.get('cmd_result_size')
            received_size=0
            cmd_result=b''
            while received_size < cmd_result_size:
                if cmd_result_size -received_size<8192:
                    data=self.sock.recv(cmd_result_size -received_size)
                else:
                    data=self.sock.recv(8192)
                received_size+=len(data)
                cmd_result+=data
            else:
               print(cmd_result.decode('GBK'))
    def _cd(self,args):
        '''       
              
              '''
        if self.parameter_checke(args,exact_args=1):
            target_dir=args[0]

            self.send_msg(action_type='cd',target_dir=target_dir)
            response=self.get_response()
            if response.get['status_code']== 350:
                self.terminal_display=response.get['current_dir']
                self.current_dir=response.get['current_dir']
    def progrss_bar(self,total_size,current_percent=0,last_percent=0):
        '''   '''
        while True:
            received_size=yield
            current_percent=int(received_size/total_size*100)
            if current_percent >last_percent:
                print('#'*(int(current_percent)/2) +'[{}]'.format(current_percent),end='\r')
            last_percent=current_percent
    def _put(self,data):
        '''        
        1.      
        2.          
        3.        
        :param data:
        :return:
        '''
        if self.parameter_checke(data,exact_args=1):
            local_file=data[0]
            if os.path.isfile(local_file):
                total_size=os.path.getsize(local_file)
                self.send_msg('put',file_size=total_size,filename=local_file)
                f=open(local_file,'rb')
                progrss_generator=self.progrss_bar(total_size)
                progrss_generator.__next__
                uploaded_size=0
                for line in f:
                    self.sock.send(line)
                    uploaded_size+=0
                    progrss_generator.send(uploaded_size)
                else:
                    print()
                    print('uploaded file done...'.center(50,'-'))
                    f.close()

    def _pwd(self,args):
        '''
            
        :return:
        '''
        print('{}'.format(self.current_dir))

    def _mkdir(self,args):
        '''
        1.    
        2.    
        :param args:
        :return:
        '''

        if self.parameter_checke(args, exact_args=1):

            new_dir = args[0]
            print(new_dir)
            self.send_msg(action_type='mkdir', new_dir=new_dir)
            response = self.get_response()
            if response.get('status_code') == 353:
                cmd_result_size = response.get('cmd_result_size')
                received_size = 0
                cmd_result = b''
                while received_size < cmd_result_size:
                    if cmd_result_size - received_size < 8192:  # last receive
                        data = self.sock.recv(cmd_result_size - received_size)
                    else:
                        data = self.sock.recv(8192)
                    cmd_result += data
                    received_size += len(data)
                else:
                    print(cmd_result.decode("gbk"))
            if response.get('status_code') == 352:
                print('Dir create success')

    def _del(self,args):
        '''
            
            ,
        '''
        if self.parameter_checke(args,min_args=1):
            filename=args[0]
            self.send_msg(filename=filename,action_type='del')
            response=self.get_response()
            if response.get('status_code')== 500:
                print('delect success')
            if response.get('status_code')== 501:
                print("NO search file or dir")
            else:
                data=self.sock.recv(8192)
                print(data.decode('GBK'))











if __name__ =='__main__':
    client=FtpClient()
    client.interactive()


server
import sys,os
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)

if __name__ == '__main__':
    from core import management
    argv_parser=management.ManagementTool(sys.argv)
    argv_parser.execute()#    
from core import main

class ManagementTool:
    '''                        '''
    def __init__(self,sys_argv):
        self.sys_argv=sys_argv#  sys.argv   ['  ','']
        print(self.sys_argv)
        self.verify_argv()#  sys_argv      
    def verify_argv(self):
        '''       '''
        if len(self.sys_argv) < 2:#  sys.argv      ,       
            self.help_msg()
        cmd=self.sys_argv[1]
        if not hasattr(self,cmd):#         
            print('invalid argument')
            self.help_msg()

    def help_msg(self):
        msg='''
        start   start FTP server
        stop    stop  FTP server
        restart  testart FTP server
        createuser username create a ftp user
    
        '''
        exit(msg)#     

    def execute(self):
        '''       '''
        cmd=self.sys_argv[1]
        func=getattr(self,cmd)#      
        func()
    def start(self):
        '''     '''
        server=main.FTPServer(self)
        server.run_forever()
import socket,json,configparser,hashlib,os,subprocess,time
from conf import settings

class FTPServer:
    '''             '''
    STATUS_CODE = {
        200: "Passed authentication!",
        201: "Wrong username or password!",
        300: "File does not exist !",
        301: "File exist , and this msg include the file size- !",
        302: "This msg include the msg size!",
        350: "Dir changed !",
        351: "Dir doesn't exist !",
        352: "Dir create success",
        353:"Dir does exist",
        401: "File exist ,ready to re-send !",
        402: "File exist ,but file size doesn't match!",
        500: 'delect success',
        501: "Dir or file doesn't exist !"

    }
    MSG_SIZE = 1024  #     1024
    def __init__(self,management_instence):
        self.management_instence=management_instence
        self.sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.bind((settings.HOST,settings.PORT))
        self.sock.listen(5)
        self.accounts=self.load_accounts()
        self.user_obj=None
        self.user_current_dir=None
    def run_forever(self):
        '''     ,    '''
        print('starting FTP server on {},{}'.center(50,'-').format(settings.HOST,settings.PORT))
        while True:
            self.request,self.addr=self.sock.accept()
            print('got a new connection form {}....'.format(self.addr,))
            self.handle()


    def handle(self):
        '''            '''
        while True:
            raw_data=self.request.recv(self.MSG_SIZE)
            if not raw_data:
                print('connection {} is lost...'.format(self.addr))
                del self.request,self.sock
                break
            data=json.loads(raw_data.decode('utf8'))#  ,    
            action_type=data.get('action_type')#      action_type  
            if action_type:#      
                if hasattr(self,'_{}'.format(action_type)):
                    func=getattr(self,'_{}'.format(action_type))
                    func(data)

            else:
                print('invalid command')
    def load_accounts(self):
        '''        '''
        config_obj=configparser.ConfigParser()
        config_obj.read(settings.ACCOUNTS_FILE)#    
        return config_obj
    def authenticate(self,username,password):
        '''      '''
        if username in self.accounts:
            _password = self.accounts[username]['password']
            md5_obj=hashlib.md5()
            md5_obj.update(password.encode())
            if _password == md5_obj.hexdigest():
                self.user_obj=self.accounts[username]#              
                self.user_obj['home']=os.path.join(settings.USER_HOME_DIR,username)
                self.user_current_dir=self.user_obj['home']
                #     
                return True
            else:
                return False
        else:
            return False

    def send_response(self,status_code,*args,**kwargs):
        '''          
        :param status_code:200
        :param args:
        :param kwargs:{'filename':filename,'size':filesize}
        :return
        '''
        data=kwargs#    ,    
        data['status_code']=status_code
        data['status_msg']=self.STATUS_CODE[status_code]
        data['fill']=''
        bytes_data=bytes(json.dumps(data).encode())
        if len(bytes_data) < self.MSG_SIZE:
            data['fill'].zfill(self.MSG_SIZE-len(bytes_data))
            bytes_data = bytes(json.dumps(data).encode())
        self.request.send(bytes_data)
    def _auth(self,data):
        '''        '''
        if self.authenticate(data.get('username'),data.get('password')):
            print('pass auth...')
            self.send_response(status_code=200)
        else:
            self.send_response(status_code=201)
    def _get(self,data):
        '''
        1.     
        2.        
            2.1     ,          
                2.1.1     
            2.2      ,     

        :return:
        '''
        filename=data.get('filename')
        full_path=os.path.join(self.user_obj['home'],filename)#               
        if os.path.isfile(full_path):
            filesize=os.stat(full_path).st_size
            self.send_response(status_code=301,file_size=filesize)
            print('ready to send file')
            f=open(filename,'rb')
            for line in f:
                self.request.send(line)
            else:
                print('file send done..',full_path)
            f.close()
        else:
            self.send_response(status_code=300)#     

    def _ls(self,data):
        '''  dir        client'''
        print(self.user_current_dir)
        cmd_obj=subprocess.Popen('dir {}'.format(self.user_current_dir),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        stdout=cmd_obj.stdout.read()
        stderr=cmd_obj.stderr.read()
        cmd_result=stdout+stderr

        if not cmd_result:
            cmd_result=b'current dir has not file at all'

        self.send_response(status_code=302,cmd_result_size=len(cmd_result))
        self.request.sendall(cmd_result)


    def _cd(self,data):
        '''                
        1.         target_dir   user_current_dir   
        2.     target_dir
            2.1       user_current_dir
            2.2           
        '''
        target_dir=data.get('target_dir')
        full_dir=os.path.abspath(os.path.join(self.user_current_dir,target_dir))
        #abspath       cd ..  c:\a\b\c\..\..=c:\a
        if os.path.isdir(full_dir):
            if full_dir.startswith(self.user_obj['home']):
                self.user_current_dir=full_dir
                relative_current_dir=full_dir.replace(self.user_obj['home'],'')
                self.send_response(status_code=350,current_dir=relative_current_dir)
            else:
                self.send_response(status_code=351)
        else:
            self.send_response(status_code=351)

    def _put(self,data):
        '''
        1.          
        2.         
            2.1   ,       
                2.1     
            2.2   ,    
        :param data:
        :return:
        '''
        local_file=data.get('filename')
        full_path=os.path.join(self.user_current_dir,local_file)
        if os.path.isfile(full_path):
            filename='{}.{}'.format(full_path,time.time())
        else:
            filename=full_path
        f=open(filename,'wb')
        received_size=0
        total_size=data.get('tatal_size')
        while received_size < total_size:
            if total_size-received_size <8192:
                data=self.request.recv(total_size-received_size)
            else:
                data=self.request.recv(8192)
            received_size+=len(data)
            f.write(data)
        else:
            print('file %s recv done'% local_file)
            f.close()

    def _re_get(self,args):
        '''
        1.     ,    
        2.       
            2.1  ,        
                2.1.1     ,     
                    2.1.1.1            ,sock      
                2.1.2    ,     
            2.2         

        :param args:
        :return:
        '''
        abs_filename=args.get('abs_filename')
        full_path=os.path.join(self.user_obj['home'],abs_filename.strip('\\'))
        if os.path.isfile(full_path):
            if os.path.getsize(full_path)== args.get('file_size'):
                self.send_response(status_code=401)
                f=open(full_path,'rb')
                f.seek(args.get('received_size'))
                for line in f:
                    self.request.recv(line)
                else:
                    print("-----file re-send done------")
                    f.close()
            else:
                self.send_response(status_code=402,file_size_on_server=os.path.getsize(full_path))
        else:
            self.send_response(status_code=300)

    def _mkdir(self,argv):
        '''
        1.    
        2.  mkdir   
        3.    
        :param argv:
        :return:
        '''

        new_dir=argv.get('new_dir')

        create_target_dir=os.path.join(self.user_current_dir,new_dir)

        cmd_obj = subprocess.Popen('mkdir {}'.format(create_target_dir), shell=True, stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
        stdout = cmd_obj.stdout.read()
        stderr = cmd_obj.stderr.read()
        cmd_result = stdout + stderr
        if len(cmd_result)==0:
            self.send_response(status_code=352)
        else:
            self.send_response(status_code=353,cmd_result_size=len(cmd_result))
            self.request.sendall(cmd_result)
    def _del(self,args):
        '''
        1.    
        2.          
            2.1      del
            2.2 DIR     rd
        3.    
        :param args:
        :return:
        '''
        filename=args.get('filename')
        file_path=os.path.join(self.user_current_dir,filename)
        if os.path.isfile(file_path):
            s = subprocess.Popen('del {}'.format(file_path), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            f1 = s.stdout.read()
            f2 = s.stderr.read()
            f = f1 + f2
            if not f:
                self.send_response(status_code=500)
            else:
                self.request.sendall(f)

        elif os.path.isdir(file_path):
            s = subprocess.Popen('rd {}'.format(file_path), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            f1 = s.stdout.read()
            f2 = s.stderr.read()
            f = f1 + f2
            if not f:
                self.send_response(status_code=500)
            else:
                self.request.sendall(f)

        else:
            self.send_response(status_code=501)