Python Servers

Compare and Swap

class CompareAndSwapRegister:
    "CompareAndSwap class"
    def __init__(self):
        self.value = 0
    def CompareAndSwap(self, oldval, newval):
        presval = self.value
        if (presval == oldval):
            self.value = newval
        return presval
    def __str__(self):
        return str(self.value)

Regular Expression

A line oriented Compare and Swap

import re

def lineCAS(reg, line):
    pat = re.compile('\s*(\d+)\s+(\d+)\s*')
    mat = pat.match(line)
    if (mat == None):
        return 'Bad Syntax'
    else:
        return (str(reg.CompareAndSwap(int(mat.group(1)), int(mat.group(2)))))

A Compare and Swap driver

#! /usr/bin/python
from CompareAndSwap import CompareAndSwapRegister
from lineCAS import lineCAS

def stdinCAS():
    x = CompareAndSwapRegister()
    try:
        while True:
            line = raw_input()
            print lineCAS(x, line)
    except EOFError:
        pass

if __name__ == "__main__":
    stdinCAS()

Another Compare and Swap driver

#! /usr/bin/python
from CompareAndSwap import CompareAndSwapRegister
import re

def stdinCAS():
    pat = re.compile('\s*(\d+)\s+(\d+)\s*')
    reg = CompareAndSwapRegister()
    try:
        while True:
            line = raw_input()
            mat = pat.match(line)
            if (mat == None):
                print 'Bad Syntax'
            else:
                print (str(reg.CompareAndSwap(int(mat.group(1)),
                                              int(mat.group(2)))))
    except EOFError:
        pass

if __name__ == "__main__":
    stdinCAS()

Compare and Swap server -- pass 1

#! /usr/bin/python
from CompareAndSwap import CompareAndSwapRegister
import re
import socket

def serverCAS():
    pat = re.compile('^\s*(\d+)\s+(\d+)\s*\n$')
    reg = CompareAndSwapRegister()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ;
        s.bind(('', 22222))
        s.listen(1)

    
        while True:
            strm, addr = s.accept()
            print "Connection from ", addr
            line = strm.recv(1000)
            while line:
                print line,
                mat = pat.match(line)
                if (mat == None):
                    repl = 'Bad Syntax'
                else:
                    repl = (str(reg.CompareAndSwap(int(mat.group(1)),
                                                   int(mat.group(2)))))
                strm.send(repl+'\n\r')
                line = strm.recv(1000)
            strm.close()
    except KeyboardInterrupt:
        print 'Exiting'
        s.close()


if __name__ == "__main__":
    serverCAS()

A simple Compare and Swap client

#! /usr/bin/python
import socket
import sys

if len(sys.argv) == 4:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((sys.argv[1], 22222))
    s.send(sys.argv[2] + ' ' + sys.argv[3] + '\n')
    resp = s.recv(1000)
    print resp,
    s.close()

Simpler Compare and Swap clients to find problems

Try sending incomplete lines and multiple lines.

#! /usr/bin/python
import socket
import sys

if len(sys.argv) == 3:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((sys.argv[1], 22222))
    s.send(sys.argv[2])
    resp = s.recv(1000)
    print resp,
    s.close()

Break up the line. This should result in unpredictable output.

#! /usr/bin/python
import socket
import sys

if len(sys.argv) == 4:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((sys.argv[1], 22222))
    s.send(sys.argv[2])
    s.send(' ')
    s.send(sys.argv[3])
    s.send('\n')
    resp = s.recv(1000)
    print resp,
    s.close()

Problem of streams

Because TCP is stream oriented, there is a problem in reading line oriented input.

Problem of serialization

The server can only process one connection at a time.

The original multi-process solution

Try out the following Python program.

import os

pid = os.fork()
print 'PID = ', pid
print 'Hello world'

In the original Unix solution to this problem, which works well if the two connections are independent and requires shared memory if they don't, the following calls are used to create independent processes. This will be illustrated on the board.

Fortunately, much of the hard work can be put off to a super server. For CGI scripts, the web server acts as a super server. RPC mechanisms also resemble super servers.

The bad one-process solution

The bad solution is to use non-blocking socket and write busy-wait loops to test them.

The hard but better one-process solution

The hard one-process solution is to use a blocking call that can wait on several input sources simultaneously. This one uses the poll system call with can be used via the Python select module.

Here's the basic idea.

And here's some examples.

Threads

The preferred modern solution is to use threads with Python's threading module.

Finally, due to the use of a Global Interpreter Lock (GIL) in the CPython and Ruby MRI interpreters, think hard before using threads for compute-bound computation in Python and Ruby.