Here is my server
\"\"\"Server using epoll method\"\"\"
import os
import select
import socket
import time
from oodict import OODict
addr = (\
EPOLLERR and EPOLLHUP never happens in the code pasted in the post is because they've always occurred in conjunction with an EPOLLIN or an EPOLLOUT (several of these can be set at once), so the if/then/else have always picked up an EPOLLIN or EPOLLOUT.
Experimenting I've found that EPOLLHUP only happens in conjunction with EPOLLERR, the reason for this may be the way python interfaces with epoll and lowlevel IO, normally recv would return a -1 and set errno to EAGAIN when nothing is available on a non-blocking recv, however python uses '' (nothing returned) to signal EOF.
Closing your telnet-session only closes that end of the tcp-connection, so it's still perfectly valid to call recv on your side, there may be pending data in the tcp receive buffers which your application hasn't read yet so that won't trigger an error-condition.
It seems that EPOLLIN and a recv that returns an empty string is indicative of the other end having closed the connection, however, using an older version of python (before epoll were introduced) and plain select on a pipe, I've experienced that a read that returned '' did not indicate EOF just a lack of available data.
elif event & (select.EPOLLERR | select.EPOLLHUP):
epoll.unregister(fileno)
cs[fileno].close()
del cs[fileno]
If the socket is still open but no read/write available epoll.poll will timeout.
If data if available from the peer, you get an EPOLLIN and data will be available.
If the socket is closed by the peer, you will get an EPOLLIN but when you read it it will return "".
you could then close the socket by shutting it down and picking up the resulting EPOLLHUP event to clean up your internal structures.
or perform cleanup and unregister the epoll.
elif event & select.EPOLLIN:
data = cs[fileno].recv(4)
if not data:
epoll.modify(fileno, 0)
cs[fileno].shutdown(socket.SHUT_RDWR)
if event & select.EPOLLHUP:
epoll.unregister(fd)
My ad-hoc solution to bypass this problem
--- epoll_demo.py.orig 2009-04-28 18:11:32.000000000 +0800
+++ epoll_demo.py 2009-04-28 18:12:56.000000000 +0800
@@ -18,6 +18,7 @@
epoll.register(s.fileno(), select.EPOLLIN) # Level triggerred
cs = {}
+en = {}
data = ''
while True:
time.sleep(1)
@@ -29,10 +30,18 @@
sk.setblocking(0)
print addr
cs[sk.fileno()] = sk
+ en[sk.fileno()] = 0
epoll.register(sk.fileno(), select.EPOLLIN)
elif event & select.EPOLLIN:
data = cs[fileno].recv(4)
+ if not data:
+ en[fileno] += 1
+ if en[fileno] >= 3:
+ print 'closed'
+ epoll.unregister(fileno)
+ continue
+ en[fileno] = 0
print 'recv ', data
epoll.modify(fileno, select.EPOLLOUT)
elif event & select.EPOLLOUT:
Don't you just need to combine the masks together to make use of EPOLLHUP and EPOLLIN at the same time:
epoll.register(sk.fileno(), select.EPOLLIN | select.EPOLLHUP)
Though to be honest I'm not really familiar with the epoll library, so it's just a suggestion really...