I have small server and client Python scripts where the client sends a string and the server responds with the reverse. When the client enters a quit string, the client exits and then the server exits.
I want the server's "receive, reverse, send" procedure running in the background while the program is constantly checking stdin for a quit string.
I've tried using threading
but because of the blocking that many socket calls cause it wouldn't work properly.
Just so you can get an idea of what I've already done.
server.py:
import socket
from time import sleep
sock = socket.socket()
sock.bind(("127.0.0.1",12346))
sock.listen(3)
print "Waiting on connection"
conn = sock.accept()
print "Client connected"
while True:
m = conn[0].recv(4096)
if m == "exit":
sleep(1)
break
else:
conn[0].send(m[::-1])
sock.shutdown(socket.SHUT_RDWR)
sock.close()
client.py:
import socket
sock = socket.socket()
sock.connect(("127.0.0.1",12346))
while True:
s = raw_input("message: ")
sock.send(s)
if s == "exit":
print "Quitting"
break
print sock.recv(4096)
sock.shutdown(socket.SHUT_RDWR)
sock.close()
Since you want the server process to be able to handle the client while in the same time receiving input from the server's stdin
, you can just put the whole current server code in a Thread
, then wait input from stdin
.
import socket
from time import sleep
import threading
def process():
sock = socket.socket()
sock.bind(("127.0.0.1",12346))
sock.listen(3)
print "Waiting on connection"
conn = sock.accept()
print "Client connected"
while True:
m = conn[0].recv(4096)
conn[0].send(m[::-1])
sock.shutdown(socket.SHUT_RDWR)
sock.close()
thread = threading.Thread(target=process)
thread.daemon = True
thread.start()
while True:
exit_signal = raw_input('Type "exit" anytime to stop server\n')
if exit_signal == 'exit':
break
and you can remove the "exit" checking in client.
In this code, the server will be doing nothing after the client disconnect, though, it will just wait for "exit" to be typed in the stdin
. You might want to extend the code to make the server able to accept new clients, since you don't want the client to have the ability to close the server. In that case, you can put another while
loop from conn = sock.accept()
to sock.close()
.
And as @usmcs suggested, if you don't have any other command to be sent to the server, it will be better if you use CTRL-C (KeyboardInterrupt
) instead, so you don't need the thread, while it can still end the server gracefully (meaning no error due to the CTRL-C is reported) with this code:
import socket
from time import sleep
import threading
sock = socket.socket()
sock.bind(("127.0.0.1",12346))
sock.listen(3)
print "Waiting on connection"
conn = sock.accept()
print "Client connected"
while True:
try:
m = conn[0].recv(4096)
conn[0].send(m[::-1])
except KeyboardInterrupt:
break
sock.close()
This is an example of non-blocking socket receiving. In case of no-data to receive socket will throw an exception.
import sys
import socket
import fcntl, os
import errno
from time import sleep
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1',9999))
fcntl.fcntl(s, fcntl.F_SETFL, os.O_NONBLOCK)
while True:
try:
msg = s.recv(4096)
except socket.error, e:
err = e.args[0]
if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
sleep(1)
print 'No data available'
continue
else:
# a "real" error occurred
print e
sys.exit(1)
else:
# got a message, do something :)
Here is an example of non-blocking stdin read:
import sys
import select
# If there's input ready, do something, else do something
# else. Note timeout is zero so select won't block at all.
while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
line = sys.stdin.readline()
if line:
something(line)
else: # an empty line means stdin has been closed
print('eof')
exit(0)
else:
something_else()
Basically, you want to combine them and may be add some timeout to force reading stdin on a regular basis in case of many connections.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With