python網路編程-異常處理-異常捕獲-拋出異常-斷言-自定義異常-UDP通訊-socketserver模組應用-03

  • 2019 年 10 月 7 日
  • 筆記

異常捕獲

異常:程式在運行過程中出現了不可預知的錯誤,並且該錯誤沒有對應的處理機制,那麼就會以異常的形式表現出來

影響:整個程式無法再正常運行

異常的結構

異常的類型

NameError

異常的資訊

name 'safaf' is not defined

異常的位置

'''  Traceback (most recent call last):    File "E:/PyCharm 2019.1.3/ProjectFile/day010/day029/01 異常處理.py", line 1, in <module>      safaf  '''

異常的種類

語法錯誤

程式錯誤編譯成一般都會直接有提示,提示就應該直接解決的,出現語法錯誤一般是基礎太差。

邏輯錯誤

這種錯誤是可以被容忍的,邏輯錯誤無法一眼看出來

針對邏輯上的錯誤可以採用異常處理機制來進行捕獲

常見的錯誤類型

常見的出錯類型

原因

NameError

名字錯誤

SyntaxError

語法錯誤

keyError

鍵不存在

ValueError

值錯誤

IndexError

索引錯誤

如何避免

異常處理

使用:在你認為可能會出現問題的程式碼上方用 try 包起來

注意:try 內部的程式碼塊越少越好 異常處理能少用就少用,不然該報錯的不報錯哪裡錯了你都不知道 像服務端、客戶端斷開鏈接就需要用到異常處理,你不知道他什麼時候會斷開

異常處理使用模板(try.. except.. else.. finally..)

try:      可能出錯的程式碼  except 出錯類型 as e:  # 將報錯資訊賦值給e      錯誤的處理程式碼  except 出錯類型2 as e:  # 將報錯資訊賦值給e      錯誤的處理程式碼  except 出錯類型3 as e:  # 將報錯資訊賦值給e      錯誤的處理程式碼    # 或者 Exception直接捕獲所有的  except Exception as e:  # 可以匹配所有的錯誤類型      BaseException 是 Exception 的父類,也可以捕獲所有類型      錯誤的處理程式碼  else:      當被檢測的程式碼沒有任何異常發生,才會執行這裡面的語句  finally:      無論被檢測的程式碼有沒有異常發生,都會在程式碼執行完後執行這裡面的程式碼

案例

try:      safaf  except NameError as e:      print(e)  else:      print("沒有報錯才會執行我哦~")  finally:      print("管你報不報錯,我都會執行的!")    # name 'safaf' is not defined  # 管你報不報錯,我都會執行的!    try:      print("我沒錯哦~")  except Exception as e:  # 捕捉所有異常      print("管你啥錯,我都抓了!")  else:      print("沒有報錯才會執行我哦~")  finally:      print("管你報不報錯,我都會執行的!")  # 我沒錯哦~  # 沒有報錯才會執行我哦~  # 管你報不報錯,我都會執行的!

補充

出錯後會立即停止程式碼運行,去與except中的錯誤類型一個個比較,匹配上了就執行裡面的程式碼,沒匹配上就直接報錯

主動拋出異常raise

if 'egon' == 'DSB':      pass  else:      raise TypeError('盡說大實話')  # 這一行就是報錯的位置      # raise 關鍵字就是用來主動拋出異常的

斷言assert

斷言不成立直接報錯

l = [1, 2, 3]  assert len(1) < 0  # assert 斷言,預言,猜某個數據的狀態,猜對了不影響程式碼執行,猜錯了直接報錯

自定義異常(繼承異常類)

報錯類型,其實對應的就是一個個類(可以自定義拼接異常的格式)

class MyError(BaseException):      def __init__(self, msg):          super().__init__()          self.msg = msg        def __str__(self):          return f'----<{self.msg}>----'    raise MyError('自定義的異常')  # Traceback (most recent call last):  #   File "E:/PyCharm 2019.1.3/ProjectFile/day010/day029/test.py", line 15, in <module>  #     raise MyError('自定義的異常')  # __main__.MyError: ----<自定義的異常>----

UDP通訊

UDP協議又叫用戶數據報協議

它沒有雙向通道,類似於發簡訊(只管發,不管對方有沒有收到,不需要對方立即回應)

UDP的程式可以先啟動客戶端再啟動服務端(客戶端發數據給服務端之前都沒問題)

UDP類似於發簡訊

TCP類似於打電話,你一句我一句的

普通使用

服務端

