您的位置:首页 > 理论基础 > 计算机网络

多路复用(select、epoll)实现tcp服务

2016-09-06 23:31 183 查看
-------------------------------多路复用的服务器(select)-------------------------------

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

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

应用:
#import select:对应import包的引用操作

#readable,writeable,exceptinal=select.select(inputs,[],[]):使用select对套接字组成的列表进行遍历,过滤出对应的未堵塞的套接字

python代码示例:

#coding=utf-8

#引用对应的包
from socket import *

import select

import sys

#函数:main
def main():
#创建服务器的套接字
serTcpSocket=socket(AF_INET,SOCK_STREAM)

#进行端口和ip的绑定操作
serTcpSocket.bind(("",int(sys.argv[1])))

#开启被动,进行监听
serTcpSocket.listen(10)

#创建一个epoll对象
epoll=select.epoll()

#使用epoll对套接字在操作系统中进行注册
epoll.register(serTcpSocket.fileno(),select.EPOLLIN|select.EPOLLET)

#创建两个字典
#根据套接字的文件标识符对应套接字
connection={}

#根据套接字的文件标识符对应ip和端口元组信息
address={}

#提示服务开启
print("------服务开启-----")

#进行循环遍历
while True:
#接收对象中,未阻塞的套接字
epollList = epoll.poll()

for fd,event  in epollList:
#判断是否为服务器的套接字
if fd==serTcpSocket.fileno():
#接收客户端传递过来的数据信息
newSocket,destAddr=serTcpSocket.accept()

print("客户端(%s)以上线"%str(destAddr))

#将对应的数据向字典中进行存储
connection[newSocket.fileno()]=newSocket
address[newSocket.fileno()]=destAddr

#将套接字在对应的epoll对象中进行注册
epoll.register(newSocket.fileno(),select.EPOLLIN|select.EPOLLET)

elif event==select.EPOLLIN:
#在字典中取出对应的套接字对象和地址(ip和端口)
soc=connection[fd]
addr=address[fd]

#接收客户端发送过来的数据信息
recvData=soc.recv(1024)

#判断客户端是否下线
if len(recvData)>0:
#进行打印
print("客户端(%s):%s"%(str(addr),recvData))

#echo服务,将信息回发到客户端
soc.send(recvData)
else:
print("客户端(%s)以下线"%str(addr))

#进行该套接字在epoll中的注销操作
epoll.unregister(fd)

#关闭该套接字
soc.close()

#程序入口
if __name__=="__main__":
main()


多路复用epoll实现tcp服务
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