这里简单搞搞select和eopll的接口开发 ~
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实
上从现在看来,这也是它所剩不多的优点之一,现在其实更多的人用epoll,在
python下epoll文档有点少,就先讲究搞搞select ~
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在
Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一
限制。
说点我的理解,要是用烦了多线程的网络编程,可以试试select的模型。
传递给 select
的参数是几个列表,分别表示读事件、写事件和错误事件。select
方法返回三个列表,其中包含满足条件的对象(读、写和异常)。
服务端的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#coding:utf-8
import
socket,select
import
time
import
os
#xiaorui.cc
host
=
"localhost"
port
=
50000
s
=
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host,port))
s.listen(
5
)
while
1
:
infds,outfds,errfds
=
select.select([s,],[],[],
5
)
if
len
(infds) !
=
0
:
clientsock,clientaddr
=
s.accept()
buf
=
clientsock.recv(
8196
)
if
len
(buf) !
=
0
:
print
(buf)
os.popen(
'sleep 10'
).read()
clientsock.close()
# print "no data coming"
|
客户端的代码:
1
2
3
4
5
6
7
8
9
10
|
#coding:utf-8
import
socket,select
#xiaorui.cc
host
=
"localhost"
port
=
50000
s
=
socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,port))
s.send(
"coming from select client"
)
s.close()
|
一个完成的select的例子:
这里有队列的概念
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
#
import
select
import
socket
import
Queue
import
time
import
os
#创建socket 套接字
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(False)
#配置参数
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR ,
1
)
server_address= (
'192.168.0.101'
,
9999
)
server.bind(server_address)
server.listen(
10
)
inputs = [server]
outputs = []
message_queues = {}
#timeout =
20
while
inputs:
print
"waiting for next event"
# readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout) 最后一个是超时,当前连接要是超过这个时间的话,就会kill
readable , writable , exceptional = select.select(inputs, outputs, inputs)
# When timeout reached , select
return
three empty lists
if
not (readable or writable or exceptional) :
print
"Time out ! "
break
;
for
s
in
readable :
if
s
is
server:
#通过inputs查看是否有客户端来
connection, client_address = s.accept()
print
" connection from "
, client_address
connection.setblocking(
0
)
inputs.append(connection)
message_queues[connection] = Queue.Queue()
else
:
data = s.recv(
1024
)
if
data :
print
" received "
, data ,
"from "
,s.getpeername()
message_queues[s].put(data)
# Add output channel
for
response
if
s not
in
outputs:
outputs.append(s)
else
:
#Interpret empty result
as
closed connection
print
" closing"
, client_address
if
s
in
outputs :
outputs.remove(s)
inputs.remove(s)
s.close()
#清除队列信息
del message_queues[s]
for
s
in
writable:
try
:
next_msg = message_queues[s].get_nowait()
except Queue.Empty:
print
" "
, s.getpeername() ,
'queue empty'
outputs.remove(s)
else
:
print
" sending "
, next_msg ,
" to "
, s.getpeername()
os.popen(
'sleep 5'
).read()
s.send(next_msg)
for
s
in
exceptional:
print
" exception condition on "
, s.getpeername()
#stop listening
for
input on the connection
inputs.remove(s)
if
s
in
outputs:
outputs.remove(s)
s.close()
#清除队列信息
del message_queues[s]
|
关于epoll的方面,大家可以看看这个老外的文档,写不错 ~
select是轮询、epoll是触发式的,所以epoll的效率高。
参考的文档地址:http://scotdoyle.com/python-epoll-howto.html
下面是用epoll实现一个服务端 ~
blog from xiaorui.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
import
socket, select
EOL1 = b
'\n\n'
EOL2 = b
'\n\r\n'
response = b
'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
response += b
'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
response += b
'Hello, world!'
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
1
)
serversocket.bind((
'0.0.0.0'
,
8080
))
serversocket.listen(
1
)
serversocket.setblocking(
0
)
epoll = select.epoll()
epoll.register(serversocket.fileno(), select.EPOLLIN)
try
:
connections = {}; requests = {}; responses = {}
while
True:
events = epoll.poll(
1
)
for
fileno, event
in
events:
if
fileno == serversocket.fileno():
connection, address = serversocket.accept()
connection.setblocking(
0
)
epoll.register(connection.fileno(), select.EPOLLIN)
connections[connection.fileno()] = connection
requests[connection.fileno()] = b
''
responses[connection.fileno()] = response
elif event & select.EPOLLIN:
requests[fileno] += connections[fileno].recv(
1024
)
if
EOL1
in
requests[fileno] or EOL2
in
requests[fileno]:
epoll.modify(fileno, select.EPOLLOUT)
connections[fileno].setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK,
1
)
print(
'-'
*
40
+
'\n'
+ requests[fileno].decode()[:-
2
])
elif event & select.EPOLLOUT:
byteswritten = connections[fileno].send(responses[fileno])
responses[fileno] = responses[fileno][byteswritten:]
if
len(responses[fileno]) ==
0
:
connections[fileno].setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK,
0
)
epoll.modify(fileno,
0
)
connections[fileno].shutdown(socket.SHUT_RDWR)
elif event & select.EPOLLHUP:
epoll.unregister(fileno)
connections[fileno].close()
del connections[fileno]
finally
:
epoll.unregister(serversocket.fileno())
|
Epoll的最大好处是不会随着FD的数目增长而降低效率,在select中采用轮询处理,每个fd的处理情况,而epoll是维护一个队列,直接看队列是不是空就可以了。
在这里也推荐大家用epoll写服务端的东西,当然我自己理解的不够好,咱们多交流 !!!
本文转自 rfyiamcool 51CTO博客,原文链接:http://blog.51cto.com/rfyiamcool/1344824,如需转载请自行联系原作者