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">

相關文章
相關標籤/搜索