当前位置: 移动技术网 > IT编程>脚本编程>Python > socketserver 模块简介

socketserver 模块简介

2019年12月26日  | 移动技术网IT编程  | 我要评论

恐怖六一劫,六宫 月揽香,群众意见调查表

一、socketserver模块简介

socketserver模块简化了网络编程,模块下有五个服务类:baseserver、tcpserver、udpserver、unixstreamserver、unixdatagramserver 。这五个类的关系如下:

+------------+
| baseserver |
+------------+
      |
      v
+-----------+        +------------------+
| tcpserver |------->| unixstreamserver |
+-----------+        +------------------+
      |
      v
+-----------+        +--------------------+
| udpserver |------->| unixdatagramserver |

1、四个基本的同步服务器类简介: 

class socketserver.tcpserver(server_address, requesthandlerclass, bind_and_activate=true):tcp数据流服务器
class socketserver.udpserver(server_address, requesthandlerclass, bind_and_activate=true):udp数据报服务器

class socketserver.unixstreamserver(server_address, requesthandlerclass, bind_and_activate=true):仅限于unix系统的,unix套接字流
class socketserver.unixdatagramserver(server_address, requesthandlerclass, bind_and_activate=true):仅限于unix系统的,unix数据报

他们的参数意义相同,如下:

server_address:服务器的地址,他应该是一个元组包含地址和端口如:('192.168.1.1',9000),

requesthandlerclass:我们自定义的类,类中必须重写handle()方法。用于处理所有socket请求。

bind_and_activate:如果为true,将自动调用server_bind()和server_activate()。

 这四个类运行时只能处理一个请求,也就是一个服务端只能对应一个客户端,这对于我们将来要编写的ftp服务器可能不适用,因为我们希望一个服务能处理多个客户端,下面我们来看socketserver为我们提供的两个处理异步的类。

2、异步服务器类简介

class socketserver.forkingmixin:启用多进程
class socketserver.threadingmixin:启用多线程

创建异步服务的方法非常简单,下面创建一个简单的tcp框架为例:

import socketserver

class mytcpserver(socketserver.baserequesthandler):  # 自定义tcp服务类,必须继承socketserver.baserequesthandler
    def handle(self):  # 重写此类,
        '''
        我们要处理的任务
        :return: 
        '''
        self.fun()  # 执行我们的任务
    def fun(self):
        print('hello world')

class threadingtcpserver(socketserver.threadingmixin, socketserver.tcpserver): # 类名随便起,必须要继承这两个类
    pass
if __name__ == "__main__":
    ip_port = ("127.0.0.1", 9000)  # tcp服务器的地址和端口

    with socketserver.threadingtcpserver(ip_port, mytcpserver) as server:
        server.serve_forever()  # 开启tcp服务

这样我们就简单的创建了一个异步tcp服务器框架,其实threadingtcpserver这个类我们不用自己创建,因为socketserver已经为我们创建好了,如下:

class forkingudpserver(forkingmixin, udpserver): pass  # 多进程udp服务器
class forkingtcpserver(forkingmixin, tcpserver): pass  # 多进程tcp服务器
class threadingudpserver(threadingmixin, udpserver): pass  # 多线程udp服务器
class threadingtcpserver(threadingmixin, tcpserver): pass  # 多线程tcp服务器

如果觉得socketserver提供的这四个类不是你想要的,那么你就可以像上面那样自己定义,上面都是服务类,通过服务类实例化对象,但是目前还不知道对象拥有哪些方法,因为这些服务类都是继承的baseserver类,所以方法都在baseserver类中,有些方法只是定义了接口,在子类中实现的。

3、服务对象常用方法

class socketserver.baseserver(server_address, requesthandlerclass):

fileno():返回服务器正在监听的套接字的文件描述符(int类型的数字)。此函数最常传递给选择器,以允许监视同一进程中的多个服务器。

handle_request():处理单个请求。此函数按顺序调用以下方法:get_request()、verify_request()和process_request()。如果handler类的用户提供的handle()方法引发异常,则将调用服务器的handle_error()方法。如果在超时秒内未收到请求,将调用handle_timeout(),并返回handle_request()。

serve_forever(poll_interval=0.5):定时任务,通常是在一个线程中,每poll_interval秒轮询一次,直到调用shutdown停止。

