前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >socket编程【3】SocketServer模块

socket编程【3】SocketServer模块

作者头像
用户2936342
发布2018-08-27 14:59:50
5090
发布2018-08-27 14:59:50
举报
文章被收录于专栏:nummynummy

Python提供了SocketServer用于创建网络服务器。

服务器类型

SocketServer中定义了五个不同的服务器类,它们之间的关系如下:

代码语言:javascript
复制
        +------------+
        | BaseServer |
        +------------+
              |
              v
        +-----------+        +------------------+
        | TCPServer |------->| UnixStreamServer |
        +-----------+        +------------------+
              |
              v
        +-----------+        +--------------------+
        | UDPServer |------->| UnixDatagramServer |
        +-----------+        +--------------------+

BaseServer是基类,它不能实例化使用,TCPServer使用TCP协议通信,UDPServer使用UDP协议通信,UnixStreamServer和UnixDatagramServer使用Unix域套接字,只适用于UNIX平台。

创建服务器流程

创建服务器的步骤。首先,你必须创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。其次,你必须实例化一个服务器类,传入服务器的地址和请求处理程序类。最后,调用handle_request()或serve_forever()。

BaseServer还提供了其他一些方法,我们可以重载这些方法以实现特定功能:

  • verify_request(request, client_address):返回一个布尔值,如果该值为True ,则该请求将被处理,反之请求将被拒绝。此功能可以重写来实现对服务器的访问控制。默认的实现始终返回True。client_address可以限定客户端,比如只处理指定ip区间的请求,常用。
  • *process_request(request, client_address) *:调用finish_request()创建RequestHandlerClass的实例。如果需要,此功能可以创建新的进程或线程来处理请求,ForkingMixIn和ThreadingMixIn类做到这点,常用。
  • 实际处理RequestHandlerClass发起的请求并调用其handle()方法, 常用。

请求处理器

处理器接收数据并决定如何操作。它负责在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可以读取或写入,以获得请求数据或将数据返回到客户端。

实例

下面实现一个简单的带日志功能的Echo服务器,它接收TCP连接,然后响应客户端发送哦所有数据:

代码语言:javascript
复制
import sys
import logging
import SocketServer

logging.basicConfig(level=logging.DEBUG, format="%(name)s:%(message)s")

# 请求处理器类
class EchoRequestHandler(SocketServer.BaseRequestHandler):

    def __init__(self, request, client_address, server):
        self.logger = logging.getLogger("EchoRequestHandler")
        self.logger.debug("__init__")
        SocketServer.BaseRequestHandler.__init__(self, request,client_address, server)
        return

    def setup(self):
        self.logger.debug("setup")
        return SocketServer.BaseRequestHandler.setup(self)

    def handle(self):
        self.logger.debug("handle")
        data = self.request.recv(1024)
        self.request.send(data)
        return

    def finish(self):
        self.logger.debug("finish")
        return SocketServer.BaseRequestHandler.finish(self)

# 服务器类
class EchoServer(SocketServer.TCPServer):
    def __init__(self, server_address, handler_class=EchoRequestHandler):
        self.logger = logging.getLogger("EchoServer")
        self.logger.debug("__init__")
        SocketServer.TCPServer.__init__(self, server_address, handler_class)
        return

    def server_activate(self):
        self.logger.debug("server activate")
        SocketServer.TCPServer.server_activate(self)
        return

    def serve_forever(self, poll_interval=0.5):
        self.logger.debug("waiting for request")
        self.logger.info("Handling requests, press <Ctrl-C> to quit")
        SocketServer.TCPServer.serve_forever(self, poll_interval)
        return

    def handle_request(self, request, client_address):
        self.logger.debug("verify_request %s, %s",request, client_address)
        return SocketServer.TCPServer.verify_request(request, client_address)

    def process_request(self, request, client_address):
        self.logger.debug("process request %s, %s", request, client_address)
        return SocketServer.TCPServer.process_request(self, request, client_address)

    def server_close(self):
        self.logger.debug("server close")
        return SocketServer.TCPServer.server_close(self)

    def finish_request(self, request, client_address):
        self.logger.debug("finish request %s, %s", request, client_address)
        return SocketServer.TCPServer.finish_request(self, request, client_address)

    def close_request(self, request_address):
        self.logger.debug("close request %s", request_address)
        return SocketServer.TCPServer.close_request(self, request_address)

    def shutdown(self):
        self.logger.debug("shutdown")
        return SocketServer.TCPServer.shutdown(self)

