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

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

server类

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

  1. +------------+
  2. | BaseServer |
  3. +------------+
  4. |
  5. v
  6. +-----------+ +------------------+
  7. | TCPServer |------->| UnixStreamServer |
  8. +-----------+ +------------------+
  9. |
  10. v
  11. +-----------+ +--------------------+
  12. | UDPServer |------->| UnixDatagramServer |
  13. +-----------+ +--------------------+

两个Mixin类

  1. +--------------+ +----------------+
  2. | ForkingMixIn | | ThreadingMixIn |
  3. +--------------+ +----------------+

各自实现了多进程和多线程的功能(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)处理。

  1. import socketserver
  2. # 创建一个基于TCP的server对象,并使用BaseRequestHandler处理客户端发送的消息
  3. server = socketserver.ThreadingTCPServer(("127.0.0.1", 8000), BaseRequestHandler)
  4. 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的源码

  1. class BaseRequestHandler:
  2.  
  3. def __init__(self, request, client_address, server):
  4. self.request = request
  5. self.client_address = client_address
  6. self.server = server
  7. self.setup()
  8. try:
  9. self.handle()
  10. finally:
  11. self.finish()
  12. def setup(self):
  13. pass
  14.  
  15. def handle(self):
  16. pass
  17.  
  18. def finish(self):
  19. pass

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

简单示例

  1. import socketserver
  2. import threading
  3.  
  4. class MyRequestHandler(socketserver.BaseRequestHandler):
  5. """ BaseRequestHandler的实例化方法中,获得了三个属性
  6. self.request = request # 该线程中与客户端交互的 socket 对象。
  7. self.client_address # 该线程处理的客户端地址
  8. self.server = server # 服务器对象
  9. """
  10.  
  11. def handle(self):
  12. while True:
  13. msg = self.request.recv() # 接受客户端的数据
  14. if msg == b"quit" or msg == "": # 退出
  15. break
  16.  
  17. print(msg.decode())
  18. self.request.send(msg) # 将消息发送回客户端
  19. def finish(self):
  20. self.request.close() # 关闭套接字
  21. if __name__ == "__main__":
  22. # 创建一个基于TCP的server对象,并使用BaseRequestHandler处理客户端发送的消息
  23. server = socketserver.ThreadingTCPServer(("127.0.0.1", 8000), MyRequestHandler)
  24.  
  25. server.serve_forever() # 启动服务器

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

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

  1. class MyRequestHandle(BaseRequestHandler):
  2. clients = {} # 在类属性中记录所有与客户端连接socket。
  3. lock = threading.Lock() # 互斥锁,各个线程共用
  4.  
  5. def setup(self): # 新的用户连接时,预处理,将这个新的连接加入到clients中,考虑线程安全,需要加锁
  6. with self.lock:
  7. self.clients[self.client_address] = self.request
  8.  
  9. def handle(self): # 处理客户端的请求主逻辑
  10. while True:
  11. data = self.request.recv(1024).strip() # 接受数据
  12.  
  13. if data == b"quit" or data == b"": # 客户端退出
  14. with self.lock:
  15. self.server.clients.pop(self.client_address)
  16. self.request.close()
  17. break
  18.  
  19. print("{}-{}: {}".format(*self.client_address, data.decode()))
  20.  
  21. with self.lock:
  22. for _, c in self.server.clients.items(): # 群发
  23. c.send(data)
  24. def finish(self):
  25. with server.lock:
  26. for _, c in server.clients.items():
  27. c.close()
  28. server.server_close()def main():
  29. server = ThreadingTCPServer(("127.0.0.1", 8000), MyRequestHandle)
  30. # 将创建的所有线程设置为daemon线程,这样控台主程序退出时,这个服务器的所有线程将会被结束
  31. server.daemon_threads = True
  32.  
  33. if __name__ == "__main__":
  34. main()

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

内置的两个RequestHandlerClass

StreamHandlerRequest

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

  1. class StreamRequestHandler(BaseRequestHandler):
  2. rbufsize = -1 # 读缓存
  3. wbufsize = 0 # 写缓存
  4. timeout = None # 超时时间
  5. # IP/TCP拥塞控制的Nagle算法算法。
  6. disable_nagle_algorithm = False
  7.  
  8. def setup(self): # 实现了setup,
  9. self.connection = self.request
  10. if self.timeout is not None:
  11. self.connection.settimeout(self.timeout)
  12. if self.disable_nagle_algorithm:
  13. self.connection.setsockopt(socket.IPPROTO_TCP,
  14. socket.TCP_NODELAY, True)
  15.  
  16. # 使用 makefile方法获得了一个只读文件对象 rfile
  17. self.rfile = self.connection.makefile('rb', self.rbufsize)
  18.  
  19. # 获得一个只写的文件对象 wfile
  20. if self.wbufsize == 0:
  21. self.wfile = _SocketWriter(self.connection)
  22. else:
  23. self.wfile = self.connection.makefile('wb', self.wbufsize)
  24.  
  25. def finish(self): # 负责将这个 wfile 和 rfile方法关闭。
  26. if not self.wfile.closed:
  27. try:
  28. self.wfile.flush()
  29. except socket.error:
  30. pass
  31. self.wfile.close()
  32. self.rfile.close()

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

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

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

DatagramRequestHandler

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

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

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

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

Python使用socketServer包搭建简易服务器过程详解的更多相关文章

  1. Linux中用postfix搭建邮件服务器实战详解

    Linux中用postfix搭建邮件服务器实战详解 postfix是Wietse Venema在IBM的GPL协议之下开发的MTA(邮件传输代理)软件.Postfix试图更快.更容易管理.更安全,同时 ...

  2. vue-cli3.0 脚手架搭建项目的过程详解

    1.安装vue-cli 3.0 ? 1 2 3 npm install -g @vue/cli # or yarn global add @vue/cli 安装成功后查看版本:vue -V(大写的V) ...

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

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

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

    1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet ...

  5. 【转】Nginx搭建反向代理服务器过程详解

    阅读目录 1.1 反向代理初印象 1.2 反向代理的作用 2.1 Nginx是神马? 2.2 Nginx的应用现状 2.3 Nginx的核心特点 3.1 准备一个ASP.NET网站部署到IIS服务器集 ...

  6. Nginx搭建反向代理服务器过程详解(转)

    一.反向代理 我们都知道,80端口是web服务的默认端口,其他主机访问web服务器也是默认和80端口进行web交互,而一台服务器也只有一个80端口,这是约定俗成的标准. 我们来看下面两个场景: 1.服 ...

  7. Citrix XenApp登录服务器过程详解

    详细流程: 1. 客户端上的receiver负责解析ICA文件,并根据ICA文件的内容发起连接请求.若是外网访问,则ICA文件中记录的是NetScaler的AG FQDN信息,连接请求发至NetSca ...

  8. python搭建简易服务器实例参考

    有关python搭建简易服务器的方法. 需求分析: 省油宝用户数 已经破了6000,原有的静态报表 已经变得臃肿不堪, 每次打开都要缓上半天,甚至浏览器直接挂掉 采用python搭建一个最最简易的 w ...

  9. [转]DNS服务器原理详解与Centos6.x下搭建DNS服务器

    转自:http://blog.it985.com/8958.html DNS 数据库的记录:正解,反解, Zone 的意义 通过DNS解析过程详解这篇文章,我们知道了要想访问www.zmit.cn,最 ...

随机推荐

  1. java 基本语法(十九)Optional类的使用

    java.util.Optional类1.理解:为了解决java中的空指针问题而生!Optional<T> 类(java.util.Optional) 是一个容器类,它可以保存类型T的值, ...

  2. Quartz.Net系列(十五):Quartz.Net四种修改配置的方式

    案例:修改默认线程个数 1.NameValueCollection System.Collections.Specialized.NameValueCollection collection = ne ...

  3. LeetCode 82,考察你的基本功,在有序链表中删除重复元素II

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题的第51篇文章,我们来看LeetCode第82题,删除有序链表中的重复元素II(Remove Duplicates ...

  4. CMMI规范目录结构

  5. C++中类继承public,protected和private关键字作用详解及派生类的访问权限

    注意:本文有时候会用Visual Studio Code里插件的自动补全功能来展示访问权限的范围(当且仅当自动补全范围等价于对象访问权限范围的时候),但是不代表只要是出现在自动补全范围内的可调用对象/ ...

  6. Cmd重定向

    1.执行单条cmd命令 public static string ExecuteCmd(string command) { Process p = new Process(); p.StartInfo ...

  7. linux中无法使用vim命令

    报:linux中  vim 不是内部指令! 解决: 1. rpm -qa | grep vim // 查看vim命令在什么软件包 出现 vim-minimal-7.4.160-4.el7.x86_64 ...

  8. 浅谈工业4.0背景下的空中数据端口,无人机3D 可视化系统的应用

    前言 近年来,无人机的发展越发迅速,既可民用于航拍,又可军用于侦察,涉及行业广泛,把无人机想象成一个“会飞的传感器”,无人机就成了工业4.0的一个空中数据端口,大至地球物理.气象.农业数据.小至个人位 ...

  9. Python协程之Gevent模块

    背景 进程是操作系统分配资源的最小单位,每个进程独享4G的内存地址空间,因此进程内数据是安全的,检查间的通信需要使用特定的方法.同理,正是因为进程是数据安全的,所以导致进程的切换是一个很麻烦效率不高的 ...

  10. Linux文件搜索

    一.whereis及which命令 这两个命令用来搜索命令的路径(也遵循/etc/updatedb.conf配置文件的筛选规则) whereis 命令名                        ...