异常处理
错误
程序里的错误一般分为两种:
1、语法错误,这种错误,根本过不了python解释器的语法检测,必须在程序执行前就改正
2、逻辑错误,人为造成的错误,如数据类型错误、调用方法错误等,这些解释器是不会进行检测的,只有在执行的过程中才能抛出的错误
异常
异常是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
其他异常
异常处理
默认情况下(非程序员使用raise触发的异常),当执行的程序遇到错误抛出异常后会终止程序的运行,在错误点中断,后面的代码不会继续执行。
异常处理是指通过指定的代码进行异常捕捉,当捕捉到指定的异常类则进入特定的逻辑继续执行,使得程序不会在错误点中断运行,一般用来捕捉程序无法控制的异常错误(如客户端异常中断与服务器端的连接导致服务器端崩溃)。
if判断式异常处理
通过逻辑判断进行控制,想想曾经input+if的方式进行输入检测、防止语法错误的过程,但是这类if判断式与原来的逻辑并无关系,所以会造成代码可读性差
try...except...异常处理
python定制的一个异常处理类,语法如下:
try:
被检测的代码块
except 异常类型:
try中一旦检测到异常,如果这个异常和except标记的异常一样,就执行这个位置的逻辑
示例1:迭代器StopIteration异常处理
准备文件a.txt,只有五行内容
1111111
222222222222
33333333
4444
555555555
异常抛出:由于迭代器f没有那么多内容,所以第六个__next__会抛出StopIteration异常提示
f=open('a.txt','r')
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
异常处理:当碰到第六个__next__抛出异常后,except比较异常,如果一样,那么关闭文件对象
f=open('a.txt','r')
try:
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
except StopIteration: #只能够捕捉处理指定的StopIteration异常,如果是其他的异常则无法处理,依然会抛出错误
f.close()
示例2:多分支异常处理,代码段中可能会出现多个异常类,而一个except只能指定一个异常,所以可以用多分支结构
s='asdf'
l=['a']
d={'name':'bob'}
try:
# int(s) #ValueError
# l[2] #IndexError
d['age'] #KeyError
except IndexError as e: #as e表示将捕获的IndexError错误信息的异常值赋值给e
print(e)
except KeyError as e:
print(e)
except ValueError as e:
print(e)
示例3:万能异常,Exception,他可以捕获任意异常
s='asdf'
l=['a']
d={'name':'bob'}
try:
int(s) #ValueError
l[2] #IndexError
d['age'] #KeyError
except Exception as e:
print(e)
注意:如果需求为所有的异常统一一段逻辑处理,那么使用万能异常,如果针对不同的异常有不同的处理逻辑,那么使用多分支结构,多分枝结构的最后可以跟一个万能异常处理分支
示例4:异常的其他选项
s='asdf'
l=['a']
d={'name':'bob'}
try:
int(s) #ValueError
l[2] #IndexError
d['age'] #KeyError
except Exception as e:
print(e)
else:
print('try内代码块没有异常则执行我')
finally:
print('无论异常与否,都会执行该模块,通常是进行清理工作,如关闭文件、删除对象')
示例5:主动触发异常,人为产生一个异常
try:
raise TypeError('类型错误') #类型错误是所触发的异常的值
except Exception as e:
print(e) 输出结果:
类型错误
示例6:自定义异常处理类
class DefaultException(BaseException):
def __init__(self,msg):
self.msg=msg
def __str__(self): #定义类本身的返回值
return self.msg
try:
raise DefaultException('类型错误')
except DefaultException as e:
print(e) #DefaultException的返回值
示例7:断言,类似于if判断,不过断言是绝对真,如果不是真就不会往下执行
a=1
b=2
assert a<b
print(a+b) #输出结果3
assert a>b #输出结果:AssertionError异常错误
print(b-a) #没执行
示例8:try和if的比较,如果是if,需要每一个input都要做一次判断,而try可以对所有的input做处理
try:
num1=input('>>: ')
int(num1) #正统程序放到了这里,其余的都属于异常处理范畴
num2=input('>>: ')
int(num2)
num3=input('>>: ')
int(num3)
except ValueError as e:
print(e)
使用try..except的方式(但是也别忘了if)
1:把错误处理和真正的工作分开来
2:代码更易组织,更清晰,复杂的工作任务更容易实现;
3:毫无疑问,更安全了,不至于由于一些小的疏忽而使程序意外崩溃了;
只有在有些异常无法预知的情况下,才应该加上try...except,其他的逻辑错误应该尽量修正,尽量避免使用异常处理
套接字基础
C/S架构
即客户端/服务端架构,B/S架构(浏览器/服务器)也属于C/S架构。
socket套接字就是为了完成C/S架构软件的开发。
socket基础的基础
socket依赖于网络,所以骚年,网络基础不能忘了
socket介绍
在python里,socket子层位于TCP/IP协议栈的传输层和应用层的中间层,是一个提供向上向下接口的软件抽象层。
socket封装了tcp和udp协议,所以遵循socket语法写出的程序是遵循tcp和udp协议的。
注意:socket=ip+port,ip用来标识网络中主机的位置,port用来标识主机的应用,所以ip+port能够标识互联网中的唯一一个应用,所以说socket其实就是ip和端口的组合
socket分类
网络编程只需要关注AF_INET,这种是应用最广泛的,如果是用于ipv6平台需要用AF_INET6。
其他:AF_UNIX,用于UNIX文件系统间通信、还有很多其他的平台使用的,不多说
socket通信原理
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen)并调用accept阻塞,等待客户端连接。
客户端初始化一个Socket,然后连接服务器(connect),如果正常访问到了服务器端,服务器端阻塞结束,连接成功,这时客户端与服务器端的连接就建立了。
客户端发送数据请求,服务器端接收请求并处理请求,然后服务器把回应数据发送给客户端,客户端读取数据,循环。
最后客户端或者服务器端关闭连接,一次交互结束。
socket模块
一次性套接字通信(基于本地回环地址),以打电话的过程解释
服务端:
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
s.bind(('127.0.0.1',9000)) #手机插卡,如果元组为('',9000)表示本机所有的网卡,相当于0.0.0.0
s.listen(5) #手机待机,括号中的值是用于TCP连接的优化
conn,addr=s.accept() #手机接电话
print('接到来自%s的电话' %addr[0])
msg=conn.recv(1024) #听消息,听话
print(msg,type(msg))
conn.send(msg.upper()) #发消息,说话
conn.close() #挂电话
s.close() #手机关机
客户端:
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
s.connect_ex(('127.0.0.1',9000)) #拨电话
s.send('nb'.encode('utf-8')) #发消息,说话(只能发送字节类型)
feedback=s.recv(1024) #收消息,听话
print(feedback.decode('utf-8'))
s.close() #挂电话
执行先执行服务端,然后执行客户端。
相关值说明:
socket.socket(socket_family,socket_type,protocal=0)
socket_family可以是 AF_UNIX 或 AF_INET
socket_type 可以是 SOCK_STREAM(面向连接的可靠数据传输,即TCP协议)或 SOCK_DGRAM(面向无连接的不可靠数据传输,即UDP)
protocol 一般不填,默认值为 0
相关方法说明:
服务端套接字函数
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() 创建一个与该套接字相关的文件
socket循环通信,即一个连接来了可以不断的循环交互,有多个连接请求连接的时候如果第一个连接不中断,则无法连接,当第一个连接中断,第二个连接连上来,依次
服务器端:
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
s.bind(('127.0.0.1',9000)) #手机插卡
s.listen(5) #手机待机,括号中的值是用于TCP连接的优化
while True: #可以不停的接电话,即不停地接受连接
conn,addr=s.accept() #手机接电话
print('接到来自%s的电话' %addr[0])
while True: #循环的收发消息
msg=conn.recv(1024) #听消息,听话
print(msg,type(msg))
conn.send(msg.upper()) #发消息,说话
conn.close() #挂电话
s.close() #手机关机
客户端:
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
s.connect(('127.0.0.1',9000)) #拨号
while True: #通讯循环
msg=input('>>: ').strip()
if not msg:continue
s.send(msg.encode('utf-8'))
server_res=s.recv(1024)
print('server_res: ',server_res.decode('utf-8'))
phone_client.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))
windows
发现系统存在大量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 时间
linux
简单SSH示例
服务端
import socket
import subprocess
ssh_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #生成socket实例对象
ssh_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #重用地址,防止占用
ssh_server.bind(('127.0.0.1',8080))
ssh_server.listen(5)
print('server run...')
while True:
conn,client_addr=ssh_server.accept() #循环等待连接
print('客户端: ',client_addr)
while True: #通讯循环
try:
cmd=conn.recv(1024) #收消息
res=subprocess.Popen(cmd.decode('utf-8'), #执行命令
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout=res.stdout.read() #标准输出
stderr=res.stderr.read() #标准输入
std=stdout+stderr
conn.sendall(std) except Exception:
break
conn.close() #断连接,进入下一次连接等待
ssh_server.close() #关闭程序
客户端:
import socket
ssh_client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ssh_client.connect(('127.0.0.1',8080))
while True: #通讯循环
cmd=input('>>: ').strip()
if not cmd:continue
ssh_client.send(cmd.encode('utf-8'))
cmd_res = ssh_client.recv(1024)
print(cmd_res.decode('gbk')) #windows
# print(cmd_res.decode('utf-8')) #linux
ssh_client.close()