# 程序入口
if __name__ == "__main__":
    import socket
    import threading
    address = ("localhost", 0)
    server = EchoServer(address, EchoRequestHandler)
    ip, port = server.server_address
    t = threading.Thread(target=server.serve_forever)
    t.setDaemon(True)
    t.start()

    logger = logging.getLogger("client")
    logger.debug("Server on %s:%s", ip, port)

    logger.debug("creating socket")
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    logger.debug("connecting to server")
    s.connect((ip, port))
    message  = "Hello, world"
    logger.debug("sending data: %s", message)
    len_sent = s.send(message)

    logger.debug("waiting for response")
    response = s.recv(len_sent)
    logger.debug("response from server %s", response)

    server.shutdown()
    logger.debug("closing socket")
    s.close()
    logger.debug("done")
    server.socket.close()

运行代码,输出以下结果:

代码语言:javascript
复制
EchoServer:__init__
EchoServer:server activate
EchoServer:waiting for request
EchoServer:Handling requests, press <Ctrl-C> to quit
client:Server on 127.0.0.1:63245
client:creating socket
client:connecting to server
client:sending data: Hello, world
client:waiting for response
EchoServer:process request <socket._socketobject object at 0x026FD6C0>, ('127.0.0.1', 63246)
EchoServer:finish request <socket._socketobject object at 0x026FD6C0>, ('127.0.0.1', 63246)
EchoRequestHandler:__init__
EchoRequestHandler:setup
EchoRequestHandler:handle
EchoRequestHandler:finish
EchoServer:close request <socket._socketobject object at 0x026FD6C0>
client:response from server Hello, world
EchoServer:shutdown
client:closing socket

线程和进程

TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步,这两个类重载了process_request()方法,当准备 处理一个请求时,就开始一个新的进程或者线程,从而把具体工作放到进程或者线程中运行。

对于线程,需要使用ThreadingMiXin:

代码语言:javascript
复制
import threading
import socket
import SocketServer

class ThreadEchoRequestHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        data = self.request.recv(1024)
        cur_thread = threading.currentThread()
        response = "%s:%s" % (cur_thread.getName(), data)
        print "ok" , response
        self.request.send(response)
        return

class ThreadEchoServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass


if __name__ == "__main__":
    address = ("localhost", 0)
    server = ThreadEchoServer(address, ThreadEchoRequestHandler)
    ip, port = server.server_address
    t = threading.Thread(target=server.serve_forever)
    t.setDaemon(True)
    t.start()
    print "Server loop running in thread: ", t.getName()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((ip, port))
    message  = "Hello, world"
    len_sent = s.send(message)

    response = s.recv(1024)
    print "Received:%s" % response

    s.close()
    server.socket.close()

运行结果如下:

代码语言:javascript
复制
Server loop running in thread:  Thread-1
ok Thread-2:Hello, world
Received:Thread-2:Hello, world

对于不同进程,则使用ForkingMixIn:

代码语言:javascript
复制
import os
import SocketServer

class ForkingEchoRequestHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        # Echo the back to the client
        data = self.request.recv(1024)
        cur_pid = os.getpid()
        response = '%s: %s' % (cur_pid, data)
        self.request.send(response)
        return

class ForkingEchoServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):
    pass

if __name__ == '__main__':
    import socket
    import threading

    address = ('localhost', 0) # let the kernel give us a port
    server = ForkingEchoServer(address, ForkingEchoRequestHandler)
    ip, port = server.server_address # find out what port we were given

    t = threading.Thread(target=server.serve_forever)
    t.setDaemon(True) # don't hang on exit
    t.start()
    print 'Server loop running in process:', os.getpid()

    # Connect to the server
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((ip, port))

    # Send the data
    message = 'Hello, world'
    print 'Sending : "%s"' % message
    len_sent = s.send(message)

    # Receive a response
    response = s.recv(1024)
    print 'Received: "%s"' % response

    # Clean up
    s.close()
    server.socket.close()

运行结果如下:

代码语言:javascript
复制
Server loop running in process: 96038
Sending : "Hello, world"
Received: "96040: Hello, world"
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.11.21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 服务器类型
  • 创建服务器流程
  • 请求处理器
  • 实例
  • 线程和进程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档