Python之異常處理和socket套接字連接7


一、異常處理

1)異常處理的使用意義

1 什么是異常處理
    異常是程序發生錯誤的信號,即程序一旦出錯就會立刻產生一個異常,如果該異常沒有被處理
    那么異常就拋出來,程序的運行也隨之終止

    異常分為三部分:
        異常的類型
        異常的內容、提示信息
        異常的追蹤/定位信息信息

    捕捉/檢測異常,一旦發生異常就立刻執行相應的處理邏輯,而不是任由異常拋出來終止程序
2 為何要進行異常處理
    增強程序的健壯性

3 如何進行異常處理
    try...except...

2)邏輯錯誤導致的異常

# int('xxxxxxx') #ValueError

# age #NameError

# for i in 10: #TypeError:
#     pass

# l=[]
# l[1111111] #IndexError

# d={'x':1}
# d['y'] #KeyError

# 1 / 0 #ZeroDivisionError
View Code

3)異常處理的單分支結構

try:
    l=[1,2,3]
    l[100]
    print('====>')
except IndexError:
    print('=====>NameError')
print('其他代碼')
View Code

4)異常的多分支結構

try:
    age
    l=[1,2,3]
    # l[100]
    d={'x':1}
    # d['y']
    print('====>')
except NameError as e:
    print('NameError: %s' %e)
except IndexError as e:
    print('IndexError: %s' %e)
except KeyError as e:
    print('KeyError: %s' %e)

print('其他代碼')
View Code

5)萬能異常:Exception,可以匹配所有種類的異常,最好不要直接萬能匹配異常

try:
    # age
    l=[1,2,3]
    # l[100]
    d={'x':1}
    d['y']
    print('====>')
except Exception as e:
    print(e)


print('其他代碼')
View Code

 6)多分支+Exception,注意Exception一定要放到except 其他異常的的后面

try:
    d={'x':1}
    d['y']
    print('====>')
except IndexError as e:
    print('IndexError: %s' %e)
except KeyError as e:
    print('KeyError:%s' %e)
except Exception as e:
    print(e)

print('其他代碼')
View Code

7)try...else,else會在被檢測的代碼塊沒有異常發生的情況下執行, else一定要與except連用,並且一定要放到多個except后面

try:
    l=[1,2,3]
    print('====>')
except IndexError as e:
    print('IndexError: %s' %e)
except KeyError as e:
    print('KeyError:%s' %e)
except Exception as e:
    print(e)
else:
    print('else的代碼只有在被檢測的代碼塊沒有異常發生的情況下才會執行')
View Code

8)try...finally,finnaly的代碼會什么時候運行? finally應放到最后面,常應用於回收資源使用

try:
    f=open('a.txt','w')
    d={'x':1}
    print(d['y'])
except KeyError as e:
    print('KeyError:%s' %e)
except Exception as e:
    print(e)
else:
    print('else的代碼只有在被檢測的代碼塊沒有異常發生的情況下才會執行')
finally:
    print('finally的代碼,無論被檢測的代碼有無異常,都會執行,通常在finally內做一些回收資源的事情')
    f.close()

print('其他代碼')
View Code

9)主動觸發異常raise 異常類型(’異常的內容‘)

print('===>1')
raise TypeError('類型錯誤')
print('===>2')
# 應用於程序中自定義某種法則,一旦不遵循則會像拋出語法異常一樣,終止程序的運行
View Code

 10)斷言,和代替raise觸發的異常

info=[1,2,3,4,5,6]

# if len(info) != 7:
#     raise ValueError('值的個數 < 7')
assert len(info) == 7 # 我斷定len(info) == 7,如果我斷言失敗,程序則拋出異常

print('階段2--->1')
View Code

 11)自定義異常

class MyError(BaseException):
    def __init__(self,msg):
        super().__init__()
        self.msg=msg
    def __str__(self):
        return '<%s>' %self.msg

raise MyError('我自己定義的異常')
View Code

 二、socker套接字網絡編程,tcp連接方式

1)套接字工作流程

先從服務器端說起。服務器端先初始化Socket,然后與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。在這時如果有個客戶端初始化一個Socket,然后連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了。客戶端發送數據請求,服務器端接收請求並處理請求,然后把回應數據發送給客戶端,客戶端讀取數據,最后關閉連接,一次交互結束

 2)實例化出簡單的服務端和客戶端程序

import socket

#1、買手機
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式協議指的就是tcp協議
# print(phone)

#2、插手機卡
phone.bind(('127.0.0.1',8080)) #端口范圍0-65535

#3、開機
phone.listen(5) #限制的是請求數,而非鏈接數

#4、等待電話連接
print('服務的啟動......')
conn,client_addr=phone.accept() #(tcp的連接對象,客戶端的ip和端口)
print(conn)
print(client_addr)

#5、收消息
data=conn.recv(1024) #最大接收1024bytes
print('客戶端數據:%s' %data)
#6、發消息
conn.send(data.upper())

#7、掛電話
conn.close()

#9、關機
phone.close()
服務端
import socket

#1、買手機
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式協議指的就是tcp協議