service_actions():该函数被serve_forever定时函数重复调用,这个方法我们可以继承baseserver,然后重写此方法。

shutdown():此方法用于停止serve_forever()定时任务。

socket:socket对象。

socket_type:socket套接字类型,tcp,udp等。

allow_reuse_address:服务器是否允许地址的重用。默认为false ,并且可在子类中更改。

address_family:设置socket套接字家族。

server_address:值是一个元组,socket服务器地址和监听的端口。

server_activate():服务器将处于监听状态,该函数可被重写,其实他的内部就是self.socket.listen(self.request_queue_size)。

server_bind():将socket绑定到地址上,可以被重写。

get_request():此方法的前提是必须接收到来自套接字的请求,返回一个元组(与客户端通信的新套接字对象,客户端地址)。其实该方法就是将self.socket.accept()的结果返回。

server_close():关闭服务(关闭socket),此方法可被重写。

requesthandlerclass:值是类名,这个类是我们定义的用于创建实例处理我们的请求,如上面tcp异步框架中的mytcpserver,这个类就是requesthandlerclass的值。

request_queue_size:请求队列的大小。如果处理单个请求需要很长时间,在服务器繁忙时会将请求放到队列中,当请求数达到request_queue_size的值时。来自客户端的进一步请求将得到一个“拒绝连接”错误。默认值通常是5,但是这个值可以被子类覆盖。

finish_request(request, client_address):此方法会实例化requesthandlerclass并调用它的handle()方法来实际处理请求。

process_request(request,client_address):调用finish_request()来创建requesthandlerclass的一个实例。我们可以自己创建线程池或进程池来调用这个函数,实现服务器处理多个请求的问题,forkingmixin和threadingmixin类就是这样做的。

handle_error(request,client_address):如果requesthandlerclass实例的handle()方法引发异常,则调用此函数。默认操作是将回溯打印到标准错误,并继续处理其他请求。在版本3.6中更改:现在仅调用从exception类派生的异常。

timeout:超时时间(以秒为单位),如果是none,会一直阻塞。如果设置了timeout,handle_request()在超时期间没有收到传入请求,则调用handle_timeout()方法。

handle_timeout():当timeout属性被设置为none以外的值,并且超时周期已经过去而没有收到任何请求时,将调用此函数。多进程 服务器的默认操作是收集已退出的任何子进程的状态,而在线程服务器中,此方法不执行任何操作。

verify_request(request,client_address):返回一个布尔值;如果值为真,请求将被处理,如果值为假,请求将被拒绝。可以重写此函数以实现服务器的访问控制。默认实现只是一句return true。

上面这些都是服务对象的方法,下面来介绍处理socket请求类baserequesthandler。

4、客户端请求处理类baserequesthandler

class socketserver.baserequesthandler:

这是所有socket请求处理程序的基类。它只定义了接口,而没有实现,如果想要使用接口,我们首先继承baserequesthandler,然后在子类中重写这些方法。每个socket请求处理程序子类必须重写handle()方法,因为该方法是用于处理所有socket请求。该类的方法如下:

setup():在handle()方法之前调用,执行初始化操作。 默认不执行任何操作,我们可以重写此方法来实现程序的初始化。

handle():所有socket请求任务都是在这个函数内部完成的,我们在子类中必须重写此方法,并处理socket请求,因为默认基类中的handle()的实现不执行任何操作。

finish():在handle()方法之后调用以执行清理操作。默认实现不执行任何操作。如果setup()引发异常,则不会调用此函数。

虽然上面的接口都只是定义而没有实现,但是它的实例属性还是很有用的;

self.request;客户端和服务端的连接对象,用于发送数据,接收数据。 

self.client_address:socket客户端地址 。

self.server:socket服务端信息。

class socketserver.streamrequesthandler
class socketserver.datagramrequesthandler
这两个类是socketserver继承baserequesthandler后重写了setup(),finish(),实现了对读,写缓冲区的设置,有兴趣的可以看看源码。

5、官方示例:

tcp同步服务示例:

# 服务端
import socketserver

