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

原文地址:https://www.cnblogs.com/linu/p/9127914.html