select版-TCP服务器

1. select 原理

在多路复用的模型中,比较常用的有select模型和epoll模型。这两个都是系统接口,由操作系统提供。当然,Python的select模块进行了更高级的封装。

网络通信被Unix系统抽象为文件的读写,通常是一个设备,由设备驱动程序提供,驱动可以知道自身的数据是否可用。支持阻塞操作的设备驱动通常会实现一组自身的等待队列,如读/写等待队列用于支持上层(用户层)所需的block或non-block操作。设备的文件的资源如果可用(可读或者可写)则会通知进程,反之则会让进程睡眠,等到数据到来可用的时候,再唤醒进程。

这些设备的文件描述符被放在一个数组中,然后select调用的时候遍历这个数组,如果对于的文件描述符可读则会返回改文件描述符。当遍历结束之后,如果仍然没有一个可用设备文件描述符,select让用户进程则会睡眠,直到等待资源可用的时候在唤醒,遍历之前那个监视的数组。每次遍历都是依次进行判断的。

2. select 回显服务器

使用python的select模块很容易写出下面一个echo(回显)服务器:

import select
import socket
import sys server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('', 7788))
server.listen(5) inputs = [server, sys.stdin] running = True while True: # 调用 select 函数,阻塞等待
readable, writeable, exceptional = select.select(inputs, [], []) # 数据抵达,循环
for sock in readable: # 监听到有新的连接
if sock == server:
conn, addr = server.accept()
# select 监听的socket
inputs.append(conn) # 监听到键盘有输入
elif sock == sys.stdin:
cmd = sys.stdin.readline()
running = False
break # 有数据到达
else:
# 读取客户端连接发送的数据
data = sock.recv(1024)
if data:
sock.send(data)
else:
# 移除select监听的socket
inputs.remove(sock)
sock.close() # 如果检测到用户输入敲击键盘,那么就退出
if not running:
break server.close()

另外一个服务器(包含writeList):

#coding=utf-8
import socket
import Queue
from select import select SERVER_IP = ('', 9999) # 保存客户端发送过来的消息,将消息放入队列中
message_queue = {}
input_list = []
output_list = [] if __name__ == "__main__":
server = socket.socket()
server.bind(SERVER_IP)
server.listen(10)
# 设置为非阻塞
server.setblocking(False) # 初始化将服务端加入监听列表
input_list.append(server) while True:
# 开始 select 监听,对input_list中的服务端server进行监听
stdinput, stdoutput, stderr = select(input_list, output_list, input_list) # 循环判断是否有客户端连接进来,当有客户端连接进来时select将触发
for obj in stdinput:
# 判断当前触发的是不是服务端对象, 当触发的对象是服务端对象时,说明有新客户端连接进来了
if obj == server:
# 接收客户端的连接, 获取客户端对象和客户端地址信息
conn, addr = server.accept()
print("Client %s connected! "%str(addr))
# 将客户端对象也加入到监听的列表中, 当客户端发送消息时 select 将触发
input_list.append(conn)
# 为连接的客户端单独创建一个消息队列,用来保存客户端发送的消息
message_queue[conn] = Queue.Queue() else:
# 由于客户端连接进来时服务端接收客户端连接请求,将客户端加入到了监听列表中(input_list),客户端发送消息将触发
# 所以判断是否是客户端对象触发
try:
recv_data = obj.recv(1024)
# 客户端未断开
if recv_data:
print("received %s from client %s"%(recv_data, str(addr)))
# 将收到的消息放入到各客户端的消息队列中
message_queue[obj].put(recv_data) # 将回复操作放到output列表中,让select监听
if obj not in output_list:
output_list.append(obj) except ConnectionResetError:
# 客户端断开连接了,将客户端的监听从input列表中移除
input_list.remove(obj)
# 移除客户端对象的消息队列
del message_queue[obj]
print("\n[input] Client %s disconnected"%str(addr)) # 如果现在没有客户端请求,也没有客户端发送消息时,开始对发送消息列表进行处理,是否需要发送消息
for sendobj in output_list:
try:
# 如果消息队列中有消息,从消息队列中获取要发送的消息
if not message_queue[sendobj].empty():
# 从该客户端对象的消息队列中获取要发送的消息
send_data = message_queue[sendobj].get()
sendobj.send(send_data)
else:
# 将监听移除等待下一次客户端发送消息
output_list.remove(sendobj) except ConnectionResetError:
# 客户端连接断开了
del message_queue[sendobj]
output_list.remove(sendobj)
print("\n[output] Client %s disconnected"%str(addr))

总结

优点

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。

缺点

select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。

一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.

对socket进行扫描时是依次扫描的,即采用轮询的方法,效率较低。

当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。

python网络编程(十)的更多相关文章

  1. Python 网络编程(二)

    Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单 ...

  2. Python 网络编程(一)

    Python 网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. ...

  3. Python学习(22)python网络编程

    Python 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的 ...

  4. Day07 - Python 网络编程 Socket

    1. Python 网络编程 Python 提供了两个级别访问网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...

  5. python网络编程-01

    python网络编程 1.socket模块介绍 ①在网络编程中的一个基本组件就是套接字(socket),socket是两个程序之间的“信息通道”. ②套接字包括两个部分:服务器套接字.客户机套接字 ③ ...

  6. 《Python网络编程》学习笔记--使用谷歌地理编码API获取一个JSON文档

    Foundations of Python Network Programing,Third Edition <python网络编程>,本书中的代码可在Github上搜索fopnp下载 本 ...

  7. Python网络编程基础pdf

    Python网络编程基础(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1VGwGtMSZbE0bSZe-MBl6qA 提取码:mert 复制这段内容后打开百度网盘手 ...

  8. python 网络编程(Socket)

    # from wsgiref.simple_server import make_server## def RunServer(environ,start_response):# start_resp ...

  9. python 网络编程 IO多路复用之epoll

    python网络编程——IO多路复用之epoll 1.内核EPOLL模型讲解     此部分参考http://blog.csdn.net/mango_song/article/details/4264 ...

  10. 自学Python之路-Python网络编程

    自学Python之路-Python网络编程 自学Python之路[第一回]:1.11.2 1.3

随机推荐

  1. 泛微云桥e-birdge之金蝶云之家集成配置手册

    最近在配置金蝶云之家与泛微ecology的打通,没有看官方文档,直接配置,一直配置不通,看了官方文档后知道了少配置参数了,转载一下,省得四处去找了. 泛微云桥e-birdge之金蝶云之家集成配置手册 ...

  2. 关于The specified Android SDK Build Tools version (26.0.2) is ignored, as it is below the minimum...

    今天将项目迁移到另一台笔记本,进行build出现以下问题,导致build失败 The specified Android SDK Build Tools version (26.0.2) is ign ...

  3. sql select中加入常量列

    string sql="select a,b,'常量' as c from table" 注:单引号' ' 很重要,否则编译时会把其看成查询参数,从而提示参数未指定错误

  4. php7安装参数编译

    系统:Centos6.8 软件包:php-7.0.14.tar.gz yum install bzip2 bzip2-devel -y yum install curl curl-devel -y y ...

  5. libssl.so.10: cannot open shared object file: No such file or directory

    今天在使用yum安装东西的时候报错 yum list There was a problem importing one of the Python modulesrequired to run yu ...

  6. Python学习(十) —— 常用模块

    一.collections模块 在内置数据类型(dict.list.set.tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter.deque.defaultdic ...

  7. PHP算法排序之快速排序、冒泡排序、选择排序、插入排序性能对比

    <?php //冒泡排序 //原理:从倒数第一个数开始,相邻的两个数比较,后面比前面的小,则交换位置,一直到比较第一个数之后则最小的会排在第一位,以此类推 function bubble_sor ...

  8. HDU4622 Reincarnation 字符串 SAM

    原文链接https://www.cnblogs.com/zhouzhendong/p/HDU4622.html 题目传送门 - HDU4622 题意 多组数据. 对于每一组数据,给定一个字符串 s , ...

  9. Substrings kmp

    Problem Description You are given a number of case-sensitive strings of alphabetic characters, find ...

  10. FileZilla FTP Client

    FileZilla Client是一个快速.实用.多功能和界面直观的免费的FTP客户端,虽然它是免费软件,可功能却一点也不含糊,比起那些共享软件来有过之而无不及,在新的版本中作者改进了手动下载的界面和 ...