python开发学习-day08(socket高级、socketserver、进程、线程)
s12-20160305-day08
pytho自动化开发 day08
Date:2016.03.05
@南非波波
课程大纲:
day07
http://www.cnblogs.com/alex3714/articles/5213184.html
day08
http://www.cnblogs.com/alex3714/articles/5227251.html
推荐电影
绝美之城 上帝之城 | 千与千寻 龙猫 卡尔的移动城堡
通过实例私有变量,需要将在类中封装一个方法,该方法返回私有变量的值
一、socket深入
1.概念
Unix的进程通信机制。一个完整的socket有一个本地唯一的socket号,由操作系统分配。socket是面向客户/服务器模型而设计的,针对客户和服务器程序提供不同的socket系统调用。socket利用客户/服务器模式巧妙的解决了进程之间建立通信连接的问题。
套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
2.地址簇
socket.AF_UNIX unix本机进程间通信
socket.AF_INET 使用IPV4地址协议进行进程间通信
socket.AF_INET6 使用IPV6地址协议进行进程间通信
3.套接字类型
socket.SOCK_STREAM #使用tcp协议
socket.SOCK_DGRAM #使用udp协议
socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IFMP等网络报文,而SOCK_RAM可以。其次SOCK_RAM也可以处理特殊的IPV4报文,此外,利用原始套接字可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAW用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAW通常仅限于高级用户或管理员运行的程序使用。
4.socket方法
1. socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
2. socket.socketpair([family[, type[, proto]]])
3. socket.create_connection(address[, timeout[, source_address]])
4. socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
获取要连接的对端主机地址
5. sk.bind(address)
将套接字绑定到地址,地址的格式取决于地址簇。在AF_INET下,以元组(host.port)的形式表示地址。
6. sk.listen(backlog)
开始监听传入的连接,backlog指定在拒绝连接之前,可以挂起的最大连接数量。backlog等于5,表示内核已经连接到连接请求,但服务器还没有调用accept进行处理的连接个数最大为5.这个值根据内核和服务器物理配置进行设置。
7. sk.setblocking(bool)
是否阻塞(默认True),如果设置为False,那么accept和recv时一旦无数据则报错。
8. sk.accept()
接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据,address是连接客户端的地址。接收TCP客户的连接(阻塞式)等待连接的到来。
9. sk.connect(address)
连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.err错误。
10. sk.connect_ex(address)
同上,只是会有返回值,连接成功时返回0,连接失败时会返回编码,例如:10061
11. sk.close()
关闭套接字
12. sk.recv(bufsize[,flag])
接收套接字的数据,数据以字符串形式返回。bufsize指定最多可以接收的数量,建议不要超过1024*8。flag提供有关消息的其他信息。通常可以忽略。
13. sk.recvfrom(bufsize[.flag])
与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址
14. sk.send(string[,flag])
将string中的数据发送到连接的套接字,返回值是要发送的字节数量,该数量可能小于string的字节大小,即:可能未壮指定内容全部发送
15. sk.sendall(string[,flag])
将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则跑出异常。内部通过递归调用send将所有内容发送出去。
16. sk.sendto(string[,flag],address)
将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
17. sk.settimeout(timeout)
设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
18. sk.getpeername()
返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)
19. sk.getsockname()
返回套接字自己的地址,通常在是一个元组(ipaddr,port)
20. socket.gethostname()
获取程序运行所在计算机的主机名
21. gethostbyname(name)
尝试将给定的主机名解释为一个IP地址。首先将检查当前计算机是否能够解释。如果不能,一个解释请求将发送给一个远程的DNS服务器(远程的DNS服务器 还可能将解释请求转发给另一个DNS服务器,直到该请求可以被处理)。gethostbyname函数返回这个IP地址或在查找失败后引发一个异常。例如: socket.gethostbyname('www.apicloud.com')
扩展形式:socket.gethostbyname_ex('www.apicloud.com')
('98e86f98d416f10c.7cname.com', ['www.apicloud.com'], ['117.25.140.17'])
它返回一个包含三个元素的元组,分别是给定地址的主要的主机名、同一IP地址的可选的主机名的一个列表、关于同一主机的同一接口的其它IP地址的一个列表(列表可能都是空的)。
22. gethostbyaddr(address)
函数的作用与gethostbyname_ex相同,只是你提供给它的参数是一个IP地址字符串。
socket.gethostbyaddr('202.165.102.205')
('homepage.vip.cnb.yahoo.com', ['www.yahoo.com.cn'], ['202.165.102.205'])
23. getservbyname(service,protocol)
函数要求一个服务名(如'telnet'或'ftp')和一个协议(如'tcp'或'udp'),返回服务所使用的端口号:
>>> socket.getservbyname('http','tcp')
80
>>> socket.getservbyname('https','tcp')
443
>>> socket.getservbyname('telnet','tcp')
24. sk.fileno()
套接字的文件描述符
socket.sendfile(file, offset=0, count=None)
发送文件 ,但目前多数情况下并无什么卵用
二、socketserver
参考链接:http://my.oschina.net/u/1433482/blog/190612
SocketServer简化了网络服务器的编写。它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。
创建服务器的步骤
首先,你必须创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。其次,你必须实例化一个服务器类,传入服务器的地址和请求处理程序类。最后,调用handle_request()(一般是调用其他事件循环或者使用select())或serve_forever()。
服务器类型
5种类型:BaseServer,TCPServer,UnixStreamServer,UDPServer,UnixDatagramServer。 注意:BaseServer不直接对外服务。
服务器对象
class SocketServer.BaseServer:这是模块中的所有服务器对象的超类。它定义了接口,如下所述,但是大多数的方法不实现,在子类中进行细化。
BaseServer.fileno():返回服务器监听套接字的整数文件描述符。通常用来传递给select.select(), 以允许一个进程监视多个服务器。
BaseServer.handle_request():处理单个请求。处理顺序:get_request(), verify_request(), process_request()。如果用户提供handle()方法抛出异常,将调用服务器的handle_error()方法。如果self.timeout内没有请求收到, 将调用handle_timeout()并返回handle_request()。
BaseServer.serve_forever(poll_interval=0.5): 处理请求,直到一个明确的shutdown()请求。每poll_interval秒轮询一次shutdown。忽略self.timeout。如果你需要做周期性的任务,建议放置在其他线程。
BaseServer.shutdown():告诉serve_forever()循环停止并等待其停止。python2.6版本。
BaseServer.address_family: 地址家族,比如socket.AF_INET和socket.AF_UNIX。
BaseServer.RequestHandlerClass:用户提供的请求处理类,这个类为每个请求创建实例。
BaseServer.server_address:服务器侦听的地址。格式根据协议家族地址的各不相同,请参阅socket模块的文档。
BaseServer.socketSocket:服务器上侦听传入的请求socket对象的服务器。
服务器类支持下面的类变量:
BaseServer.allow_reuse_address:服务器是否允许地址的重用。默认为false ,并且可在子类中更改。
BaseServer.request_queue_size
请求队列的大小。如果单个请求需要很长的时间来处理,服务器忙时请求被放置到队列中,最多可以放request_queue_size个。一旦队列已满,来自客户端的请求将得到 “Connection denied”错误。默认值通常为5 ,但可以被子类覆盖。
BaseServer.socket_type:服务器使用的套接字类型; socket.SOCK_STREAM和socket.SOCK_DGRAM等。
BaseServer.timeout:超时时间,以秒为单位,或 None表示没有超时。如果handle_request()在timeout内没有收到请求,将调用handle_timeout()。
下面方法可以被子类重载,它们对服务器对象的外部用户没有影响。
BaseServer.finish_request():实际处理RequestHandlerClass发起的请求并调用其handle()方法。 常用。
BaseServer.get_request():接受socket请求,并返回二元组包含要用于与客户端通信的新socket对象,以及客户端的地址。
BaseServer.handle_error(request, client_address):如果RequestHandlerClass的handle()方法抛出异常时调用。默认操作是打印traceback到标准输出,并继续处理其他请求。
BaseServer.handle_timeout():超时处理。默认对于forking服务器是收集退出的子进程状态,threading服务器则什么都不做。
BaseServer.process_request(request, client_address) :调用finish_request()创建RequestHandlerClass的实例。如果需要,此功能可以创建新的进程或线程来处理请求,ForkingMixIn和ThreadingMixIn类做到这点。常用。
BaseServer.server_activate():通过服务器的构造函数来激活服务器。默认的行为只是监听服务器套接字。可重载。
BaseServer.server_bind():通过服务器的构造函数中调用绑定socket到所需的地址。可重载。
BaseServer.verify_request(request, client_address):返回一个布尔值,如果该值为True ,则该请求将被处理,反之请求将被拒绝。此功能可以重写来实现对服务器的访问控制。默认的实现始终返回True。client_address可以限定客户端,比如只处理指定ip区间的请求。 常用。
请求处理器
处理器接收数据并决定如何操作。它负责在socket层之上实现协议(i.e., HTTP, XML-RPC, or AMQP),读取数据,处理并写反应。可以重载的方法如下:
setup(): 准备请求处理. 默认什么都不做,StreamRequestHandler中会创建文件类似的对象以读写socket.
handle(): 处理请求。解析传入的请求,处理数据,并发送响应。默认什么都不做。常用变量:self.request,self.client_address,self.server。
finish(): 环境清理。默认什么都不做,如果setup产生异常,不会执行finish。
通常只需要重载handle。self.request的类型和数据报或流的服务不同。对于流服务,self.request是socket 对象;对于数据报服务,self.request是字符串和socket 。可以在子类StreamRequestHandler或DatagramRequestHandler中重载,重写setup()和finish() ,并提供self.rfile和self.wfile属性。 self.rfile和self.wfile可以读取或写入,以获得请求数据或将数据返回到客户端。
示例代码
-
server
#!/usr/local/env python3 ''' Author:@南非波波 Blog:http://www.cnblogs.com/songqingbo/ E-mail:qingbo.song@gmail.com ''' import socketserver class MyHandleServer(socketserver.BaseRequestHandler): ''' 定义一个测试socketserver类 ''' def handle(self): ''' 定义一个函数,用来处理每个客户端发来的请求 :return: ''' print("新建立一个连接:",self.client_address) while True: try: client_data = self.request.recv(1024) if not client_data: print("客户端发送的数据为空,主动断开!",self.client_address) break print("客户端发来的请求:",client_data.decode()) self.request.send(client_data) except ConnectionResetError: print("客户端主动断开!",self.client_address) break if __name__ == "__main__": HOST,PORT = "127.0.0.1",5000 server = socketserver.ThreadingTCPServer((HOST,PORT),MyHandleServer) server.serve_forever()
-
client
#!/usr/local/env python3 ''' Author:@南非波波 Blog:http://www.cnblogs.com/songqingbo/ E-mail:qingbo.song@gmail.com ''' import socket ip_port = ("127.0.0.1",5000) sk = socket.socket() sk.connect(ip_port) while True: msg = input(">>:").strip() if not msg: break sk.sendall(bytes(msg,"utf8")) server_reply = sk.recv(1024) print("服务端返回:",str(server_reply,"utf8")) sk.close()
三、异常处理
http://www.cnblogs.com/wupeiqi/articles/5017742.html
-
异常基础(python3的写法)
在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是显示一个提示的页面,通俗来说就是不让用户看见代码出错的页面
try: pass except Exception as ex: pass
-
异常种类
常用异常:
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 #捕获IO异常 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
-
自定义异常类
#!/usr/local/env python3 ''' Author:@南非波波 Blog:http://www.cnblogs.com/songqingbo/ E-mail:qingbo.song@gmail.com ''' class SwhtError(Exception): ''' 自定义异常 ''' def __init__(self,msg): ''' 初始化函数 :param msg:用户输入message :return: ''' self.message = msg def __str__(self): #名称可以自行定义,只是通过该方法返回message的值 ''' 返回用户输入的信息 :return: ''' return self.message try: raise SwhtError("这是一个致命的错误!") except Exception as e: print("dsdsd",e)
-
示例代码:
dic = ["swht", 'shen'] try: dic[10] except IndexError as e: print("IndexError:",e) dic = {'k1':'v1'} try: dic['k20'] except KeyError as e: print("keyError:",e) s1 = 'hello' try: int(s1) except ValueError as e: print("ValueError:",e)
-
特殊异常
虽然python自带的一个处理万能异常类Exception,但是并不是有一些异常都能被捕获的。如果要想捕获这些特殊的异常,就需要进行自定义异常类
-
异常其他架构
try: # 主代码块 pass except KeyError as e: # 异常时,执行该块 pass else: # 主代码块执行完,执行该块 pass finally: # 无论异常与否,最终执行该块 pass
-
主动触发异常
try: raise Exception('错误了。。。') except Exception as e: print("Error",e)
-
Asser断言:
至关重要的判断,强制判断前面的业务结果是否符合要求,否则就抛出异常
a = 1 try: assert a == 2 print("True") except Exception as e: print("False",e)
四、进程与线程
-
概念
-
进程
一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度运行的基本单位
-
线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
-
进程与线程的区别
1. 进程是一个动态的概念 进程是程序的一次执行过程,是动态概念 程序是一组有序的指令集和,是静态概念 2. 不同的进程可以执行同一个程序 区分进程的条件:所执行的程序和数据集合。 两个进程即使执行在相同的程序上,只要他们运行在不同的数据集合上,他们也是两个进程。例如:多个用户同时调用同一个编译程序编译他们编写的C语言源程序,由于编译程序运行在不同的数据集合(不同的C语言源程序)上,于是产生了一个个不同的进程 3. 每个进程都有自己的生命周期 当操作系统要完成某个任务时,它会创建一个进程。当进程完成任务之后,系统就会撤销这个进程,收回它所占用的资源。从创建到撤销的时间段就是进程的生命期 4. 进程之间存在并发性 在一个系统中,同时会存在多个进程。他们轮流占用CPU和各种资源 5. 进程间会相互制约 进程是系统中资源分配和运行调度的单位,在对资源的共享和竞争中,必然相互制约,影响各自向前推进的速度 6. 进程可以创建子进程,程序不能创建子程序 7. 从结构上讲,每个进程都由程序、数据和一个进程控制块(Process Control Block, PCB)组成
-
-
进程锁
Python threading模块
1. 线程的两种调用方式
1. 直接调用
2. 继承式调用
2. join&&Demo
3. 线程锁
1. 互斥锁
2. 递归锁
3. Semaphore(信号量)
4.
示例代码:
#!/usr/local/env python3
import threading,time
def addNum():
global num #声明修改全局变量
print("--get num:",num)
time.sleep(1)
lock.acquire()
num -=1
lock.release()
lock = threading.Lock()
num = 100
threading_list = []
for i in range(2040):
t = threading.Thread(target=addNum)
t.start()
threading_list.append(t)
for t in threading_list: #等待所有线程执行完毕
t.join()
print("num:",num)
event
多进程
进程间通讯
队列
管道
manager
进程同步
进程池