Threaded server: what's wrong with this code?

F

Fortepianissimo

Below are three simple scripts: jmSocketLib.py contains library code
for socket server and client, server.py instantiates a server binding
to port 50000+ (grab the first available port), and client.py
simulates 1000 consecutive connection requests to the server (stress
test). The client searches from port 50000 for the server, and sends a
challenge "JM?" and expects to get a response "0_87" - otherwise
anything that happens to own a port 50000+ would be mistaken to be the
real server.

The code:

---------- jmSocketLib.py ----------
#!/usr/bin/env python

import SocketServer,socket,threading

PORT_MIN=50000
PORT_MAX=50005

THREAD_COUNT=0

class JMSocketThread (threading.Thread):
def __init__ (self, theSocket):
threading.Thread.__init__(self,target=self._myRun,args=[theSocket])
def _myRun (self,theSocket):
global THREAD_COUNT
THREAD_COUNT+=1
print 'Threaded request started (%d)...'%THREAD_COUNT
input=''
while 1:
input += theSocket.recv(1024)
if len(input) and input[-1]=='\n': break
input=input.strip()

print 'Got input: \'%s\''%input
if input=='Is it you?':
theSocket.sendall('Yes honey.\n')

print 'Threaded request finished.'
THREAD_COUNT-=1


class JMHandler (SocketServer.BaseRequestHandler):
def handle (self):
print 'Client connected...'
JMSocketThread(self.request).start()
print 'Request handed off to a thread.'


class JMServer (SocketServer.TCPServer):
def __init__ (self):
port=PORT_MIN
while port<PORT_MAX:
try:
SocketServer.TCPServer.__init__(self,('',port),JMHandler)
print 'Bound port %d'%port
break
except:
port+=1

class JMClient:
def __init__ (self):
port=PORT_MIN

while port<=PORT_MAX:
try:
self.socket=socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
self.socket.connect(('',port))
self.socket.sendall('Is it you?\n')
print 'Query sent...'
reply=''
while 1:
reply += self.socket.recv(1024)
if len(reply) and reply[-1]=='\n': break
reply=reply.strip()

print 'Got reply: \'%s\''%reply

if reply == 'Yes honey.':
print 'Found the server at port %d'%port
break
else: raise None
except:
self.socket.close()
port+=1


---------- server.py ----------
#!/usr/bin/env python

from jmSocketLib import *

if __name__ == '__main__':
jmServer=JMServer()
jmServer.serve_forever()

---------- client.py ----------
#!/usr/bin/env python

from jmSocketLib import *

if __name__ == '__main__':
# stress test
for i in range(1000):
print i
JMClient()




Now the problem is, every now and then I got this error from the
server side:

---- ERROR on server side ----
Client connected...
Threaded request started (1)...
Request handed off to a thread.
Got input: 'Is it you?'
Exception in thread Thread-125:
Traceback (most recent call last):
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/threading.py",
line 436, in __bootstrap
self.run()
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/threading.py",
line 416, in run
self.__target(*self.__args, **self.__kwargs)
File "/Users/ben/temp/jmSocketLib.py", line 25, in _myRun
theSocket.sendall('0_87\n')
File "<string>", line 1, in sendall
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/socket.py",
line 143, in _dummy
raise error(EBADF, 'Bad file descriptor')
error: (9, 'Bad file descriptor')
--------------------------

Of course the client then freezes:


---- Client output ----
124
Query sent...
(and freezes)


Does anyone have an explanation why this didn't work? Thanks a lot!
 
A

Ahmed MOHAMED ALI

Hi,
Sorry for my bad english because i am French-speaking.
The problem is in JMSocketThread.handle .When this function terminates its
execution flow,the parameter "self.request" is not longer valid.
So the thread work with an invalid socket.For this reason,you got the 'Bad
file descriptor' error.
To correct the problem here's the modified code.

#---Modified JMHandler class

class JMHandler (SocketServer.BaseRequestHandler):
def handle (self):
print 'Client connected...'
threadEvent = Event() # create an event to wait for the thread
JMSocketThread(self.request,threadEvent ).start() # don't forget
to pass the event parameter here
print 'Request handed off to a thread.'
threadEvent.wait()

#---Modified JMSocketThread class

class JMSocketThread (threading.Thread):
def __init__ (self, theSocket):

threading.Thread.__init__(self,target=self._myRun,args=[theSocket,theEvent])
# don't forget to pass the event here
def _myRun (self,theSocket,theEvent): #don't forget to pass the event
here too
global THREAD_COUNT
THREAD_COUNT+=1
print 'Threaded request started (%d)...'%THREAD_COUNT
input=''
while 1:
input += theSocket.recv(1024)
if len(input) and input[-1]=='\n': break
input=input.strip()

