SocketServer模块学习

本文介绍如何使用Python的SocketServer模块搭建一个多线程聊天室服务器。文章详细解析了SocketServer模块的工作原理,包括ThreadingTCPServer类的使用,并提供了一个完整的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SocketServer是Python中的Socket编程的一个重要的模块,有了这个模块可以快速实现一个多线程的Socket服务器。


下来看一个简单的使用SocketServer模块建立的聊天室服务器端。

#!/usr/bin/python
# -*- coding:utf-8 -*-

import SocketServer

class MyServer(SocketServer.BaseRequestHandler):
    def handle(self):
        print self.client_address
        conn = self.request
        conn.send('Welcome to North,What can I do for you')
        while True:
            data = conn.recv(1024)
            if data == 'exit':
                conn.close()
                break
            else:
                print 'client <'+str(self.client_address)+'>:'+data
                tmp = raw_input("Server : ")
                conn.send(tmp)



if __name__ == '__main__':
    ip_port = ('127.0.0.1',9000)
    server = SocketServer.ThreadingTCPServer(ip_port,MyServer)
    server.serve_forever()



Socket编程的基础就不提了,简而言之就是 

创建监听套接字,绑定监听套接字-->监听该监听套接字-->接收到请求并返回一个连接套接字-->创建一个线程(进程)去接待该连接套接字的客户端


看看上面的示例代码,直接实例化一个类。传入的参数是一监听的IP和端口和自创建的一个类。先看看ThreadingTCPServer 这个类。


这个类其实什么也没做,就是集成了两个父类。


先看看这些类的关系。


先看看ThreadingMixIn这个类。这个类的截图如下,只定义了两个方法,这两个方法在之后的serve_forever() 。有被调用。由于这些process_request ...这些太底层了,无法再往下查看,所以这个就不再往下解释了。(其实我也解释不清这个线程是怎么实现的)只知道是调用了一个线程方法实现了线程就可以。


下来进入TCPserver这个类看看。这个类在实例化的时候,就已经实现了创建监听套接字,绑定和监听。参数都是因为选择的TCP服务器而默认的,比如AF_INET的ipv4和SOCK_STREAM的流式套接字,还有监听的接收缓冲区大小为5,和跟TIME_WAIT相关的reuse_address。这个需要setsockopt函数去实现下。

    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    request_queue_size = 5

    allow_reuse_address = True

    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
        """Constructor.  May be extended, do not override."""
        BaseServer.__init__(self, server_address, RequestHandlerClass)
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if bind_and_activate:
            self.server_bind()
            self.server_activate()

    def server_bind(self):
        """Called by constructor to bind the socket.

        May be overridden.

        """
        if self.allow_reuse_address:
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)
        self.server_address = self.socket.getsockname()

    def server_activate(self):
        """Called by constructor to activate the server.

        May be overridden.

        """
        self.socket.listen(self.request_queue_size)


这里的TCPServer这个又继承了一个BaseServer这个类。而最开始传入的参数ip和端口还有MyServer这个类就被交给了BaseServer这里初始化。

class BaseServer:
    timeout = None

    def __init__(self, server_address, RequestHandlerClass):
        """Constructor.  May be extended, do not override."""
        self.server_address = server_address
        self.RequestHandlerClass = RequestHandlerClass
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False

在__init__初始化后,就可以


下面所有的初始化工作都完成了,接下来的就是serve_forever了。

看看BaseServer中的这个serve_forever这个类。

    def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.

        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        self.__is_shut_down.clear()
        try:
            while not self.__shutdown_request:
                # XXX: Consider using another file descriptor or
                # connecting to the socket to wake this up instead of
                # polling. Polling reduces our responsiveness to a
                # shutdown request and wastes cpu at all other times.
                r, w, e = _eintr_retry(select.select, [self], [], [],
                                       poll_interval)
                if self in r:
                    self._handle_request_noblock()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()

serve_forever这个方法先做了一件事。

使用self.__is_shut_down.clear()。wait()线程将阻塞直到set()叫将内部标志重新设置为true。

核心内容是采用了select()这样的阻塞方式。如果收到了r事件。

就执行_handle_request_noblock()。

    def _handle_request_noblock(self):
        """Handle one request, without blocking.

        I assume that select.select has returned that the socket is
        readable before this function was called, so there should be
        no risk of blocking in get_request().
        """
        try:
            request, client_address = self.get_request()
        except socket.error:
            return
        if self.verify_request(request, client_address):
            try:
                self.process_request(request, client_address)
            except:
                self.handle_error(request, client_address)
                self.shutdown_request(request)

刚开始,这个方法返回了request--(连接套接字)。client_address--(客户端地址)。


其实挺复杂的。所有的返回值,直接就可以在MyServer那个类中直接调用。

因为MyServer那个类继承了BaseRequestHandler这个类。

class BaseRequestHandler:

    """Base class for request handler classes.

    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.

    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define arbitrary other instance variariables.

    """

    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()
        try:
            self.handle()
        finally:
            self.finish()

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值