2
Socket server based on select/poll. Doesn't use threads.
4
Not supported for Jython due to a few obscure problems
5
with the select/poll implementation.
7
Pyro - Python Remote Objects. Copyright by Irmen de Jong.
8
irmen@razorvine.net - http://www.razorvine.net/python/Pyro
11
import select, os, socket, logging
12
from Pyro.socketutil import SocketConnection, createSocket, ERRNO_RETRIES, ERRNO_BADF
13
from Pyro.errors import ConnectionClosedError, PyroError
16
log=logging.getLogger("Pyro.socketserver.select")
18
class SocketServer_Select(object):
19
"""transport server for socket connections, select/poll loop multiplex version."""
20
def __init__(self, callbackObject, host, port, timeout=None):
22
raise NotImplementedError("select-based server is not supported for Jython, use the threadpool server instead")
23
log.info("starting select/poll socketserver")
25
self.sock=createSocket(bind=(host,port), timeout=timeout)
27
self.callback=callbackObject
28
sockaddr=self.sock.getsockname()
29
if sockaddr[0].startswith("127."):
30
if host is None or host.lower()!="localhost" and not host.startswith("127."):
31
log.warn("weird DNS setup: %s resolves to localhost (127.x.x.x)",host)
32
host=host or sockaddr[0]
33
port=port or sockaddr[1]
34
self.locationStr="%s:%d" % (host,port)
36
if self.sock is not None:
39
if hasattr(select,"poll"):
40
def requestLoop(self, loopCondition=lambda:True):
41
log.debug("enter poll-based requestloop")
43
poll=select.poll() #@UndefinedVariable (pydev)
44
fileno2connection={} # map fd to original connection object
45
poll.register(self.sock.fileno(), select.POLLIN | select.POLLPRI) #@UndefinedVariable (pydev)
46
fileno2connection[self.sock.fileno()]=self.sock
47
while loopCondition():
48
polls=poll.poll(1000*Pyro.config.POLLTIMEOUT)
49
for (fd,mask) in polls: #@UnusedVariable (pydev)
50
conn=fileno2connection[fd]
53
conn=self.handleConnection(self.sock)
54
except ConnectionClosedError:
55
log.info("server socket was closed, stopping requestloop")
58
poll.register(conn.fileno(), select.POLLIN | select.POLLPRI) #@UndefinedVariable (pydev)
59
fileno2connection[conn.fileno()]=conn
62
self.callback.handleRequest(conn)
63
except (socket.error,ConnectionClosedError):
71
del fileno2connection[fn]
73
except KeyboardInterrupt:
74
log.debug("stopping on break signal")
77
if hasattr(poll, "close"):
79
log.debug("exit poll-based requestloop")
82
def requestLoop(self, loopCondition=lambda:True):
83
log.debug("entering select-based requestloop")
84
while loopCondition():
87
rlist.append(self.sock)
89
rlist,_,_=select.select(rlist, [], [], Pyro.config.POLLTIMEOUT)
94
# swallow the select error if the loopcondition is no longer true, and exit loop
95
# this can occur if we are shutting down and the socket is no longer valid
97
if self.sock in rlist:
98
rlist.remove(self.sock)
100
conn=self.handleConnection(self.sock)
102
self.clients.append(conn)
103
except ConnectionClosedError:
104
log.info("server socket was closed, stopping requestloop")
106
for conn in rlist[:]:
107
if conn in self.clients:
111
self.callback.handleRequest(conn)
112
except (socket.error,ConnectionClosedError):
115
if conn in self.clients:
116
self.clients.remove(conn)
117
except socket.timeout:
118
pass # just continue the loop on a timeout
119
except KeyboardInterrupt:
120
log.debug("stopping on break signal")
122
log.debug("exit select-based requestloop")
124
def handleRequests(self, eventsockets):
125
"""used for external event loops: handle events that occur on one of the sockets of this server"""
126
for s in eventsockets:
128
# server socket, means new connection
129
conn=self.handleConnection(self.sock)
131
self.clients.append(conn)
133
# must be client socket, means remote call
136
self.callback.handleRequest(s)
137
except (socket.error,ConnectionClosedError):
140
if s in self.clients:
141
self.clients.remove(s)
143
def handleConnection(self, sock):
145
csock, caddr=sock.accept()
146
log.debug("connection from %s",caddr)
147
if Pyro.config.COMMTIMEOUT:
148
csock.settimeout(Pyro.config.COMMTIMEOUT)
149
except socket.error,x:
150
err=getattr(x,"errno",x.args[0])
151
if err in ERRNO_RETRIES:
152
# just ignore this error for now and continue
153
log.warn("accept() failed errno=%d, shouldn't happen", err)
155
if err in ERRNO_BADF:
156
# our server socket got destroyed
157
raise ConnectionClosedError("server socket closed")
160
conn=SocketConnection(csock)
161
if self.callback.handshake(conn):
163
except (socket.error, PyroError), x:
164
log.warn("error during connect: %s",x)
169
log.debug("closing socketserver")
173
for c in self.clients:
181
return self.sock.fileno()
184
socks.extend(self.clients)
187
def pingConnection(self):
188
"""bit of a hack to trigger a blocking server to get out of the loop, useful at clean shutdowns"""
190
self.sock.send("!!!!!!!!!!!!!!!!!!!!!!!")