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之复习if语句
Oct 02 Python
python实现爬虫统计学校BBS男女比例之数据处理(三)
Dec 31 Python
Python 的描述符 descriptor详解
Feb 27 Python
python二分查找算法的递归实现方法
May 12 Python
Python当中的array数组对象实例详解
Jun 12 Python
pyqt5 使用cv2 显示图片,摄像头的实例
Jun 27 Python
Django admin model 汉化显示文字的实现方法
Aug 12 Python
python使用 __init__初始化操作简单示例
Sep 26 Python
基于python3生成标签云代码解析
Feb 18 Python
浅析Python中字符串的intern机制
Oct 03 Python
django中ImageField的使用详解
Dec 21 Python
python 列表推导和生成器表达式的使用
Feb 01 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
全国FM电台频率大全 - 31 新疆维吾尔族自治区
2020/03/11 无线电
PHP 采集程序 常用函数
2008/12/18 PHP
Jquery:ajax实现翻页无刷新功能代码
2013/08/05 Javascript
javascript实现动态侧边栏代码
2014/02/19 Javascript
动态添加删除表格行的js实现代码
2014/02/28 Javascript
AngularJS iframe跨域打开内容时报错误的解决办法
2015/01/26 Javascript
Javascript实现商品秒杀倒计时(时间与服务器时间同步)
2015/09/16 Javascript
浅析$(function) ready和onload 的区别
2016/09/03 Javascript
网页瀑布流布局jQuery实现代码
2016/10/21 Javascript
Jquery根据浏览器窗口改变调整大小的方法
2017/02/07 Javascript
JS基于正则表达式实现的密码强度验证功能示例
2017/09/21 Javascript
JavaScript实现全选取消效果
2017/12/14 Javascript
解决ant Design Search无法输入内容的问题
2020/10/29 Javascript
[06:16]DOTA2守卫传承者——职业选手谈心路历程
2015/02/26 DOTA
python采用django框架实现支付宝即时到帐接口
2016/05/17 Python
深入理解Python变量与常量
2016/06/02 Python
使用Python3 编写简单信用卡管理程序
2016/12/21 Python
python-itchat 获取微信群用户信息的实例
2019/02/21 Python
Python 多线程搜索txt文件的内容,并写入搜到的内容(Lock)方法
2019/08/23 Python
Python threading的使用方法解析
2019/08/28 Python
Python求解正态分布置信区间教程
2019/11/20 Python
CSS3媒体查询(Media Queries)介绍
2013/09/12 HTML / CSS
幼师专业求职推荐信
2013/11/08 职场文书
学术会议邀请函范文
2014/01/22 职场文书
劳动竞赛活动方案
2014/02/20 职场文书
境外导游求职信
2014/02/27 职场文书
停车位租赁协议书
2014/09/24 职场文书
办护照工作证明
2014/10/01 职场文书
2014年勤工助学工作总结
2014/11/24 职场文书
工作试用期自我评价
2015/03/10 职场文书
初一军训感言
2015/08/01 职场文书
2016年端午节寄语
2015/12/04 职场文书
Redisson实现Redis分布式锁的几种方式
2021/08/07 Redis
VUE使用draggable实现组件拖拽
2022/04/06 Vue.js
Github 使用python对copilot做些简单使用测试
2022/04/14 Python
oracle设置密码复杂度及设置超时退出的功能
2022/06/28 Oracle