import socket    server = socket.socket(type=socket.SOCK_DGRAM)  # type=socket.SOCK_DGRAM 指定成 UDP 協議  type=socket.SOCK_STREAM TCP協議(默認就是,不用指定)  server.bind(('127.0.0.1', 8080))    # UDP 不需要設置半連接池(server.listen(5)),也沒有半連接池的概念  # UDP 沒有雙向通道,所以也不需要建立連接(conn, addr = server.accept())    # 直接就是通訊循環    while True:  # 這裡只需要直接通訊(交互)即可      data, addr = server.recvfrom(1024)      print("數據:", data.decode('utf-8'))  # 客戶端發來的消息      print("地址:", addr)  # 客戶端的地址        re_msg = input("Please input your response msg:").strip()  # 會阻塞在這裡,這裡過了,才能發出資訊,看到下一條資訊      server.sendto(re_msg.encode('utf-8'), addr)  # 向客戶端發送消息      # 數據: hi  # 地址: ('127.0.0.1', 64821)  # Please input your response msg:o  hi  # 數據: hihihi  # 地址: ('127.0.0.1', 64823)  # Please input your response msg:xxixixi  # 數據: aha e  # 地址: ('127.0.0.1', 64828)  # Please input your response msg:emmm?  # 數據:  # 地址: ('127.0.0.1', 64828)  # Please input your response msg:adsa

客戶端

import socket    client = socket.socket(type=socket.SOCK_DGRAM)    # UDP 不需要建立連接(client.connect(('127.0.0.1', 8080)))    server_addr = ('127.0.0.1', 8080)  # UDP sendto發消息時需要一個伺服器的地址及埠號    while True:      msg = input("Please input your msg:").strip()      client.sendto(msg.encode('utf-8'), server_addr)  # 向伺服器發送數據,要附帶伺服器端地址及埠(基於網路傳輸的數據都必須是二進位的)      data, msg_from_server_addr = client.recvfrom(1024)  # 收到消息,且得到地址        print("服務端發來的數據:", data.decode('utf-8'))      print("伺服器端的ip及埠", msg_from_server_addr)    # 窗口1  # Please input your msg:hi  # 服務端發來的數據: o  hi  # 伺服器端的ip及埠 ('127.0.0.1', 8080)  # Please input your msg:    # 窗口2  # Please input your msg:hihihi  # 服務端發來的數據: xxixixi  # 伺服器端的ip及埠 ('127.0.0.1', 8080)  # Please input your msg:    # 窗口3  # Please input your msg:aha e  #  # 服務端發來的數據: emmm?  # 伺服器端的ip及埠 ('127.0.0.1', 8080)  # Please input your msg:服務端發來的數據: adsa  # 伺服器端的ip及埠 ('127.0.0.1', 8080)  # Please input your msg:

TCP與UDP之間的區別

  1. UDP 允許發空數據,不會有影響
  2. UDP 直接啟動客戶端未啟動服務端不會報錯
  3. UDP 不會有粘包問題(自帶報頭)
  4. UDP 支援並發

服務端

import socket    server = socket.socket(type=socket.SOCK_DGRAM)    server.bind(('127.0.0.1', 8080))    while True:      data, addr = server.recvfrom(1024)      print(data.decode('utf-8'))        server.sendto(data.upper(), addr)

客戶端

import socket      client = socket.socket(type=socket.SOCK_DGRAM)    while True:        msg = input(">>>:")      # -------------------------------------------      # 1.UDP 允許發空數據,不會有影響      # -------------------------------------------      # UDP自帶報頭,就算數據為空,另一端也能處理      client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))  # 第二參數,目標伺服器地址        # -------------------------------------------      # 2.UDP 直接啟動客戶端未啟動服務端不會報錯      #   發數據找不到服務端也還是會報錯      # -------------------------------------------      # 下面兩行程式碼直接注釋掉,服務端沒啟動,都不會報錯,只管給伺服器發(收沒收到不管)      # data, server_addr = client.recvfrom(1024)      # print(data)        # -------------------------------------------      # 3.UDP 不會有粘包問題(自帶報頭)      # -------------------------------------------      # client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))      # client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))      # client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))      #      # server.sendto(data.upper(), addr)      # server.sendto(data.upper(), addr)      # server.sendto(data.upper(), addr)        # -------------------------------------------      # 4.UDP 支援並發      #   TCP是保持連接,而UDP不需要保持連接      #       與一個客戶端斷開連接才會和下一個客戶端建立連接      # -------------------------------------------

簡易版的QQ

伺服器端

import socket    server = socket.socket(type=socket.SOCK_DGRAM)  server.bind(('127.0.0.1', 8080))    while True:      data, client_addr = server.recvfrom(1024)      print(data.decode('utf-8'))        msg = input(">>>:").strip()      server.sendto(msg.encode('utf-8'), client_addr)    # 來自star3的消息:helo  # >>>:hi  # 來自star2的消息:aha  # >>>:haa  # 來自star的消息:hello world  # >>>:ha  # 來自star2的消息:jason nnn  # >>>:jj

客戶端1、2、3共用同一份程式碼