class mytcphandler(socketserver.baserequesthandler):  # 自定义类,继承baserequesthandler,处理socket请求

    def handle(self):  # socket客户端请求
        self.data = self.request.recv(1024).strip()  # 接收socket客户端发来的数据
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())  # 将数据大写后发给客户端
'''
class mytcphandler(socketserver.streamrequesthandler):  
    # 自定义类,功能与上面的一样,只不过是继承streamrequesthandler
    def handle(self):
        # self.rfile is a file-like object created by the handler;
        # we can now use e.g. readline() instead of raw recv() calls
        self.data = self.rfile.readline().strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # likewise, self.wfile is a file-like object used to write back
        # to the client
        self.wfile.write(self.data.upper())
'''

if __name__ == "__main__":
    host, port = "localhost", 9999

    with socketserver.tcpserver((host, port), mytcphandler) as server:
        server.serve_forever()  # 启用tcp服务器
# 打印内容如下
127.0.0.1 wrote:
b'hello world'

# 客户端
import socket
import sys

host, port = "localhost", 9999
data = "hello world"

# create a socket (sock_stream means a tcp socket)
with socket.socket(socket.af_inet, socket.sock_stream) as sock:
    # connect to server and send data
    sock.connect((host, port))
    sock.sendall(bytes(data + "\n", "utf-8"))

    # receive data from the server and shut down
    received = str(sock.recv(1024), "utf-8")

print("sent:     {}".format(data))
print("received: {}".format(received))

# 打印内容如下
sent:     hello world
received: hello world

udp服务示例:

# 服务端
import socketserver

class myudphandler(socketserver.baserequesthandler):
    """
    this class works similar to the tcp handler class, except that
    self.request consists of a pair of data and client socket, and since
    there is no connection the client address must be given explicitly
    when sending data back via sendto().
    """

    def handle(self):
        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", 9999
    with socketserver.udpserver((host, port), myudphandler) as server:
        server.serve_forever()
# 打印内容如下
127.0.0.1 wrote:
b'hello world'

# 客户端
import socket
import sys

host, port = "localhost", 9999
data = "hello world"

# sock_dgram is the socket type to use for udp sockets
sock = socket.socket(socket.af_inet, socket.sock_dgram)

# as you can see, there is no connect() call; udp has no connections.
# instead, data is directly sent to the recipient via sendto().
sock.sendto(bytes(data + "\n", "utf-8"), (host, port))
received = str(sock.recv(1024), "utf-8")

print("sent:     {}".format(data))
print("received: {}".format(received))
# 打印内容如下
sent:     hello world
received: hello world

 tcp服务异步示例:

import socket
import threading
import socketserver

class threadedtcprequesthandler(socketserver.baserequesthandler):  # 自定义socket请求处理类
    def handle(self):
        data = str(self.request.recv(1024), 'ascii')
        cur_thread = threading.current_thread()
        response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
        self.request.sendall(response)

class threadedtcpserver(socketserver.threadingmixin, socketserver.tcpserver):  # 自定义线程类处理多个请求
    pass

def client(ip, port, message):
    '''
    socket客户端
    :param ip: 服务段的ip地址
    :param port: 服务端的端口
    :param message: 给服务端发送的消息
    :return:
    '''
    with socket.socket(socket.af_inet, socket.sock_stream) as sock:
        sock.connect((ip, port))
        sock.sendall(bytes(message, 'ascii'))
        response = str(sock.recv(1024), 'ascii')
        print("received: {}".format(response))

if __name__ == "__main__":

    host, port = "localhost", 0  # 端口是0随机获取一个未被使用的端口
    server = threadedtcpserver((host, port), threadedtcprequesthandler)
    with server:
        ip, port = server.server_address  # 获取服务端的ip地址和端口号
        server_thread = threading.thread(target=server.serve_forever) # 创建线程对象
        server_thread.daemon = true  # 守护线程
        server_thread.start()  # 开启线程,在线程中开启tcp服务器
        print("server loop running in thread:", server_thread.name)
        # 模拟三个socket客户端连接tcp服务器
        client(ip, port, "hello world 1")
        client(ip, port, "hello world 2")
        client(ip, port, "hello world 3")

        server.shutdown()
# 打印内容如下:
server loop running in thread: thread-1
received: thread-2: hello world 1
received: thread-3: hello world 2
received: thread-4: hello world 3

 

参考文档:


如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网