print 'Got input: \'%s\''%input
if input=='Is it you?':
theSocket.sendall('Yes honey.\n')

print 'Threaded request finished.'
THREAD_COUNT-=1
theEvent.set() # set the event here to tell the handler to exit


Best regards,
Ahmed


Fortepianissimo said:
Below are three simple scripts: jmSocketLib.py contains library code
for socket server and client, server.py instantiates a server binding
to port 50000+ (grab the first available port), and client.py
simulates 1000 consecutive connection requests to the server (stress
test). The client searches from port 50000 for the server, and sends a
challenge "JM?" and expects to get a response "0_87" - otherwise
anything that happens to own a port 50000+ would be mistaken to be the
real server.

The code:

---------- jmSocketLib.py ----------
#!/usr/bin/env python

import SocketServer,socket,threading

PORT_MIN=50000
PORT_MAX=50005

THREAD_COUNT=0

class JMSocketThread (threading.Thread):
def __init__ (self, theSocket):
threading.Thread.__init__(self,target=self._myRun,args=[theSocket])
def _myRun (self,theSocket):
global THREAD_COUNT
THREAD_COUNT+=1
print 'Threaded request started (%d)...'%THREAD_COUNT
input=''
while 1:
input += theSocket.recv(1024)
if len(input) and input[-1]=='\n': break
input=input.strip()

print 'Got input: \'%s\''%input
if input=='Is it you?':
theSocket.sendall('Yes honey.\n')

print 'Threaded request finished.'
THREAD_COUNT-=1


class JMHandler (SocketServer.BaseRequestHandler):
def handle (self):
print 'Client connected...'
JMSocketThread(self.request).start()
print 'Request handed off to a thread.'


class JMServer (SocketServer.TCPServer):
def __init__ (self):
port=PORT_MIN
while port<PORT_MAX:
try:
SocketServer.TCPServer.__init__(self,('',port),JMHandler)
print 'Bound port %d'%port
break
except:
port+=1

class JMClient:
def __init__ (self):
port=PORT_MIN

while port<=PORT_MAX:
try:
self.socket=socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
self.socket.connect(('',port))
self.socket.sendall('Is it you?\n')
print 'Query sent...'
reply=''
while 1:
reply += self.socket.recv(1024)
if len(reply) and reply[-1]=='\n': break
reply=reply.strip()

print 'Got reply: \'%s\''%reply

if reply == 'Yes honey.':
print 'Found the server at port %d'%port
break
else: raise None
except:
self.socket.close()
port+=1


---------- server.py ----------
#!/usr/bin/env python

from jmSocketLib import *

if __name__ == '__main__':
jmServer=JMServer()
jmServer.serve_forever()

---------- client.py ----------
#!/usr/bin/env python

from jmSocketLib import *

if __name__ == '__main__':
# stress test
for i in range(1000):
print i
JMClient()




Now the problem is, every now and then I got this error from the
server side:

---- ERROR on server side ----
Client connected...
Threaded request started (1)...
Request handed off to a thread.
Got input: 'Is it you?'
Exception in thread Thread-125:
Traceback (most recent call last):
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/threading.py",
line 436, in __bootstrap
self.run()
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/threading.py",
line 416, in run
self.__target(*self.__args, **self.__kwargs)
File "/Users/ben/temp/jmSocketLib.py", line 25, in _myRun
theSocket.sendall('0_87\n')
File "<string>", line 1, in sendall
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/socket.py",
line 143, in _dummy
raise error(EBADF, 'Bad file descriptor')
error: (9, 'Bad file descriptor')
--------------------------

Of course the client then freezes:


---- Client output ----
124
Query sent...
(and freezes)


Does anyone have an explanation why this didn't work? Thanks a lot!
 
B

Benjamin Han

Thank you for the reply, my questions follow:

The problem is in JMSocketThread.handle .When this function terminates its
execution flow,the parameter "self.request" is not longer valid.
So the thread work with an invalid socket.For this reason,you got the 'Bad
file descriptor' error.

Maybe my understanding is wrong - but when you pass self.request the reference
count should still be greater than 0, so it shouldn't be garbage-collected?
To correct the problem here's the modified code.

#---Modified JMHandler class

class JMHandler (SocketServer.BaseRequestHandler):
def handle (self):
print 'Client connected...'
threadEvent = Event() # create an event to wait for the thread

Minor: should be threading.Event()
JMSocketThread(self.request,threadEvent ).start() # don't forget
to pass the event parameter here
print 'Request handed off to a thread.'
threadEvent.wait()

#---Modified JMSocketThread class

class JMSocketThread (threading.Thread):
def __init__ (self, theSocket):

