Python 创建TCP服务器的方法


Posted in Python onJuly 28, 2020

问题

你想实现一个服务器,通过TCP协议和客户端通信。

解决方案

创建一个TCP服务器的一个简单方法是使用 socketserver 库。例如,下面是一个简单的应答服务器:

from socketserver import BaseRequestHandler, TCPServer

class EchoHandler(BaseRequestHandler):
  def handle(self):
    print('Got connection from', self.client_address)
    while True:

      msg = self.request.recv(8192)
      if not msg:
        break
      self.request.send(msg)

if __name__ == '__main__':
  serv = TCPServer(('', 20000), EchoHandler)
  serv.serve_forever()

在这段代码中,你定义了一个特殊的处理类,实现了一个 handle() 方法,用来为客户端连接服务。 request 属性是客户端socket,client_address 有客户端地址。 为了测试这个服务器,运行它并打开另外一个Python进程连接这个服务器:

>>> from socket import socket, AF_INET, SOCK_STREAM
>>> s = socket(AF_INET, SOCK_STREAM)
>>> s.connect(('localhost', 20000))
>>> s.send(b'Hello')
5
>>> s.recv(8192)
b'Hello'
>>>

很多时候,可以很容易的定义一个不同的处理器。下面是一个使用 StreamRequestHandler 基类将一个类文件接口放置在底层socket上的例子:

from socketserver import StreamRequestHandler, TCPServer

class EchoHandler(StreamRequestHandler):
  def handle(self):
    print('Got connection from', self.client_address)
    # self.rfile is a file-like object for reading
    for line in self.rfile:
      # self.wfile is a file-like object for writing
      self.wfile.write(line)

if __name__ == '__main__':
  serv = TCPServer(('', 20000), EchoHandler)
  serv.serve_forever()

讨论

socketserver 可以让我们很容易的创建简单的TCP服务器。 但是,你需要注意的是,默认情况下这种服务器是单线程的,一次只能为一个客户端连接服务。 如果你想处理多个客户端,可以初始化一个 ForkingTCPServer 或者是 ThreadingTCPServer 对象。例如:

from socketserver import ThreadingTCPServer


if __name__ == '__main__':
  serv = ThreadingTCPServer(('', 20000), EchoHandler)
  serv.serve_forever()

使用fork或线程服务器有个潜在问题就是它们会为每个客户端连接创建一个新的进程或线程。 由于客户端连接数是没有限制的,因此一个恶意的黑客可以同时发送大量的连接让你的服务器奔溃。

如果你担心这个问题,你可以创建一个预先分配大小的工作线程池或进程池。 你先创建一个普通的非线程服务器,然后在一个线程池中使用 serve_forever() 方法来启动它们。

if __name__ == '__main__':
  from threading import Thread
  NWORKERS = 16
  serv = TCPServer(('', 20000), EchoHandler)
  for n in range(NWORKERS):
    t = Thread(target=serv.serve_forever)
    t.daemon = True
    t.start()
  serv.serve_forever()

一般来讲,一个 TCPServer 在实例化的时候会绑定并激活相应的 socket 。 不过,有时候你想通过设置某些选项去调整底下的 socket` ,可以设置参数 bind_and_activate=False 。如下:

if __name__ == '__main__':
  serv = TCPServer(('', 20000), EchoHandler, bind_and_activate=False)
  # Set up various socket options
  serv.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
  # Bind and activate
  serv.server_bind()
  serv.server_activate()
  serv.serve_forever()

上面的 socket 选项是一个非常普遍的配置项,它允许服务器重新绑定一个之前使用过的端口号。 由于要被经常使用到,它被放置到类变量中,可以直接在 TCPServer 上面设置。 在实例化服务器的时候去设置它的值,如下所示:

if __name__ == '__main__':
  TCPServer.allow_reuse_address = True
  serv = TCPServer(('', 20000), EchoHandler)
  serv.serve_forever()

在上面示例中,我们演示了两种不同的处理器基类( BaseRequestHandler 和 StreamRequestHandler )。 StreamRequestHandler 更加灵活点,能通过设置其他的类变量来支持一些新的特性。比如:

import socket

class EchoHandler(StreamRequestHandler):
  # Optional settings (defaults shown)
  timeout = 5           # Timeout on all socket operations
  rbufsize = -1          # Read buffer size
  wbufsize = 0           # Write buffer size
  disable_nagle_algorithm = False # Sets TCP_NODELAY socket option
  def handle(self):
    print('Got connection from', self.client_address)
    try:
      for line in self.rfile:
        # self.wfile is a file-like object for writing
        self.wfile.write(line)
    except socket.timeout:
      print('Timed out!')

最后,还需要注意的是绝大部分Python的高层网络模块(比如HTTP、XML-RPC等)都是建立在 socketserver 功能之上。 也就是说,直接使用 socket 库来实现服务器也并不是很难。 下面是一个使用 socket 直接编程实现的一个服务器简单例子:

from socket import socket, AF_INET, SOCK_STREAM

def echo_handler(address, client_sock):
  print('Got connection from {}'.format(address))
  while True:
    msg = client_sock.recv(8192)
    if not msg:
      break
    client_sock.sendall(msg)
  client_sock.close()

def echo_server(address, backlog=5):
  sock = socket(AF_INET, SOCK_STREAM)
  sock.bind(address)
  sock.listen(backlog)
  while True:
    client_sock, client_addr = sock.accept()
    echo_handler(client_addr, client_sock)

if __name__ == '__main__':
  echo_server(('', 20000))

以上就是Python 创建TCP服务器的方法的详细内容,更多关于Python 创建TCP服务器的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python实现根据图标提取分类应用程序实例
Sep 28 Python
python判断字符串是否包含子字符串的方法
Mar 24 Python
Python实现简易端口扫描器代码实例
Mar 15 Python
Python使用win32com实现的模拟浏览器功能示例
Jul 13 Python
python中reload(module)的用法示例详解
Sep 15 Python
python re模块的高级用法详解
Jun 06 Python
PythonWeb项目Django部署在Ubuntu18.04腾讯云主机上
Apr 01 Python
Python编写带选项的命令行程序方法
Aug 13 Python
PyInstaller运行原理及常用操作详解
Jun 13 Python
用Python制作mini翻译器的实现示例
Aug 17 Python
Python日志打印里logging.getLogger源码分析详解
Jan 17 Python
Python 数据可视化神器Pyecharts绘制图像练习
Feb 28 Python
Python实现画图软件功能方法详解
Jul 28 #Python
Python绘图之柱形图绘制详解
Jul 28 #Python
Python如何定义接口和抽象类
Jul 28 #Python
Python爬虫之爬取淘女郎照片示例详解
Jul 28 #Python
Python selenium键盘鼠标事件实现过程详解
Jul 28 #Python
用python写爬虫简单吗
Jul 28 #Python
公认8个效率最高的爬虫框架
Jul 28 #Python
You might like
php win下Socket方式发邮件类
2009/08/21 PHP
php学习笔记之面向对象编程
2012/12/29 PHP
简单谈谈PHP中strlen 函数
2016/02/27 PHP
实现PHP框架系列文章(6)mysql数据库方法
2016/03/04 PHP
ThinkPHP5框架缓存查询操作分析
2018/05/30 PHP
phpstorm最新激活码分享亲测phpstorm2020.2.3版可用
2020/11/22 PHP
Javascript中的相等与不等运算
2010/04/25 Javascript
关于event.cancelBubble和event.stopPropagation()的区别介绍
2011/12/11 Javascript
用js代码和插件实现wordpress雪花飘落效果的四种方法
2014/12/15 Javascript
js简单时间比较的方法
2016/08/02 Javascript
jQuery3.0中的buildFragment私有函数详解
2016/08/16 Javascript
深入理解jQuery3.0的domManip函数
2016/09/01 Javascript
微信小程序 教程之注册页面
2016/10/17 Javascript
简单实现JavaScript图片切换效果
2016/11/28 Javascript
jQuery Easy UI中根据第一个下拉框选中的值设置第二个下拉框是否可以编辑
2016/11/29 Javascript
JavaScript使用ZeroClipboard操作剪切板
2017/05/10 Javascript
Centos6.8下Node.js安装教程
2017/05/12 Javascript
EasyUI Tree树组件无限循环的解决方法
2017/09/27 Javascript
JavaScript 判断对象中是否有某属性的常用方法
2018/06/14 Javascript
python和bash统计CPU利用率的方法
2015/07/10 Python
浅谈机器学习需要的了解的十大算法
2017/12/15 Python
python3.6+django2.0开发一套学员管理系统
2018/03/03 Python
使用memory_profiler监测python代码运行时内存消耗方法
2018/12/03 Python
详解Python静态网页爬取获取高清壁纸
2019/04/23 Python
python3下载抖音视频的完整代码
2019/06/05 Python
django框架自定义模板标签(template tag)操作示例
2019/06/24 Python
利用python求积分的实例
2019/07/03 Python
基于Python获取照片的GPS位置信息
2020/01/20 Python
python实现提取COCO,VOC数据集中特定的类
2020/03/10 Python
英国灯具和灯泡网上商店:Lights.co.uk
2018/02/02 全球购物
如何启动时不需输入用户名与密码
2014/05/09 面试题
在职研究生自我鉴定
2013/10/16 职场文书
2016春季运动会开幕词
2016/03/04 职场文书
基于Redis6.2.6版本部署Redis Cluster集群的问题
2022/04/01 Redis
Java 关于String字符串原理上的问题
2022/04/07 Java/Android
springboot读取resources下文件的方式详解
2022/06/21 Java/Android