socket closing problem

F

flupke

Hi,

I have a gui (made in wxPython) that enables a user to connect to a server
and issue some commands. The problem occurs when i try to disconnect the
client. It exits but it doesn't return to the prompt. I have to push
Ctrl-C in order to have it exit completely. The GUI is closed though.

This is a piece of code from the main class that connects to the server
and starts a thread that handles the connection:
======================= snippet =======================
Create a socket and start a thread to handle the connection:
#create an INET, STREAMing socket
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#now connect to the CheckServer
try:
self.s.connect((host, int(data)))
except socket.error, msg:
sys.stderr.write('connect failed: ' + repr(msg) + '\n')
sys.exit(1)

self.connection = client_connection(self.s, self)
self.connection.start()
======================= snippet =======================

client_connection is the thread that handles the connection.
Next, the client_connection class

======================= snippet =======================
BUF_SIZE = 1024

class client_connection(threading.Thread):
def __init__(self,client_socket,parent):
self.client_socket = client_socket
self.parent = parent
threading.Thread.__init__(self)

def run(self):
done = 0
try:
while not done:
print "ready to receive data"
try:
data = self.client_socket.recv(BUF_SIZE)
except socket.error, msg:
print "Error receiving data"
break
if not data:
print "all data received"
break

print "receiving data"
sys.stdout.write(data)
sys.stdout.flush()
self.parent.handleCommand(data)
finally:
self.client_socket.close()
print "Closing client socket"

def message(self,msg):
print "from client_connection", msg
self.client_socket.sendall(msg+"\n")

def close(self):
#self.message("bye")
done = 1
self.client_socket.settimeout(0)
self.client_socket.close()
======================= snippet =======================

So when the user pushes the exit button, i try to call the close
function of the client_connection class:
======================= snippet =======================
def OnFileExit(self,e):
self.connection.close()
time.sleep(5)
self.Close(true) # Close the frame.
======================= snippet =======================

I thought that this would trigger the
"data=self.client_socket.recv(BUF_SIZE)"
from the client_connection class in such a way that it would produce an
exception or return empty data so i could close the thread gracefuly.
Apparently this doesn't happen.

When the server initiates the closing of the socket, then all happens fine.
How can i solve this & what am i doing wrong?

Thanks,
Benedict
 
G

Gandalf

try:
data = self.client_socket.recv(BUF_SIZE)
except socket.error, msg:
print "Error receiving data"
break
if not data:
print "all data received"
break
....


I thought that this would trigger the
"data=self.client_socket.recv(BUF_SIZE)"
from the client_connection class in such a way that it would produce an
exception or return empty data so i could close the thread gracefuly.
Apparently this doesn't happen.

When the server initiates the closing of the socket, then all happens
fine.
How can i solve this & what am i doing wrong?

Well, here is what you need to know:

1. First you should call select.select on the socket. Then you will know
if there is data arrived from the server.
2. If so then you should call client_socket.recv(). Please note that if
you call recv() BEFORE any data has arrived, it will block your program.
(Sockets are defaulting to blocking sockets.)
3. Then you should examine the data received. There are two possibilities:

a.) Your 'data' is not empty -> process the data
b.) Your 'data' is empty. -> it means that the server closed the
connection.

Conclusion: when select.select tells there is more data coming but you
can only recv an empty string >>> it means that the socket has been
closed from the other site. You need to call both select.select and recv
to detect if the socket is closed or not. The same procedure works on
the server side (with the client socket) too.

Another note: you can call select.select on the server socket if you
want to know when a client wants to connect to your server.

Best,

G
 
A

Albert Hofkamp

So when the user pushes the exit button, i try to call the close
function of the client_connection class:
======================= snippet =======================
def OnFileExit(self,e):
self.connection.close()
time.sleep(5)
self.Close(true) # Close the frame.
======================= snippet =======================

I thought that this would trigger the
"data=self.client_socket.recv(BUF_SIZE)"
from the client_connection class in such a way that it would produce an
exception or return empty data so i could close the thread gracefuly.
Apparently this doesn't happen.

sockets are full-duplex, which means that there are two streams of data.
One stream runs from your send-side to the receive-side of the server,
and one stream runs in the other direction (from the send-side of the
server to the receive-side of your connection).
Other than the fact that both streams are connected to the same
processes, both streams are independant.

That means that actions you perform at the send-side are independant of
actions you perform at the receive-side.
Therefore, you cannot expect for the close() to have any effect at the
recv().

(unless the server co-operates of course, and closes its send-side as
reaction to you closing its receive-side. Even then, you still have to
be prepared to receive arbitrary amounts of data before you get EOF,
because you don't know how much data is in the connection between the
server and you).


