day8 (异常处理和网络编程)
一、异常处理
#1 什么是异常? # 异常是错误发生的信号,一旦程序出错,就会产生一个异常,应用程序未处理该异常, # 异常便会抛出,程序随之终止 #2、常见异常类型 #I:语法错误应该在程序运行前修正 # if 1 >2 # print('xxxxx') #II:逻辑错误 #x #NameError: name 'x' is not defined # l=[] # l[10000] #IndexError # class Foo: # pass # Foo.x #AttributeError: # k={'x':1} # k['y'] #KeyError # 1/0 #ZeroDivisionError # for i in 3: #TypeError: # pass # age=input('>>: ') # age=int(age) #ValueError #3、如何处理异常 print('====>start<=====') try: l=[] print(l[1111]) print('====>1') print('====>2') print('====>3') except IndexError: #捕捉Indexerror错误,当匹配到该类型的错误时会出发except代码块的执行,然后继续执行except后面的代码!,若没有匹配到,代码就不会执行except代码块 pass print('====>end<=======') print('====>start<=====') try: l=[] print(l[1111]) print('====>1') print('====>2') print('====>3') except IndexError as e: #as e 的作用是把异常的值传给e print('===>',e) #list index out of range print('====>end<=======') print('====>start<=====') try: l=[] #print(l[1111]) print('====>1') d={} d['k'] print('====>2') print('====>3') except IndexError as e: print('===>',e) print(1111) except KeyError as e: #except可以接多个 当捕捉到异常后,异常的代码和匹配的except之间的代码不会执行,然后接着执行匹配到的except之后的代码!! print('----',e) print('====>end<=======') print('====>start<=====') try: l=[] # print(l[1111]) print('====>1') d={} d['k'] print('====>2') print('====>3') except IndexError: pass except KeyError: pass except Exception as e: #万能异常捕捉 print('万能异常--->',e) print('====>end<=======') print('====>start<=====') try: l=[] #print(l[1111]) # print('====>1') d={} # d['k'] # print('====>2') # print('====>3') except IndexError: pass except KeyError: pass except Exception as e: print('万能异常--->',e) else: #当没有异常发生的时候执行 print('没有异常发生的时候触发') #没有异常发生的时候触发 finally: #有没有异常都触发 print('有没有异常都触发') #有没有异常都触发 print('====>end<=======') ''' try: conn=connect('1.1.1.1',3306) conn.execute('select * from db1.t1') finally: conn.close() ''' # stus=['egon','alex','wxxx'] ip_list=[ # '1.1.1.1:8080', # '1.1.1.2:8081', # '1.1.1.3:8082', ] # if len(ip_list) == 0: # raise TypeError #主动触发异常 assert len(ip_list) > 0 #当不满足条件时触发异常 (断言失败触发异常) #AssertionError # print('从ip_list取出ip地址,验证可用性') class MyException(BaseException): #自定义异常类型 def __init__(self,msg): super(MyException,self).__init__() self.msg=msg def __str__(self): return '<%s>' %self.msg #__str__ 的返回值必须是str类型 raise MyException('类型错误') #异常的值:print(obj) 会打印 MyException('类型错误') (打印对象) #__main__.MyException: <类型错误>
#异常处理是在异常一定会发发生但是有不可以用代码处理的情况下使用的
二、socket编程
1、网络基础
http://www.cnblogs.com/linhaifeng/articles/5937962.html
2、基于tcp协议实现简单版本的套接字通信
#### 服务端 import socket #1、获得套接字对象 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp协议 #2、绑定ip和端口 # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8081)) #0-65535 #3、监听端口和设置半链接池 phone.listen(5) #4、等待连接 print('starting...') conn,client_addr=phone.accept() #(conn,client_addr) print(conn,client_addr) #5、收\发消息 data=conn.recv(1024) #1024bytes? 接收1024个bytes 1024指的是接收的最大限制 conn.send(data.upper()) #6、关闭链接 conn.close() #7、关闭链接? phone.close() ####客户端 import socket #1 获得socket对象 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp协议 #2、建立链接 phone.connect(('127.0.0.1',8081)) #0-65535 #3、发收消息 phone.send('hello'.encode('utf-8')) data=phone.recv(1024) print(data) #4、关闭链接 phone.close()
3、加上循环的通信
服务端
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp协议 # print(phone) # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) phone.bind(('127.0.0.1',8083)) #0-65535 phone.listen(5) print('starting...') while True: #链接循环 #phone 是一个等待建立连接的套接字 conn,client_addr=phone.accept() #(conn,client_addr) #等待建立连接 # print(conn,client_addr) print(client_addr) while True: #通信循环 try: data=conn.recv(1024) #1024bytes? #conn 是一个建立好的套件字连接 if not data:break #针对的是linux系统 print('客户端消息',data) conn.send(data.upper()) # print('====has send') except ConnectionResetError: #针对windowns 系统 break conn.close() phone.close() #一下所说的环境都是接收方的环境 #当有一方强制终止连接时(强制关闭程序)在windowns 的体现是会在建立链接的另一方抛出一个ConnectionResetError 的异常 #而在linux的体现是:程序并不会抛出异常,而是在接收对会一直收到空,进入死循环,非常的危险,所以应判断当收到空是应该退出通信循环端口链接(因为从理论上讲建立链接的双方是不会发送空的)
客户端 import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #tcp协议 phone.connect(('127.0.0.1',8083)) #0-65535 # 得到一个建立好的套接字连接 while True: msg=input('>>: ').strip() #msg=' ' if not msg:continue phone.send(msg.encode('utf-8')) # print('has send===>') data=phone.recv(1024) # print('has recv===>') print(data) phone.close() #当发一个数据时python解释器会对操作系统发一个指令并这个数据从用户态拷到操作系统内核态来由操作系统来进行发送数据。 # 但是当发送一个空时python解释器给操作系统发了一个指令但是没有数据所以操作系统就不会进行发数据的操作,当然对方也不会收到数据,所以程序会在 #发空后卡住(也就是send后),因为此时对方根本就不会受到数据,也谈不上给你发数据了,所以程序会卡在recv这里。 #所以send 只是涉及到了本地内存之间的数据拷贝, 发送数据是有操作系统来完成的
4、模拟ssh远程执行命令
服务端 from socket import * import subprocess server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',8090)) server.listen(5) while True: conn,client_addr=server.accept() print(client_addr) while True: try: cmd=conn.recv(1024) if not cmd:break #ls -l;sadfasdf;pwd;echo 123 obj=subprocess.Popen(cmd.decode('utf-8'),shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout=obj.stdout.read() stderr=obj.stderr.read() cmd_res=stdout+stderr print(len(cmd_res)) conn.send(cmd_res) except ConnectionResetError: break conn.close() server.close()
客户端 from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8090)) while True: cmd=input('>>: ').strip() if not cmd:continue client.send(cmd.encode('utf-8')) data=client.recv(1024) print(data.decode('gbk')) client.close()
以上的代码只是简单的实现,因为此段代码并没有解决tcp的粘包问题
粘包演示
服务端 from socket import * import time server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',8091)) server.listen(5) conn,addr=server.accept() #b'hello' res1=conn.recv(10) #b'h' print('res1: ',res1) # b'elloworld' #time.sleep(6) res2=conn.recv(5) print('res2: ',res2) conn.close() server.close()
客户端 from socket import * import time client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8091)) client.send('hello'.encode('utf-8')) #b'hello' #time.sleep(5) client.send('world'.encode('utf-8')) #b'world' client.close() #tcp为了协议规定把数据较小的和数据时间间隔非常短的数据包合并成一个包进行发送(nagle算法),这个是发送时的粘包问题 #解决粘包 #发送方解决粘包可以通过延长两次发送时间 #接收方解决粘包可以通过接收定长的包和延长时间配合使用
#解决粘包的关健就是可以每次都能得到接收的长度
解决粘包问题
#服务端 from socket import * import struct client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8093)) while True: cmd=input('>>: ').strip() if not cmd:continue client.send(cmd.encode('utf-8')) #1、先接收命令长度 headers=client.recv(4) total_size = struct.unpack('i', headers)[0] #2、再收命令的结果 recv_size=0 data=b'' while recv_size < total_size: recv_data=client.recv(1024) data+=recv_data recv_size+=len(recv_data) print(data.decode('gbk')) client.close() #解决问题的核心思想就是制作定长的header(使用struct模块)和获取数据的长度
#客户端 from socket import * import subprocess import struct server=socket(AF_INET,SOCK_STREAM) server.bind(('127.0.0.1',8093)) server.listen(5) while True: conn,client_addr=server.accept() print(client_addr) while True: try: cmd=conn.recv(8096) if not cmd:break #ls -l;sadfasdf;pwd;echo 123 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) headers=struct.pack('i',total_size) #2、先发送命令长度 conn.send(headers) #3、发送命令的执行结果 conn.send(stdout) conn.send(stderr) except ConnectionResetError: break conn.close() server.close()