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

静态方法

 

@staticmethod # 实际上跟类没关系了

 

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

   

 

    @staticmethod

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

 

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

 

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

 

如果仍然想要使用这个方法,则实际上需要将对象传入方法中

 

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 # 调用删除参数

 

关于属性方法的总结

 

1、属性方法不需要传递参数的时候,可以直接使用@property,将其转换为属性方法

 

2、如果属性方法要传递参数,就需要:

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

 

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

 

 

 

类的特殊成员方法

 

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

class Dog(object):

"""

SSSSSSS

"""

 

 

d = Dog

print(d.__doc__)

 

显示 “SSSS”

 

 

2、__module__和__class__

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

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

obj = C()

print(obj.__module__)

就是现实lib.aa

 

__class__:则就是显示这个类本身的位置

 

3、__init__:构造方法

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

5、__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()()也可以

 

6、__dict__:

   

 

7、__str__:

 

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

 

def __str__(self):

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

 

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

 

8、__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">

 

转载于:https://www.cnblogs.com/LB-BK/p/6493109.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值