Here is my server
\"\"\"Server using epoll method\"\"\"
import os
import select
import socket
import time
from oodict import OODict
addr = (\
After I move select.EPOLLHUP handling code to the line before select.EPOLLIN, hup event still cant be got in 'telnet'. But by coincidence I found that if I use my own client script, there are hup events! strange...
And according to man epoll_ctl
EPOLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of connection. (This flag is especially useful for writing simple code to
detect peer shutdown when using Edge Triggered monitoring.)
EPOLLERR
Error condition happened on the associated file descriptor. epoll_wait(2) will always wait for this event; it is not necessary to set it
in events.
EPOLLHUP
Hang up happened on the associated file descriptor. epoll_wait(2) will always wait for this event; it is not necessary to set it in
events.
Seems there shall be a EPOLLRDHUP event when remote side closed connection, which is not implemented by python, don't know why
The EPOLLRDHUP flag is not defined in Python for no reason. If your Linux kernel is >= 2.6.17, you can define it and register your socket in epoll like this:
import select
if not "EPOLLRDHUP" in dir(select):
select.EPOLLRDHUP = 0x2000
...
epoll.register(socket.fileno(), select.EPOLLIN | select.EPOLLRDHUP)
You can then catch the event you need using the same flag (EPOLLRDHUP):
elif event & select.EPOLLRDHUP:
print "Stream socket peer closed connection"
# try shutdown on both side, then close the socket:
socket.close()
epoll.unregister(socket.fileno())
For more info you can check selectmodule.c in python's repository:
The issue why you're not detecting EPOLLHUP/EPOLLERR in your code is because of the bitwise operations you are doing. See when a socket is ready to read epoll will throw a flag with bit 1 which is equal to select.EPOLLIN (select.EPOLLIN == 1). Now say the client hangs up (gracefully or not) epoll on the server will throw a flag with bit 25 which is equal to EPOLLIN+EPOLLERR+EPOLLHUP. So with the bit 25 (event variable in your code) you can see how EPOLLERR is not being detected because all of your elif statements (with the exception of EPOLLOUT line) don't return 0 so the first elif statement is executed, for example:
>>> from select import EPOLLIN,EPOLLOUT,EPOLLHUP,EPOLLERR
>>> event = 25
>>> event & EPOLLIN
1
>>> event & EPOLLERR
8
>>> event & EPOLLHUP
16
>>> event & EPOLLOUT
0
Notice how the first three don't return 0? That's why your code isn't detecting EPOLLERR/EPOLLHUP correctly. When a client hangs up you can still read from the socket as the server side is still up (of course it would return 0 data if you did) hence EPOLLIN but since the client hung up it's also EPOLLHUP and since it's EPOLLHUP it's also EPOLLERR as a hangup is somewhat of an error. I know I'm late on commenting on this but I hope I helped someone out there lol
Here is a way I would rewrite your code to express what I'm saying better:
import os
import select
import socket
import time
from oodict import OODict
addr = ('localhost', 8989)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(8)
s.setblocking(0) # Non blocking socket server
epoll = select.epoll()
read_only = select.EPOLLIN | select.EPOLLPRI | select.EPOLLHUP | select.EPOLLERR
read_write = read_only | select.EPOLLOUT
biterrs = [25,24,8,16,9,17,26,10,18] #Bitwise error numbers
epoll.register(s.fileno(),read_only)
cs = {}
data = ''
while True:
time.sleep(1)
events = epoll.poll(1) # Timeout 1 second
print 'Polling %d events' % len(events)
for fileno, event in events:
if fileno == s.fileno():
sk, addr = s.accept()
sk.setblocking(0)
print addr
cs[sk.fileno()] = sk
epoll.register(sk.fileno(),read_only)
elif (event is select.EPOLLIN) or (event is select.EPOLLPRI):
data = cs[fileno].recv(4)
print 'recv ', data
epoll.modify(fileno, read_write)
elif event is select.EPOLLOUT:
print 'send ', data
cs[fileno].send(data)
data = ''
epoll.modify(fileno, read_only)
elif event in biterrs:
print 'err'
epoll.unregister(fileno)
I have another approach..
try:
data = s.recv(4096)
except socket.error:
if e[0] in (errno.EWOULDBLOCK, errno.EAGAIN): # since this is a non-blocking socket..
return # no error
else:
# error
socket.close()
if not data: #closed either
socket.close()