So the answer to your problem is not to rely on the server reacting to
your close(), and organize you application such that you close both the
send-side and the receive-side (instead of waiting for the EOF).


Albert
 
F

flupke

Thanks Gandalf & Albert,

both your sollutions seem to be working.
This is what i've tried but as to what is the most natural sollution,
i'm not sure.

1) using select.select on the socket (Gandalf)

========================= snippet =======================
client _connection class:
...
in_socket = [self.client_socket]
try:
while not done:
print "ready to receive data"
try:
i, o, e = select.select(in_socket,[],[])
for x_socket in i:
data = x_socket.recv(BUF_SIZE)
except socket.error, msg:
print "Error receiving data"
break
...

main class:
...
def OnFileExit(self,e):
#self.s.close()
#wait until the thread dies
if ( self.s != None ):
self.s.settimeout(0)
self.connection.close()
#self.s.close()
time.sleep(5)
self.Close(true) # Close the frame.
...
========================= snippet =======================

Now, the exit closes the socket which triggers an exception, as
expected. This also triggers a "clean" closing of the connection on the
server.

2) Albert said: "herefore, you cannot expect for the close() to have any
effect at the recv()."
So in order to close, i made sure that part of closing of the client is
to first send a "bye" command to the server. This makes the server close
the socket which then also triggers the exception.

========================= snippet =======================
client _connection class:
...
try:
while not done:
print "ready to receive data"
try:
data = self.client_socket.recv(BUF_SIZE)
except socket.error, msg:
print "Error receiving data"
break
...
def close(self):
print "from client_connection close() "
self.message("bye")
done = 1
self.client_socket.settimeout(0)
self.client_socket.close()
========================= snippet =======================

Now, there are 2 ways to solve my problem but i'm not sure which one is
the nicest when you would aim for the most "logical" sollution.
Any thoughts?

Thanks,
Benedict
 
F

flupke

flupke said:
Thanks Gandalf & Albert,

both your sollutions seem to be working.
This is what i've tried but as to what is the most natural sollution,
i'm not sure.

1) using select.select on the socket (Gandalf)

========================= snippet =======================
client _connection class:
...
in_socket = [self.client_socket]
try:
while not done:
print "ready to receive data"
try:
i, o, e = select.select(in_socket,[],[])
for x_socket in i:
data = x_socket.recv(BUF_SIZE)
except socket.error, msg:
print "Error receiving data"
break
...

main class:
...
def OnFileExit(self,e):
#self.s.close()
#wait until the thread dies
if ( self.s != None ):
self.s.settimeout(0)
self.connection.close()
#self.s.close()
time.sleep(5)
self.Close(true) # Close the frame.
...
========================= snippet =======================

Now, the exit closes the socket which triggers an exception, as
expected. This also triggers a "clean" closing of the connection on the
server.

2) Albert said: "herefore, you cannot expect for the close() to have any
effect at the recv()."
So in order to close, i made sure that part of closing of the client is
to first send a "bye" command to the server. This makes the server close
the socket which then also triggers the exception.

========================= snippet =======================
client _connection class:
...
try:
while not done:
print "ready to receive data"
try:
data = self.client_socket.recv(BUF_SIZE)
except socket.error, msg:
print "Error receiving data"
break
...
def close(self):
print "from client_connection close() "
self.message("bye")
done = 1
self.client_socket.settimeout(0)
self.client_socket.close()
========================= snippet =======================

Now, there are 2 ways to solve my problem but i'm not sure which one is
the nicest when you would aim for the most "logical" sollution.
Any thoughts?

Thanks,
Benedict

Or i can combine the two. If the "bye" command doesn't trigger a close
(for whatever reason, for instance lag) immediately, then the select
could still handle the closing of the socket gracefully?

========================= snippet ==============================
client _connection class:
...
in_socket = [self.client_socket]
try:
while not done:
print "ready to receive data"
try:
i, o, e = select.select(in_socket,[],[])
for x_socket in i:
data = x_socket.recv(BUF_SIZE)
#data = self.client_socket.recv(BUF_SIZE)
except socket.error, msg:
print "Error receiving data"
break
...
def close(self):
print "from client_connection close() "
self.message("bye")
done = 1
self.client_socket.settimeout(0)
self.client_socket.close()
main class:
...
def OnFileExit(self,e):
#wait until the thread dies
if ( self.s != None ):
self.connection.close()
time.sleep(5)
self.Close(true) # Close the frame
...
========================= snippet ==============================

So there seems to be 3 options. 1, 2 or both?
What will it be?

Benedict
 

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

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top