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

可以看我的上篇文章 《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. 动态加载JS,并执行回调函数

    有些时候我们需要动态的加载一些JS,并在JS加载完成后执行一些回调函数. var loadscript = { $$: function (id) { return document.getEleme ...

  2. 运行代码后出现Process finished with exit code 0是为什么?

    Process finished with exit code 0 意味着你的程序正常执行完毕并退出. 可以科普一下exit code,在大部分编程语言中都适用: exit code 0 表示程序执行 ...

  3. php调试利器之phpdbg

    信海龙的博客 php调试利器之phpdbg 简介 PHPDBG是一个PHP的SAPI模块,可以在不用修改代码和不影响性能的情况下控制PHP的运行环境. PHPDBG的目标是成为一个轻量级.强大.易用的 ...

  4. CURL_SAFE_UPLOAD

    1.版本差异 PHP5.4:支持通过在数组数据中,使用“@+文件全路径”的语法附加文件 curl_setopt(ch, CURLOPT_POSTFIELDS, array( 'file' => ...

  5. boost 学习(1)

    智能指针的学习 中文教程网站 http://zh.highscore.de/cpp/boost/ 不过代码可能 由于BOOST 版本不同需要稍作修改 scoped_ptr 离开作用域则自动调用类析构函 ...

  6. .NET中的Func委托用法

    MSDN对于Func<T, TResult>)的官方解释: 封装一个具有一个参数并返回 TResult 参数指定的类型值的方法. 下面通过几个例子对比下,就容易知道其用法: 以下例子演示了 ...

  7. LNMP 网站搭建

    https://lnmp.org/ lnmp这个一键安装:https://lnmp.org/install.html wget -c http://soft.vpser.net/lnmp/lnmp1. ...

  8. phoenix错误

    spark集群与phoenix进行数据请求时报错: 2018-06-15 17:13:30,158 INFO - Starting task 15.3 in stage 116.0 (TID 6832 ...

  9. 2018.10.20 loj#2593. 「NOIP2010」乌龟棋(多维dp)

    传送门 f[i][j][k][l]f[i][j][k][l]f[i][j][k][l]表示用iii张111,jjj张222,kkk张333,lll张444能凑出的最大贡献. 然后从f[i−1][j][ ...

  10. 2018.07.04 BZOJ 2618 Cqoi2006凸多边形(半平面交)

    2618: [Cqoi2006]凸多边形 Time Limit: 5 Sec Memory Limit: 128 MB Description 逆时针给出n个凸多边形的顶点坐标,求它们交的面积.例如n ...