import socket    client = socket.socket(type=socket.SOCK_DGRAM)    username = 'star'  # 客戶端對應改成 star2 star3  server_address = ('127.0.0.1', 8080)  # 指定一個發消息的目標伺服器  while True:      msg = input(">>>:").strip()        msg = f'來自{username}的消息:{msg}'  # 是哪個用戶名發出的數據不應該由這裡傳過去,用戶可以隨便改,實際意義不大      '''      user_dict = {          "username1": (ip1 + port1),          "username2": (ip2 + port2),          "username3": (ip3 + port3),      }      # 可以在每個端都存這樣一個對照表,根據ip與port就可以知道用戶名了      '''        client.sendto(msg.encode('utf-8'), server_address)        data, server_addr = client.recvfrom(1024)  # server_addr 收到消息的服務端地址        print(data.decode('utf-8'))    # 各個窗口的控制台輸入與輸出  # >>>:helo  # hi  # >>>:    # >>>:aha  # haa  # >>>:jason nnn  # jj    # >>>:  # >>>:hello world  # ha  # >>>:

概念科普

  • 並發

看起來像同時運行(直接啟動多個UDP客戶端)(默認的UDP程式多開就是這個效果)

  • 並行

真正意義上的同時運行

socketserver模組科普

是給服務端用的(客戶端還是用socket模組),能夠支援服務端的並發

TCP模擬UDP實現並發

伺服器端

import socketserver  # 文件名不要和模組衝突了,不然都不知道導哪個了      class MyServer(socketserver.BaseRequestHandler):      def handle(self):          # 與客戶端進行通訊          # print("來啦 老弟")          while True:  # 需要保持通訊(後續 client.send() 可沒有附帶伺服器地址, connect 被關閉了)              data = self.request.recv(1024)              print(self.client_address)  # 客戶端地址              print(data.decode('utf-8'))              self.request.send(data.upper())      if __name__ == '__main__':      '''只要有客戶端連接,會自動交給自定義類中的handle方法去處理'''      server = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyServer)  # 創建一個基於TCP的對象      server.serve_forever()  # 啟動該服務對象    # ('127.0.0.1', 14327)  # dAddA  # ('127.0.0.1', 14326)  # ADD  # ('127.0.0.1', 14325)  # ADsafga

客戶端

# TCP 實現UDP  import socket    client = socket.socket()  client.connect(('127.0.0.1', 8080))    while True:      res = input(">>>:")      client.send(res.encode('utf-8'))        data = client.recv(1024)      print(data.decode('utf-8'))      # 窗口1 控制台數據(輸入與輸出)  # >>>:dAddA  # DADDA  # >>>:    # 窗口2 控制台數據(輸入與輸出)  # >>>:ADD  # ADD  # >>>:    # 窗口1 控制台數據(輸入與輸出)  # >>>:ADsafga  # ADSAFGA  # >>>:

socketserver之UDP

伺服器端

import socketserver  # 文件名不要和模組衝突了,不然都不知道導哪個了      class MyServer(socketserver.BaseRequestHandler):      def handle(self):          # 與客戶端進行通訊          # while True:  # UDP 不需要通訊循環,每次 sendto 都有伺服器的地址          data, sock = self.request          print(self.client_address)  # 客戶端地址          print(data.decode('utf-8'))          sock.sendto(data.lower(), self.client_address)      if __name__ == '__main__':      '''只要有客戶端連接,會自動交給自定義類中的handle方法去處理'''      server = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyServer)  # 創建一個基於UDP的對象      server.serve_forever()  # 啟動該服務對象    # 控制台列印的數據  # ('127.0.0.1', 52524)  # CLient2  # ('127.0.0.1', 52529)  # clet1  # ('127.0.0.1', 52529)  # CLienT1  # ('127.0.0.1', 54485)  # CLiEnt3

客戶端

import socket    client = socket.socket(type=socket.SOCK_DGRAM)    server_addr = ('127.0.0.1', 8080)    while True:      res = input(">>>:").strip()      client.sendto(res.encode('utf-8'), server_addr)        data, response_server_addr = client.recvfrom(1024)      print(data.decode('utf-8'), response_server_addr)    # 窗口1 控制台數據(輸入與輸出)  # >>>:clIeNt1  # clet1 ('127.0.0.1', 8080)  # >>>:CLienT1  # client1 ('127.0.0.1', 8080)  # >>>:    # 窗口2 控制台數據(輸入與輸出)  # >>>:CLient2  # client2 ('127.0.0.1', 8080)  # >>>:    # 窗口1 控制台數據(輸入與輸出)  # >>>:CLiEnt3  # client3 ('127.0.0.1', 8080)  # >>>:

為什麼UDP時重寫的handle方法里不用寫通訊循環

handle 是處理一次連接請求的,handle結束連接就斷開了    UDP是不需要保持(雙向)連接的,所以每次sendto 都是單個請求(都附帶伺服器端地址及埠),不能寫通訊循環(不然就拿著一個sendto 過來的數據循環列印了)    而TCP是基於雙向通道通訊的,handle結束後連接就斷開了(再client.send() 這個連接狀態就已經不存在了),所以TCP這邊的服務端要寫通訊循環保持連接來多次通訊

Tags: