由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 :  --> 点击这里

可以看我的上篇文章 《python 简单搭建阻塞式单进程,多进程,多线程服务》

1 单进程服务器 - 非堵塞模式

服务端 :

#coding=utf-
from socket import *
import time #用来存储所有的新连接的socket,这个是重点
g_socketList = [] def main():
serSocket = socket(AF_INET, SOCK_STREAM)
serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , )
localAddr = ('', )
serSocket.bind(localAddr)
#这里可以适当修改listen的值来看看不同的现象
serSocket.listen()
#.将套接字设置为非堵塞
#.这个非常重要,设置为非堵塞后,如果accept时,没有客户端连接,那么会产生一个异常,
#.所以我们用try来处理这个函数
serSocket.setblocking(False) while True:
try:
newClientInfo = serSocket.accept()
except Exception as result:
pass
else:
print("..新客户端连接了.:%s"%str(newClientInfo))
#这里依旧设置为非堵塞
newClientInfo[].setblocking(False)
#这里重点加入列表
g_socketList.append(newClientInfo) # 来存储需要删除的客户端信息
needDelClientInfoList = [] #这里循环读取socket列表,逐一去请求是否有客户端连接,这是select版本的简化版
#但是是理解select版本的核心
for clientSocket,clientAddr in g_socketList:
try:
recvData = clientSocket.recv()
if len(recvData)>:
print('recv[%s]:%s'%(str(clientAddr), recvData))
else:
print('[%s]客户端已经关闭'%str(clientAddr))
clientSocket.close()
g_needDelClientInfoList.append((clientSocket,clientAddr))
except Exception as result:
pass for needDelClientInfo in needDelClientInfoList:
g_socketList.remove(needDelClientInfo) if __name__ == '__main__':
main()

客户端:

#coding=utf-
from socket import *
import random
import time serverIp = raw_input("请输.服务器的ip:")
connNum = raw_input("请输.要链接服务器的次数(例如1000):")
g_socketList = [] for i in range(int(connNum)):
s = socket(AF_INET, SOCK_STREAM)
s.connect((serverIp, ))
g_socketList.append(s)
print(i)
while True:
for s in g_socketList:
s.send(str(random.randint(,)))
# .....
time.sleep()

我们可以看到,关键点在于for循环每个保存下来的套接字,循环请求是否有数据发来,由于速度很快,导致看起来像是多进程处理一样。

这种模式缺点在于当连接越来越多的时候,for循环轮询花费的时间越来越久,当有上千个连接,实际上却只有一个客户端有数据发来的时候,服务端依旧是不停歇的一个个轮询去问,去查找是否有请求。于是有了改进版的selec模型的服务:

2 单进程select版TCP服务器

在多路复用的模型中,较常见的有select模型和epoll模型。这两个都是系统接口,由操作系统提供。当然,Python的select模块进行了更高级的封装。网络通信被Unix系统抽象为文件件的读写,通常是一个个设备,由设备驱动程序提供,驱动可以知道自身的数据是否可用。支持阻塞操作的设备驱动通常会实现一组自身的等待队列,如读/写等待队列用于支持上层(用户层)所需的block或non-block操作。设备的文件的资源如果可用(可读或者可写)则会通知进程,反之则会让进程睡眠,等到数据到来可用的时候,再唤醒进程。这些设备的文件描述符被放在一个数组中,然后select调用的时候遍历这个数组,如果对于的文件描述符可读则会返回该文件描述符。当遍历结束之后,如果仍然没有这个可用设备文件描述符,select让用户进程则会睡眠,直到等待资源可用的时候在唤醒,遍历之前那个监视的数组。每次遍历都是依次进行判断的。

import select
import socket
import sys server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('', ))
server.listen() 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()
if data:
sock.send(data)
else:
# 移除select监听的socket
inputs.remove(sock)
sock.close() # 如果检测到用户输入敲击键盘,那么就退出
if not running:
break server.close()

select的缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义升职重新编译内核的方式提升这个限制,但是这样也会造成效率的降低。 一般来说这个数和系统内存关系很大,具体数量可以cat /proc/sys/fs/file-max 查看。32位机默认是1024个。64位机默认是2048.对socket进行扫描时是依次扫描的,即采用轮询的⽅法,效率较低。当套接字较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都要遍历。这会浪费很多CPU时间。

3 epoll 版的服务端

import socket
import select # 创建套接字
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 设置可以重复使.绑定的信息
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,) # 绑定本机信息
s.bind(("",)) # 变为被动
s.listen() # 创建.个epoll对象
epoll=select.epoll() # 将创建的套接字添加到epoll的事件监听中
epoll.register(s.fileno(),select.EPOLLIN|select.EPOLLET) connections = {}
addresses = {} # 循环等待客户端的到来或者对.发送数据
while True:
# epoll 进. fd 扫描的地. -- 未指定超时时间则为阻塞等待
epoll_list=epoll.poll()
# 对事件进.判断
for fd,events in epoll_list:
# 如果是socket创建的套接字被激活
if fd == s.fileno():
conn,addr=s.accept()
print('有新的客户端到来%s'%str(addr)) # 将 conn 和 addr 信息分别保存起来
connections[conn.fileno()] = conn
addresses[conn.fileno()] = addr # 向 epoll 中注册 连接 socket 的 可读 事件
epoll.register(conn.fileno(), select.EPOLLIN | select elif events == select.EPOLLIN:
recvData = connections[fd].recv()
if len(recvData)>:
print('recv:%s'%recvData)
else:
# 从 epoll 中移除该 连接 fd
epoll.unregister(fd) # server 侧主动关闭该 连接 fd
connections[fd].close() print("%s---offline---"%str(addresses[fd]))

epoll的好处在于:

1. 没有最大并发连接的限制,能打开的FD(指的是文件件描述符,通俗的理解就是套接字对应的数字编号)的上限远大于1024
2. 效率提升,不是轮询的方式,不会随着FD数.的增加效率下降。只有活跃可用的FD才会调用callback函数;即epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远大于select和poll。

也就是说epoll是一种事件机制,不再是每次都从头开始轮询,一个个去询问是否有客户端发送数据,而是 "问" 客户端,有谁有数据吗? 这是活跃的客户端就会 “举手” ,那么epoll这个函数就会返回活跃的socket链接。

python 简单搭建非阻塞式单进程,select模式,epoll模式服务的更多相关文章

  1. python 简单搭建阻塞式单进程,多进程,多线程服务

    由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 :  --> 点击这里 我们可以通过这样子的方式去理解apache的工作原理 1 单进程TCP服 ...

  2. Python异步非阻塞IO多路复用Select/Poll/Epoll使用,线程,进程,协程

    1.使用select模拟socketserver伪并发处理客户端请求,代码如下: import socket import select sk = socket.socket() sk.bind((' ...

  3. 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

    下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...

  4. Java IO(3)非阻塞式输入输出(NIO)

    在上篇<Java IO(2)阻塞式输入输出(BIO)>的末尾谈到了什么是阻塞式输入输出,通过Socket编程对其有了大致了解.现在再重新回顾梳理一下,对于只有一个“客户端”和一个“服务器端 ...

  5. (转)非阻塞Connect对于select时应注意问题

    对于面向连接的socket类型(SOCK_STREAM,SOCK_SEQPACKET)在读写数据之前必须建立连接,首先服务器端socket必须在一个客户端知道的地址进行监听,也就是创建socket之后 ...

  6. 为什么IO多路复用需要采用非阻塞式IO

    近段时间开始学习<Unix网络编程>,代码实现了一个简单的IO多路复用+阻塞式的服务端,在学习了非阻塞式IO后,有一个疑问,即: 假如调用了select,并且关注了几个描述字,当关注的描述 ...

  7. 非阻塞式I/O

    套接字的默认状态是阻塞的.这就意味着当发出一个不能立即完成的套接字调用时,其进程将被投入睡眠,等待相应的操作完成.可能阻塞的套接字调用可分为以下4类 (1)输入操作,包括read,readv,recv ...

  8. 基于NIO写的阻塞式和非阻塞式的客户端服务端

    由于功能太过简单,就不过多阐述了,直接上阻塞式代码: package com.lql.nio; import org.junit.Test; import java.io.IOException; i ...

  9. 阻塞式I/0 和 非阻塞式I/O 同步异步详细介绍

    请求描述: `阻塞/非阻塞` 和 `同步/异步` 不是一个概念.举几个简单的例子. 当进程调用一个进行IO操作的API时(比如read函数),在数据没有到达前,read 会挂起,进程会卡住.在数据读取 ...

随机推荐

  1. 启动apache时,出现httpd: Could not reliably determine the server\'s fully qualified domain name, using 127.0.0.1 for ServerName

    1.通过vi打开apache的配置文件httpd.conf > vi /data/apache/conf/httpd.conf 2.找到#ServerName www.example.com:8 ...

  2. 【转】HttpRuntime的认识与加深理解

    原文:http://www.cnblogs.com/whtydn/archive/2009/10/16/1584418.html   下面最先介绍HttpRuntime的Web.config里的配置 ...

  3. JS 读取本地Excel文件

    首先我们先引用一个Excel的类库xlsx.full.min.js 中间处理: 'use strict'; var ExcelReader = { isFirstRead: true, fixdata ...

  4. 求N的阶乘N!中末尾0的个数

    求N的阶乘N!中末尾0的个数 有道问题是这样的:给定一个正整数N,那么N的阶乘N!末尾中有多少个0呢?例如:N=10,N=3628800,则N!的末尾有两个0:直接上干货,算法思想如下:对于任意一个正 ...

  5. Jmeter 如何让变量中包含变量

    在运行Jmeter的过程中,有时候,我们可能会引用一个变量,而这个变量又是由另外一个变量组成的: 譬如我在脚本中要引用变量MappingData1,按照正常的情况,直接就是用${MappingData ...

  6. DB2 create into的用法

    . 建立表 create table zjt_tables as (select * from tables) definition only; create table zjt_views as ( ...

  7. ubuntu系统下安装pyspider:安装命令集合。

    本篇内容的前提是你已安装好python 3.5.在ubuntu系统中安装pyspider最大的困难是要依赖组件经常出错,特别是pycurl,但把对应的依赖组件安装好,简单了.下面直接上代码,所有的依赖 ...

  8. Devexpress VCL Build v2013 vol 13.2.4 发布

    不说了,自己看吧. What's New in 13.2.4 (VCL Product Line)   New Major Features in 13.2 What's New in VCL Pro ...

  9. 2018.09.17 atcoder Tak and Cards(背包)

    传送门 背包经典题. 直接f[i][j]f[i][j]f[i][j]表示选i张牌和为j的方案数. 最后统计答案就行了. 代码: #include<bits/stdc++.h> #defin ...

  10. 微信JSSDK分享接口

    <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js& ...