threading.Thread.__init__(self,target=self._myRun,args=[theSocket,theEvent])
# don't forget to pass the event here
def _myRun (self,theSocket,theEvent): #don't forget to pass the event
here too
global THREAD_COUNT
THREAD_COUNT+=1
print 'Threaded request started (%d)...'%THREAD_COUNT
input=''
while 1:
input += theSocket.recv(1024)
if len(input) and input[-1]=='\n': break
input=input.strip()

print 'Got input: \'%s\''%input
if input=='Is it you?':
theSocket.sendall('Yes honey.\n')

print 'Threaded request finished.'
THREAD_COUNT-=1
theEvent.set() # set the event here to tell the handler to exit


Best regards,
Ahmed

But this solution sort of defeats the purpose of using threads? Because
now every request must wait until the last one is fully processed...

I'd appreciate more comments on these questions. Thank you!

Ben

Fortepianissimo said:
Below are three simple scripts: jmSocketLib.py contains library code
for socket server and client, server.py instantiates a server binding
to port 50000+ (grab the first available port), and client.py
simulates 1000 consecutive connection requests to the server (stress
test). The client searches from port 50000 for the server, and sends a
challenge "JM?" and expects to get a response "0_87" - otherwise
anything that happens to own a port 50000+ would be mistaken to be the
real server.

The code:

---------- jmSocketLib.py ----------
#!/usr/bin/env python

import SocketServer,socket,threading

PORT_MIN=50000
PORT_MAX=50005

THREAD_COUNT=0

class JMSocketThread (threading.Thread):
def __init__ (self, theSocket):
threading.Thread.__init__(self,target=self._myRun,args=[theSocket])
def _myRun (self,theSocket):
global THREAD_COUNT
THREAD_COUNT+=1
print 'Threaded request started (%d)...'%THREAD_COUNT
input=''
while 1:
input += theSocket.recv(1024)
if len(input) and input[-1]=='\n': break
input=input.strip()

print 'Got input: \'%s\''%input
if input=='Is it you?':
theSocket.sendall('Yes honey.\n')

print 'Threaded request finished.'
THREAD_COUNT-=1


class JMHandler (SocketServer.BaseRequestHandler):
def handle (self):
print 'Client connected...'
JMSocketThread(self.request).start()
print 'Request handed off to a thread.'


class JMServer (SocketServer.TCPServer):
def __init__ (self):
port=PORT_MIN
while port<PORT_MAX:
try:
SocketServer.TCPServer.__init__(self,('',port),JMHandler)
print 'Bound port %d'%port
break
except:
port+=1

class JMClient:
def __init__ (self):
port=PORT_MIN

while port<=PORT_MAX:
try:
self.socket=socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
self.socket.connect(('',port))
self.socket.sendall('Is it you?\n')
print 'Query sent...'
reply=''
while 1:
reply += self.socket.recv(1024)
if len(reply) and reply[-1]=='\n': break
reply=reply.strip()

print 'Got reply: \'%s\''%reply

if reply == 'Yes honey.':
print 'Found the server at port %d'%port
break
else: raise None
except:
self.socket.close()
port+=1


---------- server.py ----------
#!/usr/bin/env python

from jmSocketLib import *

if __name__ == '__main__':
jmServer=JMServer()
jmServer.serve_forever()

---------- client.py ----------
#!/usr/bin/env python

from jmSocketLib import *

if __name__ == '__main__':
# stress test
for i in range(1000):
print i
JMClient()




Now the problem is, every now and then I got this error from the
server side:

---- ERROR on server side ----
Client connected...
Threaded request started (1)...
Request handed off to a thread.
Got input: 'Is it you?'
Exception in thread Thread-125:
Traceback (most recent call last):
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/threading.py",
line 436, in __bootstrap
self.run()
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/threading.py",
line 416, in run
self.__target(*self.__args, **self.__kwargs)
File "/Users/ben/temp/jmSocketLib.py", line 25, in _myRun
theSocket.sendall('0_87\n')
File "<string>", line 1, in sendall
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/socket.py",
line 143, in _dummy
raise error(EBADF, 'Bad file descriptor')
error: (9, 'Bad file descriptor')
--------------------------

Of course the client then freezes:


---- Client output ----
124
Query sent...
(and freezes)


Does anyone have an explanation why this didn't work? Thanks a lot!
 
B

Benjamin Han

Ah - never mind. When reading SocketServer.py I found I should have used
ThreadingTCPServer instead of TCPServer (and I don't even need to create
threads myself!). Thank you for the reply though.

Ben

Hi,
Sorry for my bad english because i am French-speaking.
The problem is in JMSocketThread.handle .When this function terminates its
execution flow,the parameter "self.request" is not longer valid.
So the thread work with an invalid socket.For this reason,you got the 'Bad
file descriptor' error.
To correct the problem here's the modified code.

