python开发学习-day08(socket高级、socketserver、进程、线程)

时间:2021-11-16 23:59:09

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可以读取或写入,以获得请求数据或将数据返回到客户端。

示例代码

  1. 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()
    
  2. 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

  1. 异常基础(python3的写法)

    在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是显示一个提示的页面,通俗来说就是不让用户看见代码出错的页面

    try:
        pass
    except Exception as ex:
        pass
    
  2. 异常种类

    常用异常:

    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
    
  3. 自定义异常类

    #!/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)
    
  4. 示例代码:

    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)
    
  5. 特殊异常

    虽然python自带的一个处理万能异常类Exception,但是并不是有一些异常都能被捕获的。如果要想捕获这些特殊的异常,就需要进行自定义异常类

  6. 异常其他架构

    try:
        # 主代码块
        pass
    except KeyError as e:
        # 异常时,执行该块
        pass
    else:
        # 主代码块执行完,执行该块
        pass
    finally:
        # 无论异常与否,最终执行该块
        pass
    
  7. 主动触发异常

    try:
        raise Exception('错误了。。。')
    except Exception as e:
        print("Error",e)
    
  8. Asser断言:

    至关重要的判断,强制判断前面的业务结果是否符合要求,否则就抛出异常

    a = 1
    try:
        assert a == 2
        print("True")
    except Exception as e:
        print("False",e)
    

四、进程与线程

  1. 概念

    1. 进程

      一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度运行的基本单位
      
    2. 线程

      线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
      
    3. 进程与线程的区别

      1. 进程是一个动态的概念
      
      进程是程序的一次执行过程,是动态概念
      
      程序是一组有序的指令集和,是静态概念
      
      2. 不同的进程可以执行同一个程序
      
      区分进程的条件:所执行的程序和数据集合。
      
      两个进程即使执行在相同的程序上,只要他们运行在不同的数据集合上,他们也是两个进程。例如:多个用户同时调用同一个编译程序编译他们编写的C语言源程序,由于编译程序运行在不同的数据集合(不同的C语言源程序)上,于是产生了一个个不同的进程
      
      3. 每个进程都有自己的生命周期
      
      当操作系统要完成某个任务时,它会创建一个进程。当进程完成任务之后,系统就会撤销这个进程,收回它所占用的资源。从创建到撤销的时间段就是进程的生命期
      
      4. 进程之间存在并发性
      
      在一个系统中,同时会存在多个进程。他们轮流占用CPU和各种资源
      
      5. 进程间会相互制约
      
      进程是系统中资源分配和运行调度的单位,在对资源的共享和竞争中,必然相互制约,影响各自向前推进的速度
      
      6. 进程可以创建子进程,程序不能创建子程序
      
      7. 从结构上讲,每个进程都由程序、数据和一个进程控制块(Process Control Block, PCB)组成
      
  2. 进程锁

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

进程同步

进程池