官方提供了socketserver包去方便我们快速的搭建一个服务器框架。

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:1097524789

server类

socketserver包提供5个Server类,这些单独使用这些Server类都只能完成同步的操作,他是一个单线程的,不能同时处理各个客户端的请求,只能按照顺序依次处理。

+------------+
| BaseServer |
+------------+|v
+-----------+    +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+    +------------------+|v
+-----------+    +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+    +--------------------+

两个Mixin类

+--------------+    +----------------+
| ForkingMixIn |    | ThreadingMixIn |
+--------------+    +----------------+

各自实现了多进程和多线程的功能(ForkingMixIn在Windows不支持)

于是将这些同步类和Mixin类组合就实现了异步服务类的效果。

class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

class ForkingUDPServer(ForkingMixIn, UDPServer): pass 
class ForkingTCPServer(ForkingMixIn, TCPServer): pass

基本使用

由于server需要同时处理来自多个客户端的请求,需要提供异步的支持,所以通常使用上面的异步类创建服务器。在Windows系统中没有提供os.fork()接口,Windows无法使用多进程的ForkingUDPServer和ForkingTCPServer,只能使用ThreadingTCPServer或者ThreadingUDPServer;而Linux和Unix多线程和多进程版本都可以使用。

服务器主要负责接受客户端的连接请求,当一个新的客户端请求到来后,将分配一个新的线程去处理这个请求(异步服务器ThreadingTCPServer),而与客户端信息的交互则交给了专门的请求处理类(RequestHandlerClass)处理。

import socketserver
# 创建一个基于TCP的server对象,并使用BaseRequestHandler处理客户端发送的消息
server = socketserver.ThreadingTCPServer(("127.0.0.1", 8000), BaseRequestHandler)
server.serve_forever() # 启动服务器,

只需要上面两行代码就可以创建开启一个服务,运行上面代码后常看本机8000端口,发现有程序正在监听。

C:\Users\user>netstat -anp tcp | findstr 8000
TCP 127.0.0.1:8000 0.0.0.0:0 LISTENING

ThreadingTCPServer可以对我们的请求进行接受,但是并不会进行处理请求,处理请求的类是上面指定BaseRequestHandler类,该类可以定义handle方法来处理接受的请求。

BaseRequestHandler的源码

class BaseRequestHandler:def __init__(self, request, client_address, server):self.request = requestself.client_address = client_addressself.server = serverself.setup()try:self.handle()finally:self.finish()def setup(self):passdef handle(self):passdef finish(self):pass

在server = socketserver.ThreadingTCPServer(("127.0.0.1", 8000), BaseRequestHandler)中,BaseRequestHandler将作为参数绑定到服务器的实例上,服务器启动后,每当有一个新的客户端接接入服务器,将会实例化一个请求处理对象,并传入三个参数,request(连接客户端的socket)、client_address(远程客户端的地址)、server(服务器对象),执行init方法,将这三个参数保存到对应属性上。这个请求处理对象便可以与客户端交互了。

简单示例

import socketserver
import threading class MyRequestHandler(socketserver.BaseRequestHandler):""" BaseRequestHandler的实例化方法中,获得了三个属性self.request = request  # 该线程中与客户端交互的 socket 对象。self.client_address   # 该线程处理的客户端地址self.server = server   # 服务器对象"""def handle(self):while True:msg = self.request.recv()  # 接受客户端的数据if msg == b"quit" or msg == "": # 退出breakprint(msg.decode())self.request.send(msg) # 将消息发送回客户端def finish(self):self.request.close()    # 关闭套接字
if __name__ == "__main__":# 创建一个基于TCP的server对象,并使用BaseRequestHandler处理客户端发送的消息server = socketserver.ThreadingTCPServer(("127.0.0.1", 8000), MyRequestHandler)server.serve_forever()  # 启动服务器

我们创建了一个ThreadingTCPServer服务器,然后在传入的处理类MyRequestHandler,并在handle方法中提供与客户端消息交互的业务逻辑,此处只是将客户端的消息返回客户端。最后我们在finish方法中关闭资源,finish方法使用了finally机制,保证了这些代码一定会执行。

上一篇使用socket实现了一个群聊服务器,这个里使用socketServer将更加方便的实现

class MyRequestHandle(BaseRequestHandler):clients = {} # 在类属性中记录所有与客户端连接socket。lock = threading.Lock() # 互斥锁,各个线程共用def setup(self): # 新的用户连接时,预处理,将这个新的连接加入到clients中,考虑线程安全,需要加锁with self.lock:self.clients[self.client_address] = self.requestdef handle(self): # 处理客户端的请求主逻辑while True:data = self.request.recv(1024).strip()  # 接受数据if data == b"quit" or data == b"": # 客户端退出with self.lock:self.server.clients.pop(self.client_address)self.request.close()breakprint("{}-{}: {}".format(*self.client_address, data.decode()))with self.lock:for _, c in self.server.clients.items(): # 群发c.send(data)def finish(self):with server.lock:for _, c in server.clients.items():c.close()server.server_close()def main():server = ThreadingTCPServer(("127.0.0.1", 8000), MyRequestHandle)# 将创建的所有线程设置为daemon线程,这样控台主程序退出时,这个服务器的所有线程将会被结束server.daemon_threads = True if __name__ == "__main__":main()

上面requestHandlerclass中的handle方法和finish方式对应了上一篇中TCP服务器的recv方法和stop方法,他们处理请求的逻辑是相同的。只是上面使用了socketserver的代码变少了,处理的逻辑也变少了,TCPserver帮我们完成了大量的工作,这利于软件的快速开发。

内置的两个RequestHandlerClass

StreamHandlerRequest

StreamHandlerRequest顾名思义是一种流式的求情处理类,对应TCP协议的面向字节流的传输形式。我们从源代码分析。(去除了一些次要代码)

class StreamRequestHandler(BaseRequestHandler):rbufsize = -1 # 读缓存wbufsize = 0  # 写缓存timeout = None # 超时时间# IP/TCP拥塞控制的Nagle算法算法。disable_nagle_algorithm = Falsedef setup(self): # 实现了setup,self.connection = self.requestif self.timeout is not None:self.connection.settimeout(self.timeout)if self.disable_nagle_algorithm:self.connection.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY, True)# 使用 makefile方法获得了一个只读文件对象 rfileself.rfile = self.connection.makefile('rb', self.rbufsize)# 获得一个只写的文件对象 wfileif self.wbufsize == 0:self.wfile = _SocketWriter(self.connection)else:self.wfile = self.connection.makefile('wb', self.wbufsize)def finish(self): # 负责将这个 wfile 和 rfile方法关闭。if not self.wfile.closed:try:self.wfile.flush()except socket.error:passself.wfile.close()self.rfile.close()

使用StreamRequestHandler方法可以将这个socket包装成一个类文件对象,方便我们使用一套文件对象的方法处理这个socket,它没有实现handle方法,我仍然需要我们实现。我们可以这样使用它

class MyHandle(StreamRequestHandler):# 如果需要使用setup和finish方法,需要调用父类方法,否则该方法将会被覆盖。def setup(self):super().setup()# 添加自己的需求def handle(self):# 这里我们可以使用wfile和rfile来处理socket消息了,例如之前使用self.request.recv()方法等同于self.rfile.read()# 而 self.wfile.write 等同于 self.request.send(),在handle方法中完成业务逻辑即可def finish(self):super().finish()server = ThreadingTCPServer("127.0.0.1", MyHandle)
server.serve_forever()

StreamRequestHandler主要定义了两个新的 wfile对象和rfile对象,来分别对这个socket进行读写操作,当我们业务需要时,比如需要使用文件接口方法时,选择继承于StreamRequestHandler构建我们自己处理请求类来完成业务逻辑将会更加的方便。

DatagramRequestHandler

DatagramRequestHandler字面意思是数据报请求处理,也就是基于UDPServer的服务器才能使用该请求处理类

class DatagramRequestHandler(BaseRequestHandler):def setup(self):from io import BytesIO# udp的self.request包含两部分(data,socket)它来自于# data, client_addr = self.socket.recvfrom(self.max_packet_size)#   return (data, self.socket), client_addr# (data, self.socket)就是这个self.request,在这里将其解构,data为recvfrom接收的数据self.packet, self.socket = self.request# 该数据包封装为 BytesIO,同样为一个类文件对象。self.rfile = BytesIO(self.packet)self.wfile = BytesIO()def finish(self):self.socket.sendto(self.wfile.getvalue(), self.client_address)

从源码可以看出,DatagramRequestHandler将数据包封装为一个rfile,并实例化一个ByteIO对象用于写入数据,写入的数据可以通过self.socket这个套接字发送。这样可以使用rfile和wfile这两个类文件对象的read或者write接口来进行一些IO方面的操作。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

Python使用socketServer包搭建简易服务器过程详解相关推荐

  1. Faster-RCNN.pytorch的搭建、使用过程详解(适配PyTorch 1.0以上版本)

    Faster-RCNN.pytorch的搭建.使用过程详解 引言 faster-rcnn pytorch代码下载 faster-rcnn pytorch配置过程 faster-rcnn pytorch ...

  2. python单例模式解析_Python下简易的单例模式详解

    Python 下的单例模式 要点: 1.某个类只能有一个实例: 2.它必须自行创建这个实例: 3.它必须自行向整个系统提供这个实例 方法:重写new函数 应该考虑的情况: 1.这个单例的类可能继承了别 ...

  3. linux apache 文件服务器,Linux下搭建Apache服务器全过程详解

    什么是Apache? Apache Licence是著名的非盈利开源组织Apache采用的协议.该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件) ...

  4. syslog 服务器过程详解

    syslog服务器可以用作一个网络中的日志监控中心,所有能够通过网络来发送日志的设施(包含了Linux或Windows服务器,路由器,交换 机以及其他主机)都可以把日志发送给它. 通过设置一个sysl ...

  5. java 详解 搭建 框架_在Eclipse中搭建Struts框架过程详解

    虽然用MyEclipse搭建Struts框架是更为便捷的方式,但是用Eclipse可以增强自己对Struts的理解.本文演示了使用Eclipse搭建Struts 1.2框架的过程.此项目实现了简单的功 ...

  6. HAproxy七层负载均衡——环境搭建及实现过程详解

    实验环境 主机名 IP 服务 虚拟机server1 172.25.6.1 haproxy,httpd,服务端 虚拟机server2 172.25.6.2 httpd,php,客户端 虚拟机server ...

  7. mysql服务器搭建方法_windows下搭建MySQL服务器步骤详解

    Mysql是一个数据库系统,它包括数据库服务器,并且有一个数据库管理系统对数据库服务器进行管理,同时还包括有一个数据库客户端,用于与用户交互. 从官方网站下载Mysql数据库系统的安装包程序,http ...

  8. Nginx搭建反向代理服务器过程详解

    一.反向代理:Web服务器的"经纪人" 1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网 ...

  9. windows server服务器下用Gogs搭建Git服务器教程详解

    一.准备条件 1.官网下载NSSM         http://www.nssm.cc/download NSSM介绍:NSSM是一个服务封装程序,它可以将普通exe程序封装成服务,使之像windo ...

最新文章

  1. exfat分配单元大小选多少_NTFS/exFAT/FAT32,这三个常见选项是什么意思?
  2. 返回局部变量或临时变量的地址_C++的函数不可以返回局部变量的指针
  3. 找出第i个小元素(算法导论第三版9.2-4题)
  4. gradle排除依赖_如何从Gradle中的所有依赖项中排除库
  5. 使用API​​密钥(aka身份验证令牌)部署到Maven Central
  6. php什么设置前端代码,代码编辑器与PHPSTUDY的安装与配置过程(前端第一课)
  7. CPU VS GPU笔记
  8. 学游戏3D建模,选机构要注意哪些方面?
  9. 这个东西可以温暖你想打BUG的心......
  10. C++冒泡排序(正宗版)
  11. C语言实现部标JTT808
  12. XCode 动态库未签名问题的解决
  13. 我的世界基岩版python插件编写教程(pyr教程)
  14. php tp6 错误接管分析,终于成功使用whoops接管tp6的异常处理!
  15. python熊猫小课_Python-机器学习小项目
  16. 浪潮受邀出席“市长论坛-深圳智慧城市国际圆桌研讨会”
  17. opencv 照片动漫风格
  18. 联想-win7系统电脑开机提示Error 1962:No operating解决步骤
  19. linux教程:查看端口占用情况及开放关闭端口
  20. 集成了补丁的windows xp 操作系统

热门文章

  1. 【工具】sysbench 0.5 简介
  2. RabbitMq 3.0.1 技术预演资料
  3. MDOP 2011 R2 DaRT 7.0 创建包含诊断和恢复的图形化PE
  4. 【编程好习惯】避免使用魔数
  5. 容器编排技术 -- AWS EC2快速入门
  6. IntelliJ IDEA版本:Ultimate、Community、EAP版本的区别
  7. 如何在Ubuntu 18.04上创建多节点MySQL集群
  8. Android开发与Sequoyah的安装问题
  9. ‘packaging‘ with value ‘jar‘ is invalid. Aggregator projects require ‘pom‘ as packaging.
  10. Swagger3、SpringBoot学习、使用复盘