s14 第7天 静态方法 类方法 属性方法 类的特殊成员方法 反射 异常处理 socket通信介绍

静态方法html

 

@staticmethod # 实际上跟类不要紧了python

 

class Dog():
    def __init__(self,name)
        self.name = namelinux

   

 

    @staticmethod编程

    def eat(self,food)
        print("{} is eating {}".format(self.name,food)json

 

在上面的类中,我实例化这个对象后调用eat函数就会有问题,由于增长了@staticmethod,一旦加入了这个,则其装饰的方法只是类里的方法,只是名义上归属这个类,但实际上和类没有关系了。既没法也没法由对象调用,也没法调用类的变量。windows

 

调用这个方法直接使用类调用,Dog.eat()api

 

若是仍然想要使用这个方法,则实际上须要将对象传入方法中缓存

 

d = Dog("dog")安全

d.eat(d,"baozi")服务器

 

可是实际看这个方法,这样确实和对象没有关系了,由于我向eat方法中传递了对象,那么即便是一个直接方法也能够调用self.name(由于在方法中定义了传递的形式参数就是self,这里的self和类方法中的self没有关系,这里的self只是个名字)

 

 

 

静态方法能够做为一个类的工具集,例如os这个模块同样

 

os里有不少方法彼此都没有什么关系,那么此时就能够直接建立静态方法定义一个os类 里面静态的一个个具体方法,我调用时也无需建立对象便可

 

 

 

只是名义上归类管理,实际上在静态方法里访问不了类或实例中的任何属性

 

类方法

 

 

@classmethod

 

class Dog():

    name = "xx"

 

    @classmethod

    def eat(self,food):

        print("{} is eating {}".format(self.name,food))

 

d = Dog()

d.eat("baozi")

 

 

类方法只能访问类的公有属性,不能访问类的成员属性。上例中没有指定成员属性(没有__init__),即使指定了 eat也没法调用

 

 

 

属性方法

 

@property

 

class Dog():

    def __init__(self,name):

        self.name = name

 

    @property

    def eat(self):

        print("{} is eating".format(self.name))

 

 

d = Dog("xx")

d.eat

 

正常调用eat方法确定是d.eat(),可是变为属性方法后,就不能增长括号了,而不括号的调用方式实际是调用静态属性的,例如d.name,因此属性方法就是讲类里面的方法变成一个静态属性

 

若是自己属性须要传递参数,则须要以下方法
 

class Dog():

    def __init__(self,name):

        self.name = name

        self.__food = None # 定义私有属性,

 

    @property

    def eat(self): # 属性方法

        print("{} is eating {}".format(self.name,self.__food))

 

    @eat.setter # 定义一个同名方法,给私有属性复制

    def eat(self,food):

        print("set the food:",food)

        self.__food = food

 

d = Dog("xx")

 

d.eat = "baozi" # 使用这种方式调用eat方法,并赋值私有属性

d.eat # 调用eat的属性方法

 

 

删除属性

del d.name

 

可是属性方法默认是没法删除的,

 

若是删除须要在定义一个同名函数

class Dog():

    def __init__(self,name):

        self.name = name

        self.__food = None

 

    @property

    def eat(self):

        print("{} is eating {}".format(self.name,self.__food))

 

    @eat.setter

    def eat(self,food):

        print("set the food:",food)

        self.__food = food

 

 

    @eat.deleter # 删除这个参数

    def eat(self):

        del self.__food

        print("删完了")

 

 

 

d = Dog("xx")

d.eat = "baozi"

d.eat

del d.eat # 调用删除参数

 

关于属性方法的总结

 

一、属性方法不须要传递参数的时候,能够直接使用@property,将其转换为属性方法

 

二、若是属性方法要传递参数,就须要:

a、将要传递的参数定义为一个私有属性

 

class Dog():

    def __init__(self,name):

        self.__food = None

 

b、定义这个属性方法

    @property

    def eat(self):

        print("{} is eating {}".format(self.name,self.__food)) # 这个方法中调用了私有属性

 

c、须要使用属性方法名.setter来装饰一个同名函数(本例就是@eat.setter),固然这个函数里能够走任何事,后面在执行时使用d.eat = XXX 的时候就是用来调用这个函数,因此不必定是为了给私有变量赋值

         @eat.setter

    def eat(self,food):

        print("set the food:",food)

        self.__food = food

 

d、若是须要删除这个属性(指的是self.__food,并不是eat这个方法),则须要使用方法名.deleter(eat.dedeter)来装饰同名函数,一样这里能够作任何事,后面del d.eat就是在调用这个函数

         @eat.deleter # 删除这个参数

    def eat(self):

        del self.__food # 删除私有属性

        print("删完了")

 

e、调用方法:

由于被eat.setter装饰了,所以在传递参数时就不能使用d.eat("baozi"),而是应该使用

d.eat = "baozi"

 

若是要删除这个属性,咱们已经定义了一个被eat.deleter装饰的方法,这样才能使用del,不然就会报错

 

del d.eat  # 这里实际是删除的eat要传递的属性self.__food

 

三、属性方法相关的装饰器:@property @属性方法名.setter @属性方法名.deleter

 

 

 

类的特殊成员方法

 

一、__doc__:在建立类后,首先应该写类的文档注释,就是在class和def之间,而__doc__就是用来显示这个注释的

class Dog(object):

"""

SSSSSSS

"""

 

 

d = Dog

print(d.__doc__)

 

显示 “SSSS”

 

 

二、__module__和__class__

__module__:表示当前操做的对象在哪一个模块

例如一个C的类是从lib/aa这个文件中的,咱们进行from lib.aa import C

obj = C()

print(obj.__module__)

就是现实lib.aa

 

__class__:则就是显示这个类自己的位置

 

三、__init__:构造方法

四、__del__:析构方法,在对象被销毁时执行,例如执行一个终止链接

五、__call__:

 

d = Dog()  # 实例化对象

d() # 对象+括号,这样实际是调用类中的__call__方法,若是类中没有定义则这样写会报错

 

class Dog(object):

    def __init__(self)

pass

    def __call__(self,*args,**kwargs):
        print(self.__dict__)

 

d = Dog()

d() # 这样调用就执行了__call__

Dog()()也能够

 

六、__dict__:

   

 

七、__str__:

 

若是直接打印对象print(d),这样实际出来的是这个对象的内存地址。可是若是我在类中定义了str

 

def __str__(self):

    return "obj:{}".format(self.name)

 

那么当我print(d)的时候就会打印这个return里的内容

 

八、__getitem__、__setitem__、__delitem__:

 

将一个对象字典化

 

class TestDict(object):

    def __init__(self):

        self.dict1 = {}

 

    def __setitem__(self, key, value): # 下面的test["name"] = "liubo"就是调用这个方法,一样的这个只是调用这个方法,可是是否执行为self.dict1[key] = value则是基于setitem方法中定义的结果

        print("setitem",key,value)

        self.dict1[key] = value

 

    def __getitem__(self, key): # 下面name = test["name"] 是调用这个方法,返回的是获取的key的值,可是也能够定义其余内容

        print("getitem",key)

        return self.dict1.get(key)

 

    def __delitem__(self, key): # 下面的del 调用这个方法,只是触发这个方法,如何执行全看方法中执行什么内容,所以若是须要删除内容甚至能够在其中增长一个用户验证的动做

        print("getitem",key)

        del self.dict1[key]

 

 

test = TestDict()

 

test["name"] = "liubo"

 

name = test["xxx"]

print(name)

 

del test["name"]

 

print(test.__dict__)

 

 

类的源头是type,任何一个类均可以使用type来制做,一切皆对象,即使是类也是对象,任何类都是type的对象:

 

def __init__(self,name,age):
    self.name = name

    self.age = age

 

def func(self)

    print(self.name)

 

Foo = type("Foo",(object,),{"func":func,"__init__":__init__}) # 必须写为(object,), 括号里面有一个逗号

 

上面的内容等价于

class Foo(object):

    def __init__(self,name,age):
        self.name = name

        self.age = age

   

    def func(self):

        print("123")

 

 

Type的起源则是python解释器内部定义的

 

 

类的建立过程:

第一阶段:python解释器从上到下执行代码建立Foo类

第二阶段:经过Foo类建立obj对象

这个阶段,其实是经过类中定义的默认方法__new__来实例化对象的,这个方法会先于__init__方法执行,而且任何一个类都默认含有这个方法,无需特别建立

__new__方法的写法

 

class Foo(object):

 

def __new__(cls,*args,**kwargs):

    print("Foo --new--")

    return object.__new__(cls) # cls就是对象,如前所述任何对象都是经过__new__来实例化的,所以这个return返回的是object的__new__,实际就是从父类继承__new__若是当前类覆盖了__new__而没有这样操做,则这个__new__就没有意义,也就没法实例化对象。任何类都是object的子类,所以这个__new__方法必须这样写

 

 

 

反射:

经过字符串映射或修改程序运行时的状态、属性、方法,有一下4种方法:

getattr(object,name,default=None)

hasattr(object,name)

setattr(x,y,v)

delattr(x,y)

 

好比 我有一个类 里面有一个eat方法

class Dog(object):

    def __init__(self,name):

        self.name = name

   

    def eat(self)

        print("%s is eating..." % self.name)

 

我但愿判断这个里面是否存在一个eat方法,用

d = Dog()

choice = intpu("input fucn:")  # 例如输入一个eat

if choice in d # 看eat是否在d这个对象中,这样确定是错的不可能实现

或者

d.choice # 想这样调用eat方法也是不可能的。

 

所以 须要用到反射

print(hasattr(d,choice))

查看eat是否在d中要用的hasattr()方法,返回True/False

getattr(d,choice)()

至关于调用这eat这个函数,getattr(d,choice)返回的是这个方法的内存地址,须要再用括号调用

 

若是调用的方法里面须要用到参数,则能够将其赋值后传递便可

 

 

判断对象是否存在,并执行

func = getattr(d,choice)

func(name,age)

 

class Dog(object):

    def __init__(self,name):

        self.name = name

   

    def eat(self,food)

        print("{} is eating...{}" .format( self.name,food))

 

d = Dog()

choice = input("input func:")

if hasattr(d,choice):

    func = getattr(d,choice)

    func("baozi")

 

setattr是设置一个方法,关联到这个类中

 

def bulk(self):

    print("{} is yelling..." format(self.name))

 

 

class Dog(object):

    def __init__(self,name):

        self.name = name

   

    def eat(self,food)

        print("{} is eating...{}" .format( self.name,food))

 

 

choice = input("input func:") # 此处输入talk

setattr(d,choice,bulk) #将bulk方法关联进d对象,并将其重命名为choice输入的名字

d.talk(d) # 调用talk,这里talk是choice的值,所以虽然这个方法的名字是bulk,可是关联进类之后的方法应该是talk,同时由于bulk要了一个self,所以还须要将对象传入talk中

 

setattr(d,choice,bulk) 这个方法的3个参数,应该是对象,对象中的属性名,属性的值。在这里bulk是一个方法,这里不加括号因此是bulk的函数体,因此才能将第二个参数和第三个参数关联起来。

 

因此choice = bulk ,执行choice(),加上对象就是d.choice()

 

所以若是要直接给类中增长属性不是方法,也能够这样

 

setattr(d,name,"liubo"),setattr(d,name,None) ,setattr(d,age,30)等等

 

delattr(obj,attr) # delattr(d,choice) 删除d对象中choice属性或方法,choice是用户输入的名字

 

 

反射的实际使用,当一个类里面定义了多个方法,须要用户按1 按2来调用这个方法,就可使用下面的代码,而无需

 

if choice = 1

xxx

elif choice = 2

          xxx

 

代码以下

 

 

 

class client(object):

def __init__:

 

def login(self):

代码

 

 

TFTP_Client = client() # 实例化对象

 

func_dict ={1,login} # 建立一个用户输入对应方法名字符串的列表

 

choice = input("input your func:") # 用户输入选择(1或2)

 

 

 

if hasattr(TFTP_Client,func_dict.get(choice)): # 首先判断这个实例中是否存在按键值对应的实例名

func = getattr(TFTP_Client,func_dict[choice]) # 而后赋值这个对象

func() # 执行他

 

 

 

 

 

 

 

 

 

 

 

异常处理

 

格式:

try:

    代码

except 报错类型 as e:# e表明当出现这个错误时的详细信息,通常报错的但是为:错误类型:详细信息

    报错时执行的代码   ,这里能够调用e

 

例如

data = {}

try:

    data["name"] # 空字典没有name这个key,会出现KeyError:name的报错

except KeyError as e:

    print("没有这个KEY",e)

 

这样就会显示

没有这个KEY name(name就是e)

 

能够一个try对应多个except例如

 

names = ["alex","jack"]

data = {}

try:

    name[3] # 没有这个元素

    data["name"]

except KeyError as e:

    print("没有这个key:",e)

except IndexError as e:

    print("列表错误",e)

 

这个时候就会显示

 

列表错误:list index out of range

 

为何上面的例子没有执行字典呢,由于当执行到列表时 实际就已经触发了错误,没有except程序就已经报错终止了,如今有了except也只是转到了except执行他的代码,try后面的内容都不会执行了

 

也可使用一个except来对应多种报错,将报错类型定义为一个元组

except (IndexError,KeyError) as e:

 

可是这种写法咱们没法区分具体是哪行,哪一个问题出发了错误因此通常不这么写

 

上面的状况是当出现这两种问题使用一样的处理就能够这么写,可是这样须要对应每一种错误,还有一种写法能够默认匹配全部报错类型而无需一一写出:

 

except Exception as e:

    print("there has an Error:",e)

 

Exception类型默认匹配一切报错,一样由于定位问题一般不用,可是有一种状况就是用来在多条except的最后使用这个类型来匹配一种考虑的全部状况之外的报错,位置错误

 

try:

    code

 

except KeyError as e:

    print("没有这个key:",e)

 

except IndexError as e:

    print("列表错误",e)

 

except Exception as e:

    print("未知错误",e)

 

当try没有遇到报错时,后面能够匹配else来输出,else只有在没有error时才会匹配

 

try:

    code

 

except KeyError as e:

    print("没有这个key:",e)

 

except IndexError as e:

    print("列表错误",e)

 

except Exception as e:

    print("未知错误",e)

 

else:
    print("一切正常")

 

无论有没有错误,都执行的内容是finally

 

try:

    code

 

except KeyError as e:

    print("没有这个key:",e)

 

except IndexError as e:

    print("列表错误",e)

 

except Exception as e:

    print("未知错误",e)

 

else:
    print("一切正常")

 

finally:

    print("无论有错没错,都执行")

 

最后的执行结果总结:

 

当执行try后,有错误执行 except --- finally

    无错误执行 else --- finally

 

自定义异常

class AlexExpection(Expection):

    def __init__(self,msg):

        self.message = msg

 

    def __str__(self): # 定义__str__方法时,print(对象)就不会显示对象的内存地址,而是显示str方法返回的内容。可是这个步骤不用写,由于在其父类Expection中已经定义了,没有必要复写

        return self.message

 

 

try:

    raise AlexExpection("个人异常") # raise抛出异常,由于系统不会自动触发自定义异常,须要使用raise触发,括号内对应的就是异常的详细内容,即类中的self.message的值msg,实际就是抛出(raise)异常对象(AlexExpection("个人异常"))

 

except AlexExpection as e: # e就是"个人异常",其动做就是print(AlexExpection)这个类,

    print(e)

 

 

socket网络编程

 

socket是对全部上层协议进行底层封装,这样开发者无需了解详细的底层操做也能够完成网络协议的交互,将TCP/IP进行封装

 

socket 地址簇(网络层)

 

socket.AF_UNIX

unix本机进程间通信

默认状况下进程与进程处于安全考虑是不能互相访问的,要想进行通信就须要使用到AF_UNIX来传递

socket.AF_INET

ipv4

 

socket.AF_INET6

ipv6

 

 

socket type(传输层)

socket.SOCK_STREAM

对TCP协议(传输层)

 

socket.SOCK_DGRAM

对UDP协议(传输层)

 

socket.SOCK_RAW

原始套接字(网络层)

普通的套接字没法处理IGMP,可是SOCK_RAW能够,SOCK_RAWyekeyi chuli teshu de IPV4报文,此外李勇原始套接字,能够经过IP_HDRINCL套接字选项由用户构造IP头,也就是伪造IP地址

socket.SOCK_RDM

可靠的UDP形式

保证交付数据报,可是不保证顺序,SOCK_RAM用来提供对原始协议的低级访问,在须要执行某些特殊操做时使用,如发送ICMP报文,SOCK_RAM一般仅限于高级用户或管理员运行的程序使用

socket.SOCK_SEQPACKET

废弃了

 

 

 

简单实例

 

客户端

import socket

 

client = socket.socket() # 声明socket类型,同时生成socket链接对象,def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):默认是IPv4 TCP的

client.connect(("localhost",6969)) # 链接须要有IP和端口,所以须要传入一个元组,元组的第二个元素时端口 int类型无需引号

 

client.send(b"Hello World!") # 链接后发送数据内容,在python2.7里 容许直接发送字符串,但3.0强制要求必须发送字节码,因此要有b

 

data =  client.recv(1024) # 将接受的数据赋值给data 1024表明接受的最大字节数

 

print("recv:",data)

 

client.close()

 

 

服务器端

 

import socket

 

server = socket.socket()

server.bind(("localhost",6969)) # 指定监听地址和端口

 

print("开始监听")

server.listen() # 开始监听,括号内指定最大排队数

 

# server.accept()

# data = server.recv(1024)

# 本想直接使用这个,可是

# 等待客户端链接,咱们并不能直接使用这个实例来接受数据,

# 由于若是这样作至关于这个程序只能接受一个链接这是不被容许的,

# 使用server.accept()会返回客户端的链接标示和链接信息,咱们应该使用每个独立的链接来获取数据

# 就像一条模拟线FXO只能同时接受一个电话,如今换成E1并发30路(30B+D),可是咱们不能使用E1来接电话,而应该用E1的具体信道

 

conn,addr = server.accept()

print("{}传来数据".format(conn))

data = conn.recv(1024) # 特定标示接受到的数据,1024表明最大接受的字节数

 

print("recv:",data)

 

print("向{}返回数据".format(conn))

conn.send(data.upper()) # 返回数据,将受到的数据大写,一样是经过标示来返回

 

server.close() # 若是要关闭链接,仍然须要结束对象而不是具体的链接

 

可是在这个实例中,发送的数据必须是英文的,由于必须是byte的类型,这个类型只能传递ASCII码,若是要传递中文则须要将其encode,在接收端也要decode

 

client

 

client.send("中文数据abcd".encode("utf-8")# 编码为utf-8

 

server

print("recv:",data.decode()) # 将收到的内容解码

 

这样返回的数据中文则是中文,英文会被大写

 

 

 

 

 

经过socket实现处理多个链接

 

客户端须要反复的发送,须要调整客户端:

 

import socket

 

client = socket.socket()

 

client.connect(("localhost",6969))

 

while True: # 增长循环

 

    msg = input(">>:").strip()

 

    client.send(msg.encode("utf-8")) # 链接后发送数据内容,在python2.7里 容许直接发送字符串,但3.0强制要求必须发送字节码,因此要有b

 

    data =  client.recv(1024) # 将接受的数据赋值给data 1024表明接受的最大字节数

 

    print("recv:",data.decode())

 

client.close()

 

服务端反复接受数据也须要增长循环,可是在这之中循环增长的位置会有差别:

 

状况1:能够多个客户端,但不准同一个客户端反复发送数据

 

import socket

 

server = socket.socket()

server.bind(("localhost",6969)) # 指定监听地址和端口

 

print("开始监听")

server.listen() # 开始监听,括号内指定最大排队数

 

while True: # 若是在这个位置开始循环,则服务端会接受来自每一个不一样的客户端发来的一个语句,同一个客户端的第二句之后的内容都不会受到

 

 

    conn,addr = server.accept()

 

    print("{}传来数据".format(conn))

 

    data = conn.recv(1024) # 特定标示接受到的数据,1024表明最大接受的字节数

 

    print("recv:",data.decode())

 

    print("向{}返回数据".format(conn))

 

    conn.send(data.upper()) # 返回数据,将受到的数据大写,一样是经过标示来返回

 

server.close() # 若是要关闭链接,仍然须要结束对象而不是具体的链接

 

状况2:能够一个客户端反复发送,但不支持多客户端

 

 

import socket

 

server = socket.socket()

server.bind(("localhost",6969)) # 指定监听地址和端口

 

print("开始监听")

 

server.listen() # 开始监听,括号内指定最大排队数

 

conn,addr = server.accept()

 

print("{}传来数据".format(conn))

 

while True: # 若是在这个位置开始循环,则服务器容许一个客户端反复发送,可是不支持多个客户端。这个程序会在客户端control-c结束程序后结束

 

    data = conn.recv(1024) # 特定标示接受到的数据,1024表明最大接受的字节数

 

    print("recv:",data.decode())

 

    print("向{}返回数据".format(conn))

 

    conn.send(data.upper()) # 返回数据,将受到的数据大写,一样是经过标示来返回

 

server.close() # 若是要关闭链接,仍然须要结束对象而不是具体的链接

 

可是现阶段只能实现第二种状况,可是第二种状况也有问题,也就是他只能接受一个客户端的。为了他能够依次接受多个客户端须要再增长一个循环

 

这个程序的问题是,必须第一个客户端终止进程,第二个客户端才能够链接

 

import socket

 

server = socket.socket()

server.bind(("0.0.0.0",6969)) # 指定监听地址和端口

 

print("开始监听")

server.listen() # 开始监听,括号内指定最大排队数

 

while True:

 

    connid,client_info = server.accept()

    print("{}传来数据".format(client_info))

 

    while True: # 若是在这个位置开始循环,则服务器容许一个客户端反复发送,可是不支持多个客户端

 

        data = connid.recv(1024) # 特定标示接受到的数据,1024表明最大接受的字节数

 

        if not data: # 这段代码在windows下运行,client断开则server断开,可是在linux下时client断开则server持续接受到空值陷入死循环,所以必须增长这个判断来阻止linux下死循环的发生

            print("client has lost")

            break

 

        print("recv:",data.decode())

 

        print("向{}返回数据".format(client_info))

       

        connid.send(data.upper()) # 返回数据,将受到的数据大写,一样是经过标示来返回

 

server.close() # 若是要关闭链接,仍然须要结束对象而不是具体的链接

 

上面的状况在windows上没法进行,只能在linux中运行,可是linux下运行当client结束,server会持续受到空字符致使死循环,所以中间须要增长一个判断来阻止死循环、

 

 

 

同时client端也存在问题,切记 client不容许发送空值,若是发送空值,什么都不输入直接回车,会形成client和server同时卡死

 

client修改以下:

 

import socket

 

client = socket.socket() # 声明socket类型,同时生成socket链接对象,def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):默认是IPv4 TCP的

client.connect(("1.1.1.102",6969)) # 链接须要有IP和端口,所以须要传入一个元组,元组的第二个元素时端口 int类型无需引号

 

while True:

    msg = input(">>:").strip()

 

    if not msg:continue # 这一步是为了验证若是输入为空的时候,就不会发送,由于程序不容许发送空值

       

    client.send(msg.encode("utf-8")) # 链接后发送数据内容,在python2.7里 容许直接发送字符串,但3.0强制要求必须发送字节码,因此要有b

 

    data =  client.recv(1024) # 将接受的数据赋值给data 1024表明接受的最大字节数

 

    print("recv:",data.decode())

 

client.close()

 

 

在client执行结果不变的状况下,若是咱们要让server段接受数据并执行操做系统的命令能够这样修改

 

 

关于socket发送和接受数据的说明:

 

 

一个数据不管是发送仍是接受,其一次发送的大小都是有限制的

 

发送:

 

对于发送来讲,发送的数据取决于对端接受数据的能力,若是发送的数据的总大小超过了对端接受的能力,则超出对端接受能力的数据会暂存在操做系统的发送缓存区里,每次新发送数据以前都要将缓存区里的数据发送,数据是排队发送的。

 

若是发送一个超过对端缓存区的数据,使用send方法,实际每次只会发送对端接受的大小,不然就须要循环send,固然也可使用sendall()方法,这个方法内部就是一个循环send的过程,关于sendall的详细内容将在下次课讲解

 

 

 

接受:

 

对于接收来讲,接收数据首先取决于recv()里面指定的大小,若是数据超过这个数值,则会缓存在发送方的缓存中,另外根据操做系统的不通,一次接收数据的大小也受限于操做系统,在有些操做系统中当设置的recv值超过操做系统所容许的值,会报错。有些则会以操做系统的值为最大值接受数据。

 

 

 

关于传输文件:

 

一般来讲传递通常的数据,咱们能够在发送时

 

self.client.send(json.dumps(data).encode("utf-8"))

 

接收时

 

data=json.loads(self.client.recv(10240).decode())

 

也就是发送时先json 在encode,接收时先decode在json这样能够发送任何字符串或者json支持的数据类型,固然若是换成pickle也同样

 

 

python3 必需要用byte类型,所以json能够胜任。

 

可是当发送文件的时候,文件必须是要用字节码打开,并发送,对方也要接受字节码写入文件,文件才能使用。所以就不能 用上面的方法了

 

对于发送文件,须要使用"rb"模式读取,而且直接使用send方法直接发送,对方也要直接接受而且以ab或wb写入,不能够作任何解码

 

发送

with open(file_path,"rb") as file:

    for line in file:

      self.client.send(line)

 

接受

 data = self.connid.recv(10240)

     with open(path,"ab") as file:

       file.write(data)

 

 

 

在发送文件时,应该是将文件大小发送给对端,而后每次发送对端须要计算下当前收到的总大小来判断文件是否接收完成,

断点续传的功能是,接收端获取以前收到的数据的大小和总大小比对,若是不到总大小就将当前获取的总字节数发送给发送端,文件操做中的seek方法实际就是以字节为单位调整指针位置的,那么接收端发来的数量就是file.seek(接收端发来的数据),而后从这里开始read(字节数)发送指定的字节

 

 接收端

def upload(self,filename,fullsize):

        """

        上传文件方法

        :param filename: 须要在服务端建立的文件名

        :param fullsize: 文件尺寸,用来检测文件是否传递完成

        :return:

        """

        path = os.path.join(root,"server_db",self.current_user,filename)

        open(path,"w").close()

        while True:

            data = self.connid.recv(10240)

            with open(path,"ab") as file:

                file.write(data)

            current_size = os.path.getsize(path)

            if fullsize <= current_size:

                break

 

 

发送端

 

def upload_data(self):

        file_path = input("please input the file path:")

        if not os.path.exists(file_path):

            print("this file is not exists")

            self.send_data(("error",))

            return

        else:

            fullsize = os.path.getsize(file_path)

            server_filename = input("please input the file name in server:")

            self.send_data(("upload",server_filename,fullsize))

            with open(file_path,"rb") as file:

                for line in file:

                    self.client.send(line)

                print("文件发送完成")

 

 

 

2048: contant = f. read(2048) len(contant) r size = self.socketsend(contant) sended size += r size common.print_process(fsize, sended size) else: contant = f.read(fsize - sended size) self.socketsend(contant) common.print_processifsize, fsizei successful'.format(file name), •info') return " src="file:///C:\Users\LiuBo\AppData\Local\Temp\msohtmlclip1\02\clip_image001.png">

 

 

 

2048: recv data = client socket.recv(2048) fa write(recv data) recv size len(recv data) if recv data dbapi.write_breakpoint(filemd5, filesize, recv size, save_path, client user) break else: recv data = client socket.recv(filesize - recv size) if recv data b": dbapi.write_breakpoint(filemd5, filesize, recv size, save_path, client user) common.writelogCCIient upload file connected closed', •error') fa write(recv data) " src="file:///C:\Users\LiuBo\AppData\Local\Temp\msohtmlclip1\02\clip_image002.png">

相关文章
相关标签/搜索