#2、打電話,建電話連接
phone.connect(('127.0.0.1',8080)) #ip和端口都是服務端的

#3、發消息
phone.send('hello world'.encode('utf-8'))

#4、收消息
data=phone.recv(1024)
print(data)

#5、掛電話
phone.close()
客戶端

3)加上通信循環

import socket

#1、買手機
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式協議指的就是tcp協議
# print(phone)

#2、插手機卡
phone.bind(('127.0.0.1',8080)) #端口范圍0-65535

#3、開機
phone.listen(5) #限制的是請求數,而非鏈接數

#4、等待電話連接
print('服務的啟動......')
conn,client_addr=phone.accept() #(tcp的連接對象,客戶端的ip和端口)
print(conn)
print(client_addr)

while True: # 通信循環
    try: #針對的是windows系統
        #5、收消息
        data=conn.recv(1024) #最大接收1024bytes
        if not data:break #針對的linux系統
        print('客戶端數據:%s' %data)
        #6、發消息
        conn.send(data.upper())
    except ConnectionResetError:
        break

#7、掛電話
conn.close()

#9、關機
phone.close()
服務端
import socket

#1、買手機
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式協議指的就是tcp協議

#2、打電話,建電話連接
phone.connect(('127.0.0.1',8080)) #ip和端口都是服務端的

while True:
    msg=input('>>>: ').strip()
    #3、發消息
    phone.send(msg.encode('utf-8'))

    #4、收消息
    data=phone.recv(1024)
    print(data.decode('utf-8'))

#5、掛電話
phone.close()
客戶端

4)加上連接循環,可等待多個連接進來

import socket

#1、買手機
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式協議指的就是tcp協議
# print(phone)
# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加

#2、插手機卡
phone.bind(('127.0.0.1',8081)) #端口范圍0-65535

#3、開機
phone.listen(5) #限制的是請求數,而非鏈接數

#4、等待電話連接
print('服務的啟動......')

while True: # 連接循環
    conn,client_addr=phone.accept() #(tcp的連接對象,客戶端的ip和端口)
    # print(conn)
    print(client_addr)

    # 通信循環
    while True:
        try: #針對的是windows系統
            #5、收消息
            data=conn.recv(1024) #最大接收1024bytes
            if not data:break #針對的linux系統
            print('客戶端數據:%s' %data)
            #6、發消息
            conn.send(data.upper())
        except ConnectionResetError:
            break

    #7、掛電話
    conn.close()

#9、關機
phone.close()
服務端
import socket

#1、買手機
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #流式協議指的就是tcp協議

#2、打電話,建電話連接
phone.connect(('127.0.0.1',8081)) #ip和端口都是服務端的

while True:
    msg=input('>>>: ').strip() #msg=''
    if not msg:continue
    #3、發消息
    phone.send(msg.encode('utf-8'))
    print('has send====>')

    #4、收消息
    data=phone.recv(1024)
    print('has recv====>')
    print(data.decode('utf-8'))

#5、掛電話
phone.close()
客戶端

5)模擬ssh遠程執行命令

from socket import *
import subprocess

phone=socket(AF_INET,SOCK_STREAM)
phone.bind(('127.0.0.1',8081))
phone.listen(5)

print('服務的啟動......')
# 連接循環
while True:
    conn,client_addr=phone.accept()
    print(client_addr)

    # 通信循環
    while True:
        try:
            cmd=conn.recv(1024)
            if not cmd:break

            obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE
                             )

            stdout=obj.stdout.read()
            stderr=obj.stderr.read()

            # conn.send(stdout+stderr)
            print(len(stdout)+len(stderr))
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break
    conn.close()

phone.close()
服務端
from socket import *

phone=socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8081))

while True:
    cmd=input('>>>: ').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))

    res=phone.recv(1024) #1024 * 8
    print(res.decode('gbk'))


phone.close()
客戶端

發現了粘包問題

 6)解決粘包問題

from socket import *
import subprocess
import struct

phone=socket(AF_INET,SOCK_STREAM)
phone.bind(('127.0.0.1',8081))
phone.listen(5)

print('服務的啟動......')
# 連接循環
while True:
    conn,client_addr=phone.accept()
    print(client_addr)
    # 通信循環
    while True:
        try:
            cmd=conn.recv(1024)
            if not cmd:break
            obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE
                             )
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
            # 1、先發送固定長度的報頭
            #目前報頭里只包含數據的大小
            total_size=len(stdout) + len(stderr)
            conn.send(struct.pack('i',total_size))
            # 2、發送真實的數據
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break
    conn.close()
phone.close()
服務端
from socket import *
import struct

phone=socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8081))
while True:
    cmd=input('>>>: ').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))
    #1、先收報頭,從報頭里取出對真實數據的描述信息
    header=phone.recv(4)
    total_size=struct.unpack('i',header)[0]
    #2、循環接收真實的數據,直到收干凈為止
    recv_size=0
    res=b''
    while recv_size < total_size:
        recv_data=phone.recv(1024)
        res+=recv_data
        recv_size+=len(recv_data)
    print(res.decode('gbk'))
