Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run a background procedure while constantly checking for input - threading?

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()
like image 802
lightandlight Avatar asked Mar 25 '14 23:03

lightandlight


2 Answers

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()
like image 67
justhalf Avatar answered Sep 29 '22 02:09

justhalf


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.

like image 35
Arseniy Avatar answered Sep 29 '22 00:09

Arseniy