python(二十三)
一 錯誤和異常
part1:程序中難免出現錯誤,而錯誤分成兩種
1.語法錯誤(這種錯誤,根本過不了python解釋器的語法檢測,必須在程序執行前就改正)
#語法錯誤示範一 if #語法錯誤示範二 def test: pass #語法錯誤示範三 class Foo pass #語法錯誤示範四 print(haha語法錯誤示範
2.邏輯錯誤(邏輯錯誤)
#用戶輸入不完整(比如輸入為空)或者輸入非法(輸入不是數字) num=input(">>: ") int(num)邏輯錯誤示範一
#無法完成計算 res1=1/0 res2=1+‘str‘邏輯錯誤示範二
part2:什麽是異常
異常就是程序運行時發生錯誤的信號,在python中,錯誤觸發的異常如下
part3:python中的異常種類
在python中不同的異常可以用不同的類型(python中統一了類與類型,類型即類)去標識,不同的類對象標識不同的異常,一個異常標識一種錯誤
AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x IOError 輸入/輸出異常;基本上是無法打開文件 ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤 IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊 IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5] KeyError 試圖訪問字典裏不存在的鍵 KeyboardInterrupt Ctrl+C被按下 NameError 使用一個還未被賦予對象的變量 SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了) TypeError 傳入對象類型與要求的不符合 UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由於另有一個同名的全局變量, 導致你以為正在訪問它 ValueError 傳入一個調用者不期望的值,即使值的類型是正確的
ArithmeticError AssertionError AttributeError BaseException BufferError BytesWarning DeprecationWarning EnvironmentError EOFError Exception FloatingPointError FutureWarning GeneratorExit ImportError ImportWarning IndentationError IndexError IOError KeyboardInterrupt KeyError LookupError MemoryError NameError NotImplementedError OSError OverflowError PendingDeprecationWarning ReferenceError RuntimeError RuntimeWarning StandardError StopIteration SyntaxError SyntaxWarning SystemError SystemExit TabError TypeError UnboundLocalError UnicodeDecodeError UnicodeEncodeError UnicodeError UnicodeTranslateError UnicodeWarning UserWarning ValueError Warning ZeroDivisionError
l=[‘egon‘,‘aa‘] l[3]觸發IndexError
dic={‘name‘:‘egon‘} dic[‘age‘]觸發KeyError
s=‘hello‘ int(s)觸發ValueError
二 異常處理
2.1 什麽是異常處理?
python解釋器檢測到錯誤,觸發異常(也允許程序員自己觸發異常)
程序員編寫特定的代碼,專門用來捕捉這個異常(這段代碼與程序邏輯無關,與異常處理有關)
如果捕捉成功則進入另外一個處理分支,執行你為其定制的邏輯,使程序不會崩潰,這就是異常處理
2.2 為何要進行異常處理?
python解析器去執行程序,檢測到了一個錯誤時,觸發異常,異常觸發後且沒被處理的情況下,程序就在當前異常處終止,後面的代碼不會運行,誰會去用一個運行著突然就崩潰的軟件。
所以你必須提供一種異常處理機制來增強你程序的健壯性與容錯性
2.3 如何進行異常處理?
首先須知,異常是由程序的錯誤引起的,語法上的錯誤跟異常處理無關,必須在程序運行前就修正
一: 使用if判斷式
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ #第一段代碼 num1=input(‘>>: ‘) #輸入一個字符串試試 int(num1) #第二段代碼 num2=input(‘>>: ‘) #輸入一個字符串試試 int(num2) #第三段代碼 num3=input(‘>>: ‘) #輸入一個字符串試試 int(num3)正常的代碼
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ num1=input(‘>>: ‘) #輸入一個字符串試試 if num1.isdigit(): int(num1) #我們的正統程序放到了這裏,其余的都屬於異常處理範疇 elif num1.isspace(): print(‘輸入的是空格,就執行我這裏的邏輯‘) elif len(num1) == 0: print(‘輸入的是空,就執行我這裏的邏輯‘) else: print(‘其他情情況,執行我這裏的邏輯‘) #第二段代碼 # num2=input(‘>>: ‘) #輸入一個字符串試試 # int(num2) #第三段代碼 # num3=input(‘>>: ‘) #輸入一個字符串試試 # int(num3) ‘‘‘ 問題一: 使用if的方式我們只為第一段代碼加上了異常處理,針對第二段代碼,你得重新寫一堆if,elif等 第三段,你還得在寫一遍,當然了,你說,可以合在一起啊,沒錯,你合起來看看,你的代碼還能被看懂嗎??? 而這些if,跟你的代碼邏輯並無關系,這就好比你在你心愛的程序中到處拉屎,拉到最後,誰都不愛看你的爛代碼,為啥,因為可讀性差,看不懂 問題二: 第一段代碼和第二段代碼實際上是同一種異常,都是ValueError,相同的錯誤按理說只處理一次就可以了,而用if,由於這二者if的條件不同,這只能逼著你重新寫一個新的if來處理第二段代碼的異常 第三段也一樣 ‘‘‘使用if判斷進行異常處理
總結:
1.if判斷式的異常處理只能針對某一段代碼,對於不同的代碼段的相同類型的錯誤你需要寫重復的if來進行處理。
2.在你的程序中頻繁的寫與程序本身無關,與異常處理有關的if,就像是在你的代碼中到處拉屎。可讀性極其的差
3.這是可以解決異常的,只是存在1,2的問題,所以,千萬不要妄下定論if不能用來異常處理。如果你不服,那你想想在沒有學習try...except之前,你的程序難道就沒有異常處理,而任由其崩潰麽
def test(): print(‘test running‘) choice_dic={ ‘1‘:test } while True: choice=input(‘>>: ‘).strip() if not choice or choice not in choice_dic:continue #這便是一種異常處理機制啊 choice_dic[choice]()打到你服為止
二:python為每一種異常定制了一個類型,然後提供了一種特定的語法結構用來進行異常處理
part1:基本語法
1 try: 2 被檢測的代碼塊 3 except 異常類型: 4 try中一旦檢測到異常,就執行這個位置的邏輯
f=open(‘a.txt‘) g=(line.strip() for line in f) ‘‘‘ next(g)會觸發叠代f,依次next(g)就可以讀取文件的一行行內容,無論文件a.txt有多大,同一時刻內存中只有一行內容。 提示:g是基於文件句柄f而存在的,因而只能在next(g)拋出異常StopIteration後才可以執行f.close() ‘‘‘ f=open(‘a.txt‘) g=(line.strip() for line in f) for line in g: print(line) else: f.close() try: f=open(‘a.txt‘) g=(line.strip() for line in f) print(next(g)) print(next(g)) print(next(g)) print(next(g)) print(next(g)) except StopIteration: f.close()View Code
part2:異常類只能用來處理指定的異常情況,如果非指定異常則無法處理。
1 # 未捕獲到異常,程序直接報錯 2 3 s1 = ‘hello‘ 4 try: 5 int(s1) 6 except IndexError as e: 7 print e
part3:多分支
1 s1 = ‘hello‘ 2 try: 3 int(s1) 4 except IndexError as e: 5 print(e) 6 except KeyError as e: 7 print(e) 8 except ValueError as e: 9 print(e)
part4:萬能異常 在python的異常中,有一個萬能異常:Exception,他可以捕獲任意異常,即:
1 s1 = ‘hello‘ 2 try: 3 int(s1) 4 except Exception as e: 5 print(e)
你可能會說既然有萬能異常,那麽我直接用上面的這種形式就好了,其他異常可以忽略
你說的沒錯,但是應該分兩種情況去看
1.如果你想要的效果是,無論出現什麽異常,我們統一丟棄,或者使用同一段代碼邏輯去處理他們,那麽騷年,大膽的去做吧,只有一個Exception就足夠了。
s1 = ‘hello‘ try: int(s1) except Exception,e: ‘丟棄或者執行其他邏輯‘ print(e) #如果你統一用Exception,沒錯,是可以捕捉所有異常,但意味著你在處理所有異常時都使用同一個邏輯去處理(這裏說的邏輯即當前expect下面跟的代碼塊)just do it
2.如果你想要的效果是,對於不同的異常我們需要定制不同的處理邏輯,那就需要用到多分支了。
s1 = ‘hello‘ try: int(s1) except IndexError as e: print(e) except KeyError as e: print(e) except ValueError as e: print(e)多分支
也可以在多分支後來一個Exception
s1 = ‘hello‘ try: int(s1) except IndexError as e: print(e) except KeyError as e: print(e) except ValueError as e: print(e) except Exception as e: print(e)View Code
part5:異常的其他機構
1 s1 = ‘hello‘ 2 try: 3 int(s1) 4 except IndexError as e: 5 print(e) 6 except KeyError as e: 7 print(e) 8 except ValueError as e: 9 print(e) 10 #except Exception as e: 11 # print(e) 12 else: 13 print(‘try內代碼塊沒有異常則執行我‘) 14 finally: 15 print(‘無論異常與否,都會執行該模塊,通常是進行清理工作‘)
part6:主動觸發異常
1 #_*_coding:utf-8_*_ 2 __author__ = ‘Linhaifeng‘ 3 4 try: 5 raise TypeError(‘類型錯誤‘) 6 except Exception as e: 7 print(e)
part7:自定義異常
1 #_*_coding:utf-8_*_ 2 __author__ = ‘Linhaifeng‘ 3 4 class EgonException(BaseException): 5 def __init__(self,msg): 6 self.msg=msg 7 def __str__(self): 8 return self.msg 9 10 try: 11 raise EgonException(‘類型錯誤‘) 12 except EgonException as e: 13 print(e)
part8:斷言
1 # assert 條件 2 3 assert 1 == 1 4 5 assert 1 == 2
part9:try..except的方式比較if的方式的好處
try..except這種異常處理機制就是取代if那種方式,讓你的程序在不犧牲可讀性的前提下增強健壯性和容錯性
異常處理中為每一個異常定制了異常類型(python中統一了類與類型,類型即類),對於同一種異常,一個except就可以捕捉到,可以同時處理多段代碼的異常(無需‘寫多個if判斷式’)減少了代碼,增強了可讀性
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ # num1=input(‘>>: ‘) #輸入一個字符串試試 # if num1.isdigit(): # int(num1) #我們的正統程序放到了這裏,其余的都屬於異常處理範疇 # elif num1.isspace(): # print(‘輸入的是空格,就執行我這裏的邏輯‘) # elif len(num1) == 0: # print(‘輸入的是空,就執行我這裏的邏輯‘) # else: # print(‘其他情情況,執行我這裏的邏輯‘) #第二段代碼 # num2=input(‘>>: ‘) #輸入一個字符串試試 # int(num2) #第三段代碼 # num3=input(‘>>: ‘) #輸入一個字符串試試 # int(num3) try: #第一段代碼 num1=input(‘>>: ‘) #輸入一個字符串試試 int(num1) #我們的正統程序放到了這裏,其余的都屬於異常處理範疇 #第二段代碼 num2=input(‘>>: ‘) #輸入一個字符串試試 int(num2) #第三段代碼 num3=input(‘>>: ‘) #輸入一個字符串試試 int(num3) except ValueError as e: print(e)再看try...except的牛逼之處
使用try..except的方式
1:把錯誤處理和真正的工作分開來
2:代碼更易組織,更清晰,復雜的工作任務更容易實現;
3:毫無疑問,更安全了,不至於由於一些小的疏忽而使程序意外崩潰了;
三 正本清源
一:有人說:異常處理就是try...except,if啥的跟異常處理沒關系----->我真特麽無語了
很多人,根本沒有搞清楚什麽是異常處理,因為他們還沒有搞清楚什麽是異常,什麽是處理,就混在一起去整了。
什麽是異常,異常就是錯誤引發的結果
什麽是處理,由於異常帶來的是程序崩潰,處理的目的就是讓程序在異常後跳轉到其他邏輯去執行,不讓程序崩潰
綜上
if本身就可以來處理異常,只不過if的方式,對於不同代碼段的同一種異常,需要重復寫多分支的if,而這段多分支if與真正的工作無關,寫多了你的程序可讀性就會及其的差。
try..except的方式,只是python提供給你一種特定的語法結構去做這件事,對於不同代碼的同一種異常,python為你定制了一中類型,一個expect就可以捕捉到
二:有人說:用if來做異常處理,缺點是有些異常是未知的,只要用try...except Exception就可以捕捉所有異常了,所以要使用異常處理----->我聽的都哭了
首先,沒有搞清楚什麽是異常處理,他以為只有try...except才叫異常處理,我哭暈了一次
其次,他想表達的是使用try...except要好,但是理由說的太...,讓我又哭暈了一次
try...excpet的多分支就好比if的多分支啊,if的else就好比try的Exception,只不過if是針對一種異常的多分支,針對不同段代碼的同種類型錯誤,你需要重復寫多分支if,而try是針對不同類型異常的多分支,可以把不同段代碼放到一起,檢測他們的同種類型錯誤==========》能說出上面紅字的話,真是被蠢??了
1 #_*_coding:utf-8_*_ 2 __author__ = ‘Linhaifeng‘ 3 4 try: 5 ‘代碼‘ 6 except TypeError:#其他的異常1 7 ‘其他的邏輯1‘ 8 except KeyError:#其他的異常2 9 ‘其他的邏輯2‘ 10 except Exception:#其他的我沒有考慮到的異常 11 ‘其他的邏輯3‘ 12 13 if ‘條件‘: 14 ‘代碼‘ 15 elif ‘其他條件1‘: 16 ‘其他的邏輯1‘ 17 elif ‘其他條件2‘: 18 ‘其他的邏輯2‘ 19 else: #其他的我沒有考慮到的條件 20 ‘其他的邏輯3‘
如果你還問我那try的else又是啥,對不起,我不知道(try的else跟if的else根本不是一回事啊)
四 什麽時候用異常處理
有的同學會這麽想,學完了異常處理後,好強大,我要為我的每一段程序都加上try...except,幹毛線去思考它會不會有邏輯錯誤啊,這樣就很好啊,多省腦細胞===》2B青年歡樂多
try...except應該盡量少用,因為它本身就是你附加給你的程序的一種異常處理的邏輯,與你的主要的工作是沒有關系的
這種東西加的多了,會導致你的代碼可讀性變差
而且異常處理本就不是你2b邏輯的擦屁股紙,只有在有些異常無法預知的情況下,才應該加上try...except,其他的邏輯錯誤應該盡量修正
socket網絡編程
一 客戶端/服務器架構
即C/S架構,包括
1.硬件C/S架構(打印機)
2.軟件C/S架構(web服務)
美好的願望:
最常用的軟件服務器是 Web 服務器。一臺機器裏放一些網頁或 Web 應用程序,然後啟動 服務。這樣的服務器的任務就是接受客戶的請求,把網頁發給客戶(如用戶計算機上的瀏覽器),然 後等待下一個客戶請求。這些服務啟動後的目標就是“永遠運行下去”。雖然它們不可能實現這樣的 目標,但只要沒有關機或硬件出錯等外力幹擾,它們就能運行非常長的一段時間。
生活中的C/S架構:
老男孩是S端,所有的學員是C端
飯店是S端,所有的食客是C端
互聯網中處處是C/S架構(黃色網站是服務端,你的瀏覽器是客戶端;騰訊作為服務端為你提供視頻,你得下個騰訊視頻客戶端才能看狗日的視頻)
C/S架構與socket的關系:
socket就是為了完成C/S架構的開發
二 osi七層
引子:
須知一個完整的計算機系統是由硬件、操作系統、應用軟件三者組成,具備了這三個條件,一臺計算機系統就可以自己跟自己玩了(打個單機遊戲,玩個掃雷啥的)
如果你要跟別人一起玩,那你就需要上網了(訪問個黃色網站,發個黃色微博啥的),互聯網的核心就是由一堆協議組成,協議就是標準,全世界人通信的標準是英語,如果把計算機比作人,互聯網協議就是計算機界的英語。所有的計算機都學會了互聯網協議,那所有的計算機都就可以按照統一的標準去收發信息從而完成通信了。人們按照分工不同把互聯網協議從邏輯上劃分了層級,詳見我另一篇博客
網絡通信原理:http://www.cnblogs.com/linhaifeng/articles/5937962.html
socket相關的互聯網協議:
圖1
TCP/IP協議族包括運輸層、網絡層、鏈路層。
三 socket層
在圖1中,沒有看到Socket的影子,那麽它到底在哪裏呢?還是用圖來說話,一目了然。
圖2
四 socket是什麽
Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
所以,我們無需深入理解tcp/udp協議,socket已經為我們封裝好了,我們只需要遵循socket的規定去編程,寫出的程序自然就是遵循tcp/udp標準的。
也有人將socket說成ip+port,ip是用來標識互聯網中的一臺主機的位置,而port是用來標識這臺機器上的一個應用程序,ip地址是配置到網卡上的,而port是應用程序開啟的,ip與port的綁定就標識了互聯網中獨一無二的一個應用程序 而程序的pid是同一臺機器上不同進程或者線程的標識掃盲篇
五 套接字發展史及分類
套接字起源於 20 世紀 70 年代加利福尼亞大學伯克利分校版本的 Unix,即人們所說的 BSD Unix。 因此,有時人們也把套接字稱為“伯克利套接字”或“BSD 套接字”。一開始,套接字被設計用在同 一臺主機上多個應用程序之間的通訊。這也被稱進程間通訊,或 IPC。套接字有兩種(或者稱為有兩個種族),分別是基於文件型的和基於網絡型的。
基於文件類型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基於文件的套接字調用的就是底層的文件系統來取數據,兩個套接字進程運行在同一機器,可以通過訪問同一個文件系統間接完成通信
基於網絡類型的套接字家族套接字家族的名字:AF_INET
(還有AF_INET6被用於ipv6,還有一些其他的地址家族,不過,他們要麽是只用於某個平臺,要麽就是已經被廢棄,或者是很少被使用,或者是根本沒有實現,所有地址家族中,AF_INET是使用最廣泛的一個,python支持很多種地址家族,但是由於我們只關心網絡編程,所以大部分時候我麽只使用AF_INET)
六 套接字工作流程
一個生活中的場景。你要打電話給一個朋友,先撥號,朋友聽到電話鈴聲後提起電話,這時你和你的朋友就建立起了連接,就可以講話了。等交流結束,掛斷電話結束此次交談。 生活中的場景就解釋了這工作原理,也許TCP/IP協議族就是誕生於生活中,這也不一定。
圖3
先從服務器端說起。服務器端先初始化Socket,然後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。在這時如果有個客戶端初始化一個Socket,然後連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了。客戶端發送數據請求,服務器端接收請求並處理請求,然後把回應數據發送給客戶端,客戶端讀取數據,最後關閉連接,一次交互結束
socket()模塊函數用法
1 import socket 2 socket.socket(socket_family,socket_type,protocal=0) 3 socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默認值為 0。 4 5 獲取tcp/ip套接字 6 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 8 獲取udp/ip套接字 9 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 10 11 由於 socket 模塊中有太多的屬性。我們在這裏破例使用了‘from module import *‘語句。使用 ‘from socket import *‘,我們就把 socket 模塊裏的所有屬性都帶到我們的命名空間裏了,這樣能 大幅減短我們的代碼。 12 例如tcpSock = socket(AF_INET, SOCK_STREAM)
服務端套接字函數
s.bind() 綁定(主機,端口號)到套接字
s.listen() 開始TCP監聽
s.accept() 被動接受TCP客戶的連接,(阻塞式)等待連接的到來
客戶端套接字函數
s.connect() 主動初始化TCP服務器連接
s.connect_ex() connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
公共用途的套接字函數
s.recv() 接收TCP數據
s.send() 發送TCP數據(send在待發送數據量大於己端緩存區剩余空間時,數據丟失,不會發完)
s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩余空間時,數據不丟失,循環調用send直到發完)
s.recvfrom() 接收UDP數據
s.sendto() 發送UDP數據
s.getpeername() 連接到當前套接字的遠端的地址
s.getsockname() 當前套接字的地址
s.getsockopt() 返回指定套接字的參數
s.setsockopt() 設置指定套接字的參數
s.close() 關閉套接字
面向鎖的套接字方法
s.setblocking() 設置套接字的阻塞與非阻塞模式
s.settimeout() 設置阻塞套接字操作的超時時間
s.gettimeout() 得到阻塞套接字操作的超時時間
面向文件的套接字的函數
s.fileno() 套接字的文件描述符
s.makefile() 創建一個與該套接字相關的文件
七 基於TCP的套接字
tcp服務端
1 ss = socket() #創建服務器套接字 2 ss.bind() #把地址綁定到套接字 3 ss.listen() #監聽鏈接 4 inf_loop: #服務器無限循環 5 cs = ss.accept() #接受客戶端鏈接 6 comm_loop: #通訊循環 7 cs.recv()/cs.send() #對話(接收與發送) 8 cs.close() #關閉客戶端套接字 9 ss.close() #關閉服務器套接字(可選)
tcp客戶端
1 cs = socket() # 創建客戶套接字 2 cs.connect() # 嘗試連接服務器 3 comm_loop: # 通訊循環 4 cs.send()/cs.recv() # 對話(發送/接收) 5 cs.close() # 關閉客戶套接字
socket通信流程與打電話流程類似,我們就以打電話為例來實現一個low版的套接字通信
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ import socket ip_port=(‘127.0.0.1‘,9000) #電話卡 BUFSIZE=1024 #收發消息的尺寸 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機 s.bind(ip_port) #手機插卡 s.listen(5) #手機待機 conn,addr=s.accept() #手機接電話 # print(conn) # print(addr) print(‘接到來自%s的電話‘ %addr[0]) msg=conn.recv(BUFSIZE) #聽消息,聽話 print(msg,type(msg)) conn.send(msg.upper()) #發消息,說話 conn.close() #掛電話 s.close() #手機關機服務端
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ import socket ip_port=(‘127.0.0.1‘,9000) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #撥電話 s.send(‘linhaifeng nb‘.encode(‘utf-8‘)) #發消息,說話(只能發送字節類型) feedback=s.recv(BUFSIZE) #收消息,聽話 print(feedback.decode(‘utf-8‘)) s.close() #掛電話客戶端
上述流程的問題是,服務端只能接受一次鏈接,然後就徹底關閉掉了,實際情況應該是,服務端不斷接受鏈接,然後循環通信,通信完畢後只關閉鏈接,服務器能夠繼續接收下一次鏈接,下面是修改版
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ import socket ip_port=(‘127.0.0.1‘,8081)#電話卡 BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機 s.bind(ip_port) #手機插卡 s.listen(5) #手機待機 while True: #新增接收鏈接循環,可以不停的接電話 conn,addr=s.accept() #手機接電話 # print(conn) # print(addr) print(‘接到來自%s的電話‘ %addr[0]) while True: #新增通信循環,可以不斷的通信,收發消息 msg=conn.recv(BUFSIZE) #聽消息,聽話 # if len(msg) == 0:break #如果不加,那麽正在鏈接的客戶端突然斷開,recv便不再阻塞,死循環發生 print(msg,type(msg)) conn.send(msg.upper()) #發消息,說話 conn.close() #掛電話 s.close() #手機關機服務端改進版
#_*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ import socket ip_port=(‘127.0.0.1‘,8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #撥電話 while True: #新增通信循環,客戶端可以不斷發收消息 msg=input(‘>>: ‘).strip() if len(msg) == 0:continue s.send(msg.encode(‘utf-8‘)) #發消息,說話(只能發送字節類型) feedback=s.recv(BUFSIZE) #收消息,聽話 print(feedback.decode(‘utf-8‘)) s.close() #掛電話客戶端改進版
問題:
在重啟服務端時可能會遇到
這個是由於你的服務端仍然存在四次揮手的time_wait狀態在占用地址(如果不懂,請深入研究1.tcp三次握手,四次揮手 2.syn洪水攻擊 3.服務器高並發情況下會有大量的time_wait狀態的優化方法)
解決方法:
#加入一條socket配置,重用ip和端口 phone=socket(AF_INET,SOCK_STREAM) phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 phone.bind((‘127.0.0.1‘,8080))方法一
發現系統存在大量TIME_WAIT狀態的連接,通過調整linux內核參數解決, vi /etc/sysctl.conf 編輯文件,加入以下內容: net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30 然後執行 /sbin/sysctl -p 讓參數生效。 net.ipv4.tcp_syncookies = 1 表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防範少量SYN攻擊,默認為0,表示關閉; net.ipv4.tcp_tw_reuse = 1 表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認為0,表示關閉; net.ipv4.tcp_tw_recycle = 1 表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉。 net.ipv4.tcp_fin_timeout 修改系統默認的 TIMEOUT 時間方法二
python(二十三)