後面學習了線程、協成和異步,它們的框架都是基於socket的協議,基本原理都是同樣的,如今把這幾個模塊重溫一下,儘可能掌握這些知識更全面一些。json
動態導入模塊,知道知道模塊名,能夠像反射同樣,使用字符串來導入模塊。服務器
mod = __import__("mulit") print(mod) mod.f("alex")
運行結果以下:
<module 'mulit' from '/home/zhuzhu/day10/mulit.py'>
called from child process function f
module name: mulit
parent process: 2092
process id: 3613
hello alex
上面就實現了動態導入模塊的方法,動態導入模塊。官方建議使用importlib模塊實現模塊字符串的動態導入,以下:微信
import importlib mulit = importlib.import_module("importy.mulit") print(mulit)
運行結果以下:
<module 'importy.mulit' from '/home/zhuzhu/day10/importy/mulit.py'>
上面代碼就實現了動態導入importy包下的mulit模塊,這樣,咱們就可以實現,只知道模塊的字符串名字,動態的導入模塊。網絡
斷言:框架
'''斷言''' name = "alex" print(name) assert type(name) is int print("斷言是如何算的") '''斷言:若是是正確的,程序繼續執行下面;若是是錯誤的,程序就執行錯誤。報錯AssertionError,直接停住不執行,後面無論對錯''' #斷言的做用:就是沒有循環的中斷程序。執行錯誤。
斷言這個也在後期用到,即終端操做的時候。異步
socket:封裝協議底層,如今全部的底層都是基於socket來實現的。網絡協議socket
conn,addr = socket.accept()學習
必定要記住,socket.accept()是接收了一個連接和一個地址,conn去實現連接,conn.recv()來接收,由於conn是連接。spa
recv(1024) 官方建議接收數據不能超過8192(8K);線程
conn.recv(1024)和conn.send()是實現數據的收發,數據必定要有收發配對,防止粘包。而且只能收發二進制字符串格式。要轉換爲二進制才能進行發送。
不能接收和發送空的數據,recv()不能接收空的消息,接收空的消息就會卡主。微信、QQ咱們知道,都不容許發送空的消息。
下面使用類建立了一對sockek客戶端和socket服務器端,實現數據的收發,以下所示:
服務器端:
import socket class MyServer(object): '''建立服務器端''' def __init__(self): self.server = socket.socket() def bind(self,ip,port): '''綁定鏈接''' self.server.bind((ip,port)) self.server.listen() def interactive(self): while True: '''實現接收終止,可是服務器端沒有斷開,等待新的連接進來''' conn, addr = self.server.accept() # 開始準備接收數據 while True: data = conn.recv(1024) if len(data) == 0: print("客戶端斷開了,接收數據不能爲空!!!") break conn.send("接收到了數據".encode("utf-8")) print(data.decode("utf-8")) if __name__ == "__main__": try: server = MyServer() server.bind("localhost",9998) server.interactive() except KeyboardInterrupt as e: print("服務器已經斷開了!!!",e)
客戶端:
import socket class Myclient(object): '''創建客戶端''' def __init__(self): self.client = socket.socket() def communicate(self,ip,port): self.client.connect((ip,port)) #創建連接 def interactive(self): '''實現交互''' while True: mess = input("請輸入您要交互的內容>>:").strip() if not mess: print("消息不能爲空!!") continue self.client.send(mess.encode("utf-8")) data = self.client.recv(1024) print(data.decode("utf-8")) if __name__ == "__main__": try: client = Myclient() client.communicate("localhost",9998) client.interactive() except KeyboardInterrupt as e: print("客戶端斷開了!!!")
上面就是客戶端和服務器端,首先要啓動的是服務器端,而後在客戶端進行收發數據,從而實現數據的交換,可是不能如何實現,單純的socket,不能實現多用戶交互,只有等一個用戶交互完成以後,才能讓另外一個用戶實現交互。
上面交互以下所示:
客戶端發送:
請輸入您要交互的內容>>:客戶端不能發送空的消息 接收到了數據 請輸入您要交互的內容>>:服務器端也不能發送空的消息 接收到了數據 請輸入您要交互的內容>>:客戶端和服務器端交互是一對的 接收到了數據 請輸入您要交互的內容>>:send和recv() 接收到了數據 請輸入您要交互的內容>>:若是主動斷開會報錯,因此加入了一個異常處理 接收到了數據 請輸入您要交互的內容>>:服務器端加入了兩層循環,主要目的是在內存循環中止接收數據以後,連接還存在,並無實現斷開 接收到了數據 請輸入您要交互的內容>>:客戶端斷開了!!!
服務器端接收:
客戶端不能發送空的消息
服務器端也不能發送空的消息
客戶端和服務器端交互是一對的
send和recv()
若是主動斷開會報錯,因此加入了一個異常處理
服務器端加入了兩層循環,主要目的是在內存循環中止接收數據以後,連接還存在,並無實現斷開
客戶端斷開了,接收數據不能爲空!!!
服務器已經斷開了!!!
上面就是簡單的數據收發,要知道,線程,協成,進程都是基於socket實現的,並且不少方法都是相似的。
服務器端有兩層循環,外層循環是用來不斷接收新的連接,內存循環是用來不停接收用戶數據,由於若是接收中斷,當前連接是要終端的。只能等待新的連接進來,實現數據的交互。
上面服務器代碼中,data = conn.recv(1024),接收的數據大小是1024(1K)個字節,若是超過1K字節會出現什麼狀況,下面來看一下:
服務器接收:
正常傳輸數據沒有問題 30 當傳輸數據超過1024的時候,會出現怎樣的錯誤 61 My father was a self-taught mandolin player. He was one of the best string instrument players in our town. He could not read music, but if he heard a tune a few times, he could play it. When he was younger, he was a member of a small country music band. They would play at local dances and on a few occasions would play for the local radio station. He often told us how he had auditioned and earned a position in a band that featured Patsy Cline as their lead singer. He told the family that after he was hired he never went back. Dad was a very religious man. He stated that there was a lot of drinking and cursing the day of his audition and he did not want to be around that type of environment.Occasionally, Dad would get out his mandolin and play for the family. We three children: Trisha, Monte and I, George Jr., would often sing along. Songs such as the Tennessee Waltz, Harbor Lights and around Christmas time, the well-known rendition of Silver Bells. "Silver Bells, Silver Bells, its Christmas time in the city" w 5146 Traceback (most recent call last): File "/home/zhuzhu/第八天/socket_server.py", line 38, in <module> server.interactive() File "/home/zhuzhu/第八天/socket_server.py", line 22, in interactive length = int(length.decode("utf-8")) #接收數據的大小 ValueError: invalid literal for int() with base 10: 'ould ring throughout the house. One of Dad\'s favorite hymns was "The Old Rugged Cross". We learned the words to the hymn when we were very young, and would sing it with Dad when he would play and si
能夠看出當傳輸超過1024字節的時候報錯了,這個有時候能接收到報錯,有時候接收不到報錯,所以儘可能不要發超過1024字節的消息,要想發,只能修改服務器的代碼,讓服務器分段接收數據,以下:
import socket class MyServer(object): '''建立服務器端''' def __init__(self): self.server = socket.socket() def bind(self,ip,port): '''綁定鏈接''' self.server.bind((ip,port)) self.server.listen() def interactive(self): while True: '''實現接收終止,可是服務器端沒有斷開,等待新的連接進來''' conn, addr = self.server.accept() # 開始準備接收數據 while True: length = conn.recv(1024) if len(length) == 0: print("客戶端斷開了!!!") break length = int(length.decode("utf-8")) #接收數據的大小 conn.send("數據的長度接收到了!!!".encode("utf-8")) receive_size = 0 received_data = b"" while receive_size < length: data = conn.recv(1024) receive_size += len(data) received_data += data conn.send("接收到了數據".encode("utf-8")) print(received_data.decode("utf-8"),length) if __name__ == "__main__": try: server = MyServer() server.bind("localhost",9999) server.interactive() except KeyboardInterrupt as e: print("服務器已經斷開了!!!",e)
上述代碼中,首先服務器接收的不是消息,而是消息的長度,而後纔開始接收,經過判斷,只要接收的長度小於消息的長度,就一直接收,而且拼接到一塊兒,這樣就能接收超過1024字節,而且不會報錯,以下:
服務器端接收:
如今修改了服務器 24 讓服務器接收超過1024時,分批接收數據,而後拼接到一塊兒 76 西漢和東漢仍置南陽郡,轄境至關於河南熊耳山以南和湖北大湖 山以北,南陽經濟文化的發展達到歷史上的鼎盛時期。西漢時,南陽水利與關中鄭國渠、成都都江堰齊名,並稱全國三大灌區。全國設工官的9個地區和設鐵官的46個地區之一。東漢 時,光武帝劉秀起兵南陽,成就帝業,南陽被稱爲「帝鄉」。諸葛亮躬耕南陽臥龍崗,劉備三顧茅廬,諸葛亮提出「三分天下」之計。太守杜詩修治坡池,廣拓田土,全郡可灌農田4萬頃 ,這時的冶鐵用水排, 水力鼓風機鼓風,大大提升了冶鐵效率,特別是採用球墨鑄鐵,提 高了冶鐵工藝水平,這一技術的使用比歐州早1000多年。當時南陽郡人口240萬,爲全國各 郡之冠。郡城周長36千米,比1990年市區面積還大。漢代南陽人才濟濟,燦若繁星。不只劉秀的28個開國元勳大多出自南陽,還涌現出張衡、張仲景聞名世界的偉大科學家和醫學家。漢代達官貴人死後流行厚葬,南陽出土衆多的畫像石和畫像磚,是一部「繡像漢代史」,成爲中華民族文化藝術寶庫中一朵絢麗多彩的奇葩。三國時期:南陽爲魏國全部,隸屬荊州。晉代:南陽曾爲南陽國,轄十四縣,都宛。隋朝:(607年)先將郡改州,後又將州改郡,今南陽市轄南陽郡、淯陽郡、淅陽郡、淮安郡(包括平氏、桐柏二縣),義陽郡的淮源縣、舂陵郡的湖陽縣等都在今南陽市內。唐朝,天下設爲十道,南陽屬山南道管轄。自唐高祖李淵至玄宗李隆基90年間,南陽前後設置純州、酈州、淅州、北澧州、宛州、淯州、顯州、湖州、新州、魯州和仙州等。玄宗天寶元年(742年)改州爲郡,鄧州稱南陽郡,唐州稱淮安郡。肅宗乾 元元年(758年)又改郡稱州,縣加以合併,今南陽市有泌州淮安郡和鄧州南陽郡。通過貞觀 、開元之治,南陽農業興旺,工商業繁榮。李白在《南都行》中說:「清歌遏流雲,豔舞有 餘閒,邀遊盛宛洛,冠蓋隨風還。」宋朝:南陽歸京西南路管轄,南陽叫武勝軍,設唐、鄧 二州,州下設縣,鄧州轄穰、南陽、內鄉、淅川、順陽五縣。唐州轄有泌陽、湖陽、桐柏、方城等縣。元朝:將原南陽郡改成南陽府。屬河南江北行中書省。轄五州,其三在今市內; 南陽府自領鎮平、南陽二縣;鄧州領內鄉、順陽、淅川、新野、欒川五縣;唐州領泌陽、湖陽、桐柏、方城等縣。其餘二州轄臨汝、伊川、郟縣、寶丰、魯山、葉縣、舞陽、盧氏、欒川等縣。其間曾將淅川、順陽合併到內鄉。明朝初年,南陽是朱元璋第二十三子唐王朱檉的封地,永樂年間在南陽城內建造了規模宏大的唐王府,成化年間又建造9座郡王府,南陽城 內皇親貴胄,車水馬龍,商業隨之活躍,山、陝、江、浙、川、鄂客商紛到沓來,各類商務會館、公館在各地興起,糧食、棉花、生絲、菸草、綢緞、油料、皮毛、木材、藥材、銅器、鐵器等大量涌入市場,並行銷全國各地。當時的南陽可謂百業俱興,建築、園林、繪畫、雕塑、書法等方面都有新的發展。清朝康熙年間,建築業尤其發達,武侯、山陝會館等古建築巍巍壯觀,富麗堂皇,南陽是北京通往湖廣和雲貴川的交通要道,陸路驛道與水路碼頭相接,有「南船北馬」之稱。山、陝、江、浙商賈雲之集,工商業興旺,南陽成了豫西南的經濟中心。光緒十年,鎮平開始生產絲綢,並遠銷歐洲及東南亞各國。民國時期:河南設十一個行政區,南陽爲第六行政區,轄十三個縣:南陽縣、南召、唐河、鎮平、方城、鄧縣、內鄉、桐柏、新野、淅川、泌陽、葉縣、舞陽縣。中華人民共和國時期:(1949年)成立後,南陽行政公署划走葉縣、舞陽。析出西峽、南陽市,仍轄十三個縣市,即南陽市、南召、方城、泌陽、唐河、新野、桐柏 4184
上面,服務器端接收了超過1024字節的數據,可是一點問題都沒有。
ftp server發送文件給客戶端的步驟:
一、讀取文件名;
二、檢測文件是否存在;
三、打開文件;
四、檢測文件大小;
五、發送文件大小和md5給客戶端; md5驗證
六、等待客戶端確認;
七、開始邊讀邊發數據;
使用socket實現文件的傳輸,下面的例子是從服務器端下載文件,具體代碼以下:
服務器端:
import socket,os,json,hashlib class MyServer(object): '''建立server實例''' def __init__(self): self.server = socket.socket() def bind(self,ip,port): self.server.bind((ip,port)) self.server.listen() def interactive(self): '''實現交互''' while True: conn,addr = self.server.accept() #循環實現連接不斷開,客戶端斷開不影響 while True: filename = conn.recv(2048).decode("utf-8") #接收文件名 if len(filename) == 0: #判斷客戶端是否斷開,客戶端不可能發送空的,接收空,就是客戶端斷開了 print("客戶端斷開了!!!") break if os.path.isfile(filename): #判斷客戶端文件是否存在 '''文件名存在''' conn.send("1".encode("utf-8")) '''文件存在,準備發送文件,首先告知客戶端文件名和文件大小''' filesize = os.stat(filename).st_size file_mess = {"filesize":filesize} conn.recv(1024) conn.send(json.dumps(file_mess).encode("utf-8")) #發送文件的大小指令 conn.recv(1024) '''下面開始發送文件信息''' m = hashlib.md5() with open(filename,"rb") as f: for line in f: m.update(line) conn.send(line) #逐行發送文件 '''文件發送完畢以後,發送md5值''' mess = m.hexdigest() conn.send(mess.encode("utf-8")) else: '''文件不存在''' conn.send("0".encode("utf-8")) conn.recv(1024) if __name__ == "__main__": try: server = MyServer() server.bind("localhost",9999) server.interactive() except KeyboardInterrupt as e: print("服務器端斷開了!!!")
服務器裏面,交互有兩層循環,外層循環是保持連接一直存在,防止客戶端關閉以後,服務器端也斷開,正常不循環連接,客戶端斷開以後,服務端也會斷開的。而且要記住,若是客戶端斷開,conn.recv(1024)是接收空的數據,所以必定要進行判斷,若是接收爲空,說明是客戶端斷開了,由於客戶端是不可能發送空給服務器的。
客戶端:
'''經過接收發送數據的大小來訓話接收,若是接收完成,則退出''' import socket,json,hashlib class MyClient(object): '''建立Client實例''' def __init__(self): self.client = socket.socket() def connect(self,ip,port): self.client.connect((ip,port)) def interactive(self): '''實現交互,客戶端接收文件''' while True: filename = input("請輸入您要下載的文件名>>:").strip() if len(filename) == 0: print("文件名不能爲空!!!") continue elif filename == "q": break self.client.send(filename.encode("utf-8")) #發送文件名給服務器 file_exist = int(self.client.recv(1024).decode("utf-8")) #接收客戶端返回文件是否存在的指令 self.client.send("好的,收到反饋的指令!".encode("utf-8")) if file_exist: '''文件存在''' file_mess = json.loads(self.client.recv(1024).decode("utf-8")) filesize = file_mess["filesize"] self.client.send("收到文件的信息".encode("utf-8")) receive_size = 0 m = hashlib.md5() with open(filename+"1","wb") as f: while receive_size < filesize: if filesize - receive_size > 1024: #確保數據完整的接收,這樣就能防止粘包 size = 1024 else: size = filesize - receive_size # print("最後一次接收文件大小:",size) #因爲是逐行發送數據的,所以會出問題 data = self.client.recv(size) m.update(data) f.write(data) receive_size += len(data) #這裏判斷長度,必定是len(data)不能是size, server_md5 = self.client.recv(1024).decode("utf-8") print("客戶端md5值",m.hexdigest()) print("服務器md5值",server_md5) if server_md5 == m.hexdigest(): print("數據接收完畢,經過MD5驗證!!!") else: print("對不起,您要下載的文件不存在") continue if __name__ == "__main__": try: client = MyClient() client.connect("localhost",9999) client.interactive() except KeyboardInterrupt as e: print("客戶端斷開了!!!")
咱們知道,爲了防止粘包,咱們一般使用的方法是,客戶端--服務器端發送數據以後,雙方要進行確認,確認對方接收到了消息,並把這個結果告知對方,這樣就能防止粘包,還有上面這種狀況是,告知對方發送數據的大小,而後按照數量進行接收,只要確保所有數據接收完畢,那麼就必定不會形成粘包。形成粘包的緣由就是,一方沒有接收完畢數據,另一方有發送數據過來,這樣就形成了粘包。
上面的交互以下:
客戶端:
請輸入您要下載的文件名>>:file_test 最後一次接收文件大小: 329 最後一次接收文件大小: 304 客戶端md5值 576a9a93dc5377b7a204b48df224569d 服務器md5值 576a9a93dc5377b7a204b48df224569d 數據接收完畢,經過MD5驗證!!! 請輸入您要下載的文件名>>:file_test 最後一次接收文件大小: 329 最後一次接收文件大小: 304 客戶端md5值 576a9a93dc5377b7a204b48df224569d 服務器md5值 576a9a93dc5377b7a204b48df224569d 數據接收完畢,經過MD5驗證!!! 請輸入您要下載的文件名>>:客戶端斷開了!!!
上面在客戶端和服務器鏈接以後,客戶端發送指令,交互的狀況。能夠看出,交互沒有問題,要注意的是,判斷接收長度的時候,必定要len(data),而不能指定數量,由於只要接收數量小於等於規定的值就沒有問題。