phone.close()
客戶端

 7)解決粘包報頭數據過大問題,先轉json,再轉報頭

from socket import *
import subprocess
import struct
import json

phone=socket(AF_INET,SOCK_STREAM)
phone.bind(('127.0.0.1',8081))
phone.listen(5)

print('服務的啟動......')
# 連接循環
while True:
    conn,client_addr=phone.accept()
    print(client_addr)

    # 通信循環
    while True:
        try:
            cmd=conn.recv(1024)
            if not cmd:break
            obj=subprocess.Popen(cmd.decode('utf-8'),shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE
                             )

            stdout=obj.stdout.read()
            stderr=obj.stderr.read()

            #制作報頭
            header_dic={
                'filename':'a.txt',
                'total_size':len(stdout) + len(stderr),
                'md5':'xxxxxsadfasdf123234e123'
            }
            header_json = json.dumps(header_dic)
            header_bytes=header_json.encode('utf-8')

            #1、先發送報頭的長度
            conn.send(struct.pack('i',len(header_bytes)))

            #2、再發送報頭
            conn.send(header_bytes)

            #3、最后發送真實的數據
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionResetError:
            break
    conn.close()

phone.close()
服務端
from socket import *
import struct
import json

phone=socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8081))

while True:
    cmd=input('>>>: ').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))

    #1、先收報頭的長度
    obj=phone.recv(4)
    header_size=struct.unpack('i',obj)[0]

    #2、再接收報頭
    header_bytes=phone.recv(header_size)
    header_json=header_bytes.decode('utf-8')
    header_dic=json.loads(header_json)
    print(header_dic)

    total_size=header_dic['total_size']
    #3、循環接收真實的數據,直到收干凈為止
    recv_size=0
    res=b''
    while recv_size < total_size:
        recv_data=phone.recv(1024)
        res+=recv_data
        recv_size+=len(recv_data)

    print(res.decode('gbk'))

phone.close()
客戶端

8) socketserver模塊實現並發的套接字通信

基於tcp的並發線程通信

import socketserver

# 通信循環
class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                data=self.request.recv(1024)
                if not data:break
                self.request.send(data.upper())
            except ConnectionResetError:
                break
        self.request.close()
if __name__ == '__main__':
    # 連接循環
    server=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyTCPHandler)
    server.serve_forever()
服務端
import socket

client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

client.connect(('127.0.0.1',8080)) #ip和端口都是服務端的

while True:
    msg=input('>>>: ').strip()
    client.send(msg.encode('utf-8'))
    data=client.recv(1024)
    print(data.decode('utf-8'))


client.close()
客戶端

基於udp的並發線程通信 

import socketserver

# 通信循環
class MyUDPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        # print(self.request)
        res=self.request[0]
        print('客戶端發來的數據:',res)

        self.request[1].sendto(res.upper(),self.client_address)

if __name__ == '__main__':
    #連接循環
    server=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyUDPHandler)
    server.serve_forever()
udpserver
import socket
import os

client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:
    msg='%s hello' %os.getpid()
    client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))

    res,server_addr=client.recvfrom(1024)
    print(res)
udpclient

三、socket套接字,基於UDP的連接方式

1)udp的工作模式

from socket import *
import time

server=socket(AF_INET,SOCK_DGRAM) # 數據報協議UDP
#1、基於udp協議每發送的一條數據都自帶邊界,即udp協議沒有粘包問題
#2、基於udp協議的通信,一定是一發對應一收

server.bind(('127.0.0.1',8080))

while True:
    msg,client_addr=server.recvfrom(1024)   # 接收客戶端的信息
    print(msg ,client_addr)
    time.sleep(3)
    server.sendto(msg.upper(),client_addr)      # 給客戶端回消息
服務端
from socket import *

client=socket(AF_INET,SOCK_DGRAM)

while True:
    # msg=input('>>: ').strip()
    client.sendto('egon'.encode('utf-8'),('127.0.0.1',8080))    # 給服務端發送消息

    res,server_addr=client.recvfrom(1024)
    print(res)
客戶端

 2) udp的連接特點

1、一發對應一收
2、沒有粘包問題
3、只能接收數據量比較小的內容,如果接收的byte數量小於了發送的數量,會丟數據
from socket import *

server=socket(AF_INET,SOCK_DGRAM) # 數據報協議UDP
#1、基於udp協議每發送的一條數據都自帶邊界,即udp協議沒有粘包問題
#2、基於udp協議的通信,一定是一發對應一收

server.bind(('127.0.0.1',8080))

msg1,client_addr=server.recvfrom(1)
print(msg1)
msg2,client_addr=server.recvfrom(1)
print(msg2)
msg3,client_addr=server.recvfrom(1)
print(msg3)
服務端
from socket import *

client=socket(AF_INET,SOCK_DGRAM)

client.sendto('hello'.encode('utf-8'),('127.0.0.1',8080))
client.sendto('world'.encode('utf-8'),('127.0.0.1',8080))
client.sendto('egon'.encode('utf-8'),('127.0.0.1',8080))
客戶端

 

原文鏈接:http://www.cnblogs.com/linhaifeng/articles/6129246.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM