热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python网络编程中socket、select和poll的简单使用

2019独角兽企业重金招聘Python工程师标准网络编程的基本组件是socket[1],包括:服务端socket和客户端socket。其中客户端s

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

网络编程的基本组件是socket[1],包括:服务端socket和客户端socket。其中客户端socket只是简单的连接、完成事务、断开连接,而服务端socket流程多一些。

一个小型服务器

v1-server

#!/usr/bin/env python
# coding=utf-8
import sockets=socket.socket()
host=socket.gethostname()
port=8889
s.bind((host,port))s.listen(10)
while True:conn,addr=s.accept()print "got conn from ",addrconn.send("thx for conn")conn.close()

服务端的accept方法会阻塞直到客户端连上。

一个小型客户机

v1-client

#!/usr/bin/env python
# coding=utf-8
import sockets=socket.socket()
host=socket.gethostname()
port=8889s.connect((host,port))
print s.recv(1024)
1 使用SocketServer框架的服务器

复杂一些的场景可以使用SocketServer模块。在其设计中,server每收到一个请求,就会实例化一个请求处理程序。

v2-SocketServer

# 和v1-client对应
from SocketServer import TCPServer,StreamRequestHandler
class Handler(StreamRequestHandler):def handle(self):addr=self.request.getpeername()print 'got connectino from ',addrself.wfile.write('thx for you connecting')server=TCPServer(('',8889),Handler)
server.serve_forever()
2 多个连接

上述两种方案server一次只能处理一个连接,

要想同时处理多个连接,有三种方法:

  • forking
  • threading
  • asnchronous I/O

其中threading方式下,每个socket交给一个线程处理,不同的socket通信过程互不干扰。

v3-forking.py

from SocketServer import TCPServer,StreamRequestHandler, ForkingMixInclass Server(ForkingMixIn, TCPServer): passclass Handler(StreamRequestHandler):def handle(self):addr=self.request.getpeername()print 'got connectino from ',addrself.wfile.write('thx for you connecting')server=Server(('',8889),Handler)
server.serve_forever()

v3-threading.py

from SocketServer import TCPServer,StreamRequestHandler, ThreadingMixInclass Server(ThreadingMixIn,TCPServer): passclass Handler(StreamRequestHandler):def handle(self):addr=self.request.getpeername()print 'got connectino from ',addrself.wfile.write('thx for you connecting')server=Server(('',8889),Handler)
server.serve_forever()

thread方式下,每次一个客户成功连接到服务器,服务器都会创建一个新线程,如果黑客控制许多client恶意连接服务器那很可能耗尽系统资源。怎么办呢?

[6]给出了一个简单的方案:线程池(pre-allocated pool)

from socketserver import StreamRequestHandler, TCPServerclass EchoHandler(StreamRequestHandler):def handle(self):print('Got connection from', self.client_address)# self.rfile is a file-like object for readingfor line in self.rfile:# self.wfile is a file-like object for writingself.wfile.write(line)'''old: spawn a new process or thread on each client connection
if __name__ == '__main__':serv = TCPServer(('', 20000), EchoHandler)serv.serve_forever()
'''if __name__ == '__main__':from threading import ThreadNWORKERS = 16serv = TCPServer(('', 20000), EchoHandler)for n in range(NWORKERS):t = Thread(target=serv.serve_forever)t.daemon = Truet.start()serv.serve_forever()

在上面的代码中,限定了线程数量为NWORKERS个。避免了前面的问题。

3 使用select的简单服务器

服务器:v4-select.py

#!/usr/bin/env python
# coding=utf-8
import socket, select
s=socket.socket()
host=socket.gethostname()
port=8889
s.bind((host,port))s.listen(5)
inputs=[s]
print 's',s
while True:rs,ws,es=select.select(inputs,[],[]) # read write except result#rs,ws,es=select.select(inputs,[],[],0) print 'inputs ',inputsfor r in rs:print 'rs ',len(rs),rsif r is s:c, addr = s.accept()print 'Got connection from',addrc.send('connect to server successful')inputs.append(c)print 'r,c ',r,celse:try:data = r.recv(1024)disconnected = not dataexcept:disconnected = Trueif disconnected:print r.getpeername(),'disconnected'inputs.remove(r)else:print data

客户端:v4-client.py

#!/usr/bin/env python
# coding=utf-8
import sockets=socket.socket()
host=socket.gethostname()
port=8889s.connect((host,port))
print socket.gethostname()
s.send("hi, i am "+socket.gethostname())
print s.recv(1024)

运行:

启动server

$ python v4-select.py
s
inputs []
rs 1 []
Got connection from ('127.0.0.1', 44464)
r,c
inputs [, ]
rs 1 []
hi, i am ubuntu
inputs [, ]
rs 1 []
('127.0.0.1', 44464) disconnected

启动client

$ python v4-client.py
ubuntu
connect to server successful

python中提供的select.select(rlist, wlist, xlist[, timeout])是Unix中select()系统调用的一个接口。[4]

rlist、wlist、xlist都是waitable对象序列,分别表示:输入、输出以及异常情况的序列。

'waitable objects’: either integers representing file descriptors or objects with a parameterless method named fileno() returning such an integer

rlist: wait until ready for reading
wlist: wait until ready for writing
xlist: wait for an “exceptional condition”

当不提供可选参数timeout的时候,select会阻塞,直到至少一个文件描述符为行动做好了准备,才会继续运行。

当给定timeout秒数,select阻塞时间不会超过timeout秒。如果超时仍然没有准备好的文件描述符就会返回空结果。

返回值是3个list组成的tuple,这3个list分别是3个输入list的子集。

当timeout为0,会给出一个连续的poll(不阻塞),设置为0后可以看到while True函数体疯狂循环。

[1]中有些话都是直接翻译文档的,看完跟不看一样,略坑。

从执行的输出可以看到:

server端的,inputs列表开始只包含socket对象4c

第一次循环中,select从inputs中找到准备好的子集:4c

4c调用accept成功之后生成了socket对象84,并且被inputs列表添加.

第二次循环中,select从inputs中找到准备好的子集:84

84接收消息

这只是一个最简单的演示,所以

[2]中的一些概念和[1]不同,这令人困惑。

[1]认为:

同步的服务器解决方案:server一次只能连接处理一个client

要实现多个连接,可以借助forking分出多个进程,并行处理多个socket连接。缺点是浪费资源。

或者用threading分多个线程,每个线程处理一个socket连接。缺点是需要进程同步。

异步IO可以避免上述问题,基本机制是select模块的select函数。

对应的[2]中认为:

同步/异步主要针对C端,阻塞/非阻塞主要针对S端。
同步IO和异步IO的区别就在于:数据访问的时候进程是否阻塞!
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回!Linux下的五种I/O模型
1)阻塞I/O(blocking I/O)
2)非阻塞I/O (nonblocking I/O)
3) I/O复用(select 和poll) (I/O multiplexing)
4)信号驱动I/O (signal driven I/O (SIGIO))
5)异步I/O (asynchronous I/O (the POSIX aio_functions))前四种都是同步,只有最后一种才是异步IO。

概念混乱不清,挖坑待辨析。

4 使用poll的简单服务器

v4-poll.py

#!/usr/bin/env python
# coding=utf-8
import socket, select
s=socket.socket()
host=socket.gethostname()
port=8889
s.bind((host,port))fdmap={s.fileno():s}
s.listen(5)
# 得到poll对象
p=select.poll()
p.register(s)while True:events=p.poll()for fd,event in events:if fd==s.fileno():c,addr = s.accept()print 'got connection from ',addrp.register(c)fdmap[c.fileno()] = celif event & select.POLLIN:data=fdmap[fd].recv(1024)if not data:print fdmap[fd].getpeername(),'disconnected'p.unregister(fd)del fdmap[fd]else: print data

4.1 select和poll的异同

[4]中提到:

The poll() system call, supported on most Unix systems, provides better scalability for network servers that service many, many clients at the same time. poll() scales better because the system call only requires listing the file descriptors of interest, while select() builds a bitmap, turns on bits for the fds of interest, and then afterward the whole bitmap has to be linearly scanned again. select() is O(highest file descriptor), while poll() is O(number of file descriptors).

区别在于poll() syscall的scalability更好。 poll()只维护感兴趣的fd列表,但是select() syscall通过bitmap来维护整个fd列表。poll()和select()的时间复杂度分别为O(fd数目)和O(最大fd数目).

[5]中认为:

select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组。当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。

poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。

??说好的scalability区别呢?

poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。相比select和poll有如下改进:

  1. 省掉文件描述符于用户态和内核的地址空间之间来回复制的开销
  2. 采用基于事件的就绪通知方式
References
  1. pyhton基础教程(第2版)
  2. socket阻塞与非阻塞,同步与异步、I/O模型
  3. https://docs.python.org/2/library/socket.html
  4. https://docs.python.org/2/library/select.html
  5. http://www.cnblogs.com/coser/archive/2012/01/06/2315216.html
  6. http://chimera.labs.oreilly.com/books/1230000000393/ch11.html#_discussion_1

转:https://my.oschina.net/SnifferApache/blog/776123



推荐阅读
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • Python实现变声器功能(萝莉音御姐音)的方法及步骤
    本文介绍了使用Python实现变声器功能(萝莉音御姐音)的方法及步骤。首先登录百度AL开发平台,选择语音合成,创建应用并填写应用信息,获取Appid、API Key和Secret Key。然后安装pythonsdk,可以通过pip install baidu-aip或python setup.py install进行安装。最后,书写代码实现变声器功能,使用AipSpeech库进行语音合成,可以设置音量等参数。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
author-avatar
易_拉罐
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有