Recent comments


Syndicate


Follow Me

Twitter Facebook

Python threads part 1

It's been almost two weeks since my last formal post and I'm back again with hopefully something informative. Today I'm gonna be showing you Python's thread package, If you don't know what threading is... in the most simplest form, its a way to run several things simultaneously. In this series of post we're gonna be creating a simple client-server command line program wherein the server got a list of products (name and price) and client should be able to view, add and delete from.

Now that sounds simple, but we wanted the server to be responsive to the admin while at the same time serving the clients so that the admin can still execute some stuff. On the client side, we also want the clients to be able to process incoming messages from server while also be able to accept inputs from the client.

The first part is to build the server, just like before; I'll show the code and explain it later:

from socket import *
import sys, thread

def display_items(items):
  out = ""
  for item in items:
    out += "%s\n" % item
  return out

def serve_clients(server):
  global items, clients
  while True:
    (client, address) = server.accept()
    client.send("Howdy, stranger!")
    clients.append(client)
    thread.start_new_thread(process_client, (client, ))

def process_client(client):
  global items, clients
  while True:
    request = client.recv(1024)
    if request == "close": break
    if len(request) > 0:
      args = request.split()
      if args[0] == "list":
        client.send(display_items(items['list']))
      elif args[0] == "add":
        if len(args) == 1:
          client.send("Invalid command")
        else:
          item = {'name': args[1], 'price': float(args[2])}
          items['list'].append(item)
          client.send(display_items(items['list']))
      elif args[0] == "delete":
        if len(args) == 1:
          client.send("Invalid command")
        else:
          for item in items['list']:
            if item.get('name') == args[1]:
              items['lock'].acquire()
              items['list'].remove(item)
              items['lock'].release()
          client.send(display_items(items['list']))
      else:
        client.send("Invalid command")
    else:
      client.send("Invalid command")
  client.close()
  clients.remove(client)


if __name__ == "__main__":
  item_list = [{'name': 'Siomai', 'price': 0.5},
               {'name': 'Siopao', 'price': 1.5},
               {'name': 'Sagmaw', 'price': 0.0}]
  items     =  {'list': item_list, 'lock': thread.allocate_lock()}
  clients   =  []
  
  port = int(sys.argv[1])
  server = socket(AF_INET, SOCK_STREAM)
  server.bind(('', port))
  
  server.listen(5)  
  thread.start_new_thread(serve_clients, (server, ))
  
  while True:
    request = raw_input(">> ")
    if request == "close": break
    args = request.split()
    if args[0] == "list":
      print display_items(items['list'])
    elif args[0] == "clients":
      print clients
    elif args[0] == "send":
      if len(args) < 3:
        print "Invalid command"
      else:
        cid = int(args[1])
        args.pop(0)
        args.pop(0)
        clients[cid].send(" ".join(args))
  
  for client in clients:
    client.send("close")
    client.close()
  
  server.close()
  
  print display_items(items['list'])

The above code is composed of 3 functions and a main, the first function display_items accepts a dictionary of items, formats and return it. The second function serve_clients, this is the function that gets called to serve any incoming connection request from clients. Let me talk about it more since the concept here is repeated on the next function:

def serve_clients(server):
  global items, clients
  while True:
    (client, address) = server.accept()
    client.send("Howdy, stranger!")
    clients.append(client)
    thread.start_new_thread(process_client, (client, ))

This function accepts the server socket object, first it references some global variables that has been declared from the main. It's followed by a loop which accepts incoming connection by calling server.accept() method, this method returns a tuple of client and address. After accepting the connection we send an initial message to client, we then append the client socket to the list of clients then let the process_client function handle any processing from that particular client by assigning it to a new thread which is done by calling start_new_thread method of thread class. This function accepts two arguments, the first one is the function to be handled by the thread and the second one is the arguments to be passed to that function, since it requires that the second argument be a tuple, we add an extra comma to make it a tuple.

The third function is a bit longer than the previous two but its really simple, here we constantly check for incoming requests from the client and process them accordingly. If the client sends a close request, we exit the loop, close and removed the client form the list of clients. There are four commands that the server is able to serve, list, add, delete ad close. But there's something worth mentioning in the delete:

items['lock'].acquire()
items['list'].remove(item)
items['lock'].release()

Here we're suing a lock to block any other thread from removing an item except for us. We need this since if two users where to delete the same item, the last one would get an error saying that the item that's about to be delete doesn't exist since it has been deleted by the first one already.

The last part is the main, first you'll notice that our server accepts a port upon execution. We then create a server socket and assign the processing on connecting with client to the serve_clients function which we just talk about a while ago. Now with the processing of clients out of our way, we can handle the admins request without freezing. If the server admin types close, all clients gets disconnected and the server socket gets closed, we the print what was the last state of our items.

This will just be a two part series I guess and the second part is where we talk about the client side, then we're going to test drive the program. I'll post it tomorrow if I don't get lazy.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.