#---Modified JMHandler class

class JMHandler (SocketServer.BaseRequestHandler):
def handle (self):
print 'Client connected...'
threadEvent = Event() # create an event to wait for the thread
JMSocketThread(self.request,threadEvent ).start() # don't forget
to pass the event parameter here
print 'Request handed off to a thread.'
threadEvent.wait()

#---Modified JMSocketThread class

class JMSocketThread (threading.Thread):
def __init__ (self, theSocket):

threading.Thread.__init__(self,target=self._myRun,args=[theSocket,theEvent])
# don't forget to pass the event here
def _myRun (self,theSocket,theEvent): #don't forget to pass the event
here too
global THREAD_COUNT
THREAD_COUNT+=1
print 'Threaded request started (%d)...'%THREAD_COUNT
input=''
while 1:
input += theSocket.recv(1024)
if len(input) and input[-1]=='\n': break
input=input.strip()

print 'Got input: \'%s\''%input
if input=='Is it you?':
theSocket.sendall('Yes honey.\n')

print 'Threaded request finished.'
THREAD_COUNT-=1
theEvent.set() # set the event here to tell the handler to exit


Best regards,
Ahmed


Fortepianissimo said:
Below are three simple scripts: jmSocketLib.py contains library code
for socket server and client, server.py instantiates a server binding
to port 50000+ (grab the first available port), and client.py
simulates 1000 consecutive connection requests to the server (stress
test). The client searches from port 50000 for the server, and sends a
challenge "JM?" and expects to get a response "0_87" - otherwise
anything that happens to own a port 50000+ would be mistaken to be the
real server.

The code:

---------- jmSocketLib.py ----------
#!/usr/bin/env python

import SocketServer,socket,threading

PORT_MIN=50000
PORT_MAX=50005

THREAD_COUNT=0

class JMSocketThread (threading.Thread):
def __init__ (self, theSocket):
threading.Thread.__init__(self,target=self._myRun,args=[theSocket])
def _myRun (self,theSocket):
global THREAD_COUNT
THREAD_COUNT+=1
print 'Threaded request started (%d)...'%THREAD_COUNT
input=''
while 1:
input += theSocket.recv(1024)
if len(input) and input[-1]=='\n': break
input=input.strip()

print 'Got input: \'%s\''%input
if input=='Is it you?':
theSocket.sendall('Yes honey.\n')

print 'Threaded request finished.'
THREAD_COUNT-=1


class JMHandler (SocketServer.BaseRequestHandler):
def handle (self):
print 'Client connected...'
JMSocketThread(self.request).start()
print 'Request handed off to a thread.'


class JMServer (SocketServer.TCPServer):
def __init__ (self):
port=PORT_MIN
while port<PORT_MAX:
try:
SocketServer.TCPServer.__init__(self,('',port),JMHandler)
print 'Bound port %d'%port
break
except:
port+=1

class JMClient:
def __init__ (self):
port=PORT_MIN

while port<=PORT_MAX:
try:
self.socket=socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
self.socket.connect(('',port))
self.socket.sendall('Is it you?\n')
print 'Query sent...'
reply=''
while 1:
reply += self.socket.recv(1024)
if len(reply) and reply[-1]=='\n': break
reply=reply.strip()

print 'Got reply: \'%s\''%reply

if reply == 'Yes honey.':
print 'Found the server at port %d'%port
break
else: raise None
except:
self.socket.close()
port+=1


---------- server.py ----------
#!/usr/bin/env python

from jmSocketLib import *

if __name__ == '__main__':
jmServer=JMServer()
jmServer.serve_forever()

---------- client.py ----------
#!/usr/bin/env python

from jmSocketLib import *

if __name__ == '__main__':
# stress test
for i in range(1000):
print i
JMClient()




Now the problem is, every now and then I got this error from the
server side:

---- ERROR on server side ----
Client connected...
Threaded request started (1)...
Request handed off to a thread.
Got input: 'Is it you?'
Exception in thread Thread-125:
Traceback (most recent call last):
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/threading.py",
line 436, in __bootstrap
self.run()
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/threading.py",
line 416, in run
self.__target(*self.__args, **self.__kwargs)
File "/Users/ben/temp/jmSocketLib.py", line 25, in _myRun
theSocket.sendall('0_87\n')
File "<string>", line 1, in sendall
File "/sw/src/root-python23-2.3.2-22/sw/lib/python2.3/socket.py",
line 143, in _dummy
raise error(EBADF, 'Bad file descriptor')
error: (9, 'Bad file descriptor')
--------------------------

Of course the client then freezes:


---- Client output ----
124
Query sent...
(and freezes)


Does anyone have an explanation why this didn't work? Thanks a lot!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top