python网络编程03 socketserver模块

1、socketserver模块

  • socketserver是标准库中的一个高级模块(Python2.x中名为SocketServer)。
    • socketserver的目标是简化创建网络客户端和服务器的代码。(隐藏了实现细节)
    • socketserver是使用类来编写应用程序。
      • 以面向对象的方式处理事务有助于组织数据,以及逻辑性地将功能放在正确的地方。应用程序现在是事件驱动的,这意味着只有在系统中的事件发生时,它们才会工作。
  • socketserver中包含了两种类,一种为服务类(server class),一种为请求处理类(request handle class)。
    • 服务类提供了许多方法:像绑定,监听,运行…… (也就是建立连接的过程)
    • 请求处理类专注于如何处理用户所发送的数据(也就是事务逻辑)。
  • socketserver模块是一个以socket为基础而创建的高级套接字通信模块,它支持客户端请求的多线程和多进程处理。
    • 在原始服务器循环中,我们阻塞等待请求,当接收到请求时就对其提供服务,然后继续等待。
    • 在socketserver的服务器循环中,并非在服务器中创建代码,而是定义一个处理程序,这样当服务器接收到一个传入的请求时,服务器就可以调用你的函数。
  • socketserver模块类

1、服务类(同步处理请求)

  • class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
    • 它使用Internet TCP协议,该协议在客户机和服务器之间提供连续的数据流。
    • 如果bind_and_activate为true,构造函数会自动尝试调用server_bind()和server_activate()。
    • 其他参数传递给BaseServer基类。
  • class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
    • 它使用数据报,这是离散的信息包,可能会无序地到达或在传输中丢失。
    • 参数与TCPServer相同。
  • class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
    class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)

    • 这两个不常用的类类似于TCP和UDP类,但使用Unix域套接字;它们在非unix平台上不可用。
    • 参数与TCPServer相同。
  • 这四个类同步处理请求每个请求必须完成后才能启动下一个请求。
    如果每个请求都需要很长时间才能完成,这是不合适的,因为它需要大量的计算,或者因为它返回大量的数据,而客户机处理起来很慢。
    解决方案每个请求都创建一个单独的进程或线程来处理;ForkingMixIn和ThreadingMixIn的混合类可以用来支持异步行为。

  • 继承图中有五个类,其中四个表示四种类型的同步服务器
    • 注意UnixDatagramServer派生于UDPServer,而不是UnixStreamServer --- IP和Unix流服务器之间的唯一区别是地址族,这只是在两个Unix服务器类中重复。

  • 服务类具有相同的外部方法和属性,无论它们使用什么网络协议。

2、请求处理类

1、socketserver.BaseRequestHandler

  • 这是所有请求处理程序对象的超类。它定义了接口,如下所示。
    • 一个具体的请求处理程序子类必须定义一个新的handle()方法,并且可以覆盖任何其他方法。
    • 每个请求都会创建一个子类的新实例。

1、setup()方法

  • 在handle()方法之前调用,以执行所需的任何初始化操作。默认什么也不做。

2、handle()方法

  • 此方法必须完成为请求提供服务所需的所有工作。默认什么也不做。
  • 它可以使用几个实例属性;请求是self.request;客户端地址是self.client_address;服务器实例是self.server,以防它需要访问每个服务器的信息。
  • self.request的类型因数据报或流服务而不同
    • 对于流服务,self.request是客户端套接字对象;
    • 对于数据报服务,self.request是一个双元组(客户端发送的数据, 客户端的套接字对象)

3、finish()方法

  • 在handle()方法之后调用,执行所需的任何清理操作。默认什么也不做。
  • 如果setup()引发异常,则不会调用此函数。

2、socketserver模块的StreamRequestHandler类和DatagramRequestHandler

  • 这两个BaseRequestHandler子类覆盖了setup()和finish()方法,并提供了self.rfile和self.wfile属性。self.rfile和self.wfile分别读取或写入,以获取请求数据或将数据返回给客户机。
  • 两个类的rfile属性都支持io.BufferedIOBase可读的接口,和DatagramRequestHandler.wfile支持io.BufferedIOBase可写接口。
  • python3.6版:StreamRequestHandler.wfile也支持io.BufferedIOBase可写接口。

3、mix-in(混合)服务类(异步处理请求)

  • 可以使用mix-in类创建每种类型基于进程(Forking)和线程(threading)的服务器。
    • mix-in类:socketserver.ForkingMixIn类每次处理用户连接的时候都会开启新的进程。(多进程)
    • mix-in类:socketserver.ThreadingMixIn类每次处理用户连接的时候都会开启新的线程。(多线程)
  • 四种混合服务类
    • mix-in类排在第一位,因为它覆盖了UDPServer中定义的方法。设置各种属性还会更改底层服务器机制的行为。
    • ForkingMixIn和Forking类只在支持fork()的POSIX平台上可用。
#socketserver.ForkingTCPServer
class ForkingTCPServer(ForkingMixIn, TCPServer):
    pass

#socketserver.ForkingUDPServer
class ForkingUDPServer(ForkingMixIn, UDPServer):
    pass

#socketserver.ThreadingTCPServer
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
    pass

#socketserver.ThreadingUDPServer
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass
  • socketserver.ForkingMixIn.server_close()会等待所有子进程完成,除非socketserver.ForkingMixIn.block_on_close属性为false。
  • socketserver.ThreadingMixIn.server_close()会等待所有非守护线程完成,除非socketserver.ThreadingMixIn.block_on_close属性为false。通过将ThreadingMixIn.daemon_threads设置为True来使用守护线程,这样就不会等待线程完成。
  • python3.7:socketserver.ForkingMixIn.server_close()和socketserver.ThreadingMixIn.server_close()现在会等待所有子进程和非守护线程完成。添加一个新的socketserver.ForkingMixIn.block_on_close类属性来选择加入3.7之前的行为。

4、创建服务器需的步骤

  1. 创建一个请求处理类(request handler class),合理选择StreamRequestHandler和DatagramRequestHandler之中的一个作为父类(当然也可以使用BaseRequestHandler作为父类),并重写它的handle()方法,此方法将处理传入的请求。。
  2. 实例化一个服务类(server class)对象,并将服务器地址和之前创建的请求处理类传递给它。(建议在with语句中实例化服务类)
  3. 调用服务类对象的handle_request()或serve_forever()方法来处理一个或多个请求。
  4. 调用server_close()关闭套接字(除非使用with语句)。
  • socketserver请求处理程序的默认行为是接受连接、获取请求,然后关闭连接。由于这个原因,我们不能在应用程序整个执行过程中都保持连接,因此每次向服务器发送消息时,都需要创建一个新的套接字。

2、服务类创建TCP服务器

1、python3创建TCP服务器和客户端

  • 事件包括消息的发送和接收。事实上,类定义只包括一个用来接收客户端消息的事件处理程序。所有其他的功能都来自使用的SocketServer类。
  • 最后一行代码通常是一个服务器的无限循环,它等待并响应客户端的服务请求。

示例1:实例化服务类时不使用with语句

  • 创建SocketServer TCP服务器
    • 多个客户端连接到服务器时,服务器只能按照客户端连接的顺序(即等待队列中的顺序)依次处理,否则将阻塞。
###python3.8
#!/usr/bin/env python
from socketserver import (TCPServer as TCP, StreamRequestHandler as SRH)
from time import ctime

HOST = ''
PORT = 21567
ADDR = (HOST, PORT)

class MyRequesHandler(SRH):                                                    #创建一个请求处理类
    def handle(self):                                                          #重写handle()方法
        self.data = str(self.request.recv(1024).strip(), 'utf8')               #接收客户端发来的数据
        print('...connected from:', self.client_address)
        print(self.data)
        self.request.sendall(bytes('%s %s' % (self.data, ctime()), 'utf8'))    #处理数据,并发送给客户端

TCP.request_queue_size = 10                      #默认等待队列中可以有5个客户端,即最大可以连接6个客户端,request_queue_size = 5
tcpServ = TCP(ADDR, MyRequesHandler)             #实例化一个服务类对象。会创建一个套接字,并绑定地址和监听
print('waiting for connection...')
tcpServ.serve_forever()                          #处理客户端发来的请求,其实就是调用handle()方法
  • 创建SocketServer TCP客户端
###python3.8
#!/usr/bin/env python
from socket import *

HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

while True:
    tcpClisock = socket(AF_INET, SOCK_STREAM)
    tcpClisock.connect(ADDR)
    data = input('> ')
    if not data:
        break
    tcpClisock.send(bytes(data,'utf8'))
    data = tcpClisock.recv(BUFSIZ)
    if not data:
        break
    print(str(data.strip(),'utf8'))
    tcpClisock.close()

示例2:实例化服务类时使用with语句

  • 创建SocketServer TCP服务器
    • 多个客户端连接到服务器时,服务器只能按照客户端连接的顺序(即等待队列中的顺序)依次处理,否则将阻塞。
###python3.8
#!/usr/bin/env python
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 21567
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        server.serve_forever()
  • 创建SocketServer TCP客户端
###python3.8
#!/usr/bin/env python
import socket
import sys

HOST, PORT = "localhost", 21567
while True:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((HOST, PORT))
        data = input('>')
        if not data:
            break
        sock.sendall(bytes(data + "\n", "utf-8"))
        received = str(sock.recv(1024), "utf-8")
    print("Sent:     {}".format(data))
    print("Received: {}".format(received))

2、python2创建TCP服务器和客户端

  • 创建SocketServer TCP服务器
###python2.7
#!/usr/bin/env python
from SocketServer import (TCPServer as TCP, StreamRequestHandler as SRH)
from time import ctime

HOST = ''
PORT = 21567
ADDR = (HOST, PORT)

class MyRequesHandler(SRH):
    def handle(self):
        print '...connected from:',self.client_address
        self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline()))

tcpServ = TCP(ADDR, MyRequesHandler)
print 'waiting for connection...'
tcpServ.serve_forever()
  • 创建SocketServer TCP客户端
###python2.7
#!/usr/bin/env python
from socket import *

HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

while True:
    tcpClisock = socket(AF_INET, SOCK_STREAM)
    tcpClisock.connect(ADDR)
    data = raw_input('> ')
    if not data:
        break
    tcpClisock.send('%s\r\n' % data)
    data = tcpClisock.recv(BUFSIZ)
    if not data:
        break
    print data.strip()
    tcpClisock.close()

3、服务类创建UDP服务器

  • 创建SocketServer UDP服务器
    • 多个客户端连接到服务器时,服务器收到一个请求处理一个请求,不会阻塞。
###python3.8
#!/usr/bin/env python
import socketserver

class MyUDPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        #print(self.request)    #(b'221\n', <socket.socket fd=428, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('127.0.0.1', 21567)>)
        data = self.request[0].strip()
        socket = self.request[1]
        print("{} wrote:".format(self.client_address[0]))
        print(data)
        socket.sendto(data.upper(), self.client_address)

if __name__ == "__main__":
    HOST, PORT = "localhost", 21567
    with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
        server.serve_forever()
  • 创建SocketServer UDP客户端
###python3.8
#!/usr/bin/env python
import socket

HOST, PORT = "localhost", 21567
while True:
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    data = input('> ')
    if not data:
        break
    sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
    received = str(sock.recv(1024), "utf-8")
    print("Sent:     {}".format(data))
    print("Received: {}".format(received))

4、mix-in(混合)类创建TCP服务器

1、多线程处理请求(ThreadingMixIn类)

  • 创建SocketServer TCP服务器
    • 多个客户端连接到服务器时,客户端的每个请求服务器都会新创建一个线程进行处理。
###python3.8
#!/usr/bin/env python
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 21567
    with socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) as server:    #与同步的相比,几乎没有变化,除了服务类使用了ThreadingTCPServer类
        server.serve_forever()
  • 创建SocketServer TCP客户端
###python3.8
#!/usr/bin/env python
from socket import *

HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

while True:
    tcpClisock = socket(AF_INET, SOCK_STREAM)
    tcpClisock.connect(ADDR)
    data = input('> ')
    if not data:
        break
    tcpClisock.send(bytes(data, 'utf8'))
    data = tcpClisock.recv(BUFSIZ)
    if not data:
        break
    print(str(data.strip(), 'utf8'))
    tcpClisock.close()

2、多进程处理请求(ForkingMixIn类)

  • 创建SocketServer TCP服务器
    • 多个客户端连接到服务器时,客户端的每个请求服务器都会新创建一个进程进行处理。
    • 特别注意:必须运行在unix上。因为Windows不支持fork(),所以ForkingMixIn类在Windows上不可用
#!/usr/bin/env python
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = '192.168.248.128', 21567
    with socketserver.ForkingTCPServer((HOST, PORT), MyTCPHandler) as server:    #与同步的相比,几乎没有变化,除了服务类使用了ForkingTCPServer类
        server.serve_forever()
  • 创建SocketServer TCP客户端
###python3.8
#!/usr/bin/env python
from socket import *

HOST = '192.168.248.128'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT)

while True:
    tcpClisock = socket(AF_INET, SOCK_STREAM)
    tcpClisock.connect(ADDR)
    data = input('> ')
    if not data:
        break
    tcpClisock.send(bytes(data, 'utf8'))
    data = tcpClisock.recv(BUFSIZ)
    if not data:
        break
    print(str(data.strip(), 'utf8'))
    tcpClisock.close()

5、mix-in(混合)类创建UDP服务器

1、多线程处理请求(ThreadingMixIn类)

  • 创建SocketServer UDP服务器
###python3.8
#!/usr/bin/env python
import socketserver

class MyUDPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        #print(self.request)
        data = self.request[0].strip()
        socket = self.request[1]
        print("{} wrote:".format(self.client_address[0]))
        print(data)
        socket.sendto(data.upper(), self.client_address)

if __name__ == "__main__":
    HOST, PORT = "localhost", 21567
    with socketserver.ThreadingUDPServer((HOST, PORT), MyUDPHandler) as server:    #区别仅在于服务类是ThreadingUDPServer
        server.serve_forever()
  • 创建SocketServer UDP客户端
###python3.8
#!/usr/bin/env python
import socket

HOST, PORT = "localhost", 21567
while True:
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    data = input('> ')
    if not data:
        break
    sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
    received = str(sock.recv(1024), "utf-8")
    print("Sent:     {}".format(data))
    print("Received: {}".format(received))

2、多进程处理请求(ForkingMixIn类)

  •  创建SocketServer UDP服务器
    • 特别注意:必须运行在unix上。因为Windows不支持fork(),所以ForkingMixIn类在Windows上不可用。
#!/usr/bin/env python
import socketserver
 
class MyUDPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        #print(self.request)
        data = self.request[0].strip()
        socket = self.request[1]
        print("{} wrote:".format(self.client_address[0]))
        print(data)
        socket.sendto(data.upper(), self.client_address)
 
if __name__ == "__main__":
    HOST, PORT = '192.168.248.128', 21567
    with socketserver.ForkingUDPServer((HOST, PORT), MyUDPHandler) as server:    #区别仅在于服务类是ForkingUDPServer
        server.serve_forever()
  • 创建SocketServer UDP客户端
###python3.8
# !/usr/bin/env python
import socket

HOST, PORT = '192.168.248.128', 21567
while True:
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    data = input('> ')
    if not data:
        break
    sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
    received = str(sock.recv(1024), "utf-8")
    print("Sent:     {}".format(data))
    print("Received: {}".format(received))

 

posted @ 2021-06-24 01:19  麦恒  阅读(1877)  评论(0编辑  收藏  举报