0
votes

Me and my class mate are writing a server/client-model in Python for a school project. All the server script does is accepting connections made from clients (running on the localhost), and receives messages printing them to the terminal window. The script was written on a Macbook Air running OSX10.7.3 64bit and acts as it should on this computer, but when run on my classmate's Linux computer (Ubuntu 11.04 64bit) or our computer lab Linux computer (Ubuntu 10.04 32bit) it runs somewhat different. In the script, both server and client are set to be non-blocking, and this works fine on my Mac as stated, but on the Linux-machine they acts as they were blocking, not allowing any data to be sent from Client nr2, before receiving any from Client nr1.

Shouldn't Python be a multiplatform language? And if not, what will be the best way to tweak our code, making it work as it should on both MacOS and Ubuntu?

I'm sorry if this was somewhat vague, but i post my code from both server and client script below.

Btw, my Mac runs Python 2.7.1, the Ubuntu 11.04 runs Python 2.7.1+, and the Ubuntu 10.04 runs Python 2.6.5.

As we are both new to socket programming and are beginners to Python (hence the school project and the simple code =)) it would be most appreciated if someone could please elaborate in a simple way.

First the server.py file:

import select
import socket
import sys
import threading
import client

class Server:
  def __init__(self):
    self.host = 'localhost'
    self.port = 50000
    self.backlog = 5
    self.size = 1024
    self.server = None
    self.threads = []

def openSocket(self):
    try: 
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server.bind((self.host, self.port))
        self.server.listen(5)
        print "Listening to port " + str(self.port) + "..."
    except socket.error, (value,message):
        if self.server:
            self.server.close()
        print "Could not open socket: " + message
        sys.exit(1)

def run(self):
    self.openSocket()
    self.server.setblocking(0)
    input = [self.server,sys.stdin]
    running = True
    while running:

        inputready, outputready, exceptready = select.select(input,[],[], 0.01)             

        for s in inputready:
            c = client.Client(self.server.accept())
            self.threads.append(c)
            print "Client " + str(c.address) + " connected"
            inputready.pop()
        for c in self.threads:
            try:
                data = c.client.recv(self.size)
                print data
            except socket.error, (value,message):   
                continue

    #close threads
    self.server.close()
    for c in self.threads:
        c.join()

if __name__ == "__main__":
s = Server()
s.run()

Then the client.py file:

import select
import socket
import sys
import server
import threading

class Client(threading.Thread): 
def __init__(self,(client,address)): 
    threading.Thread.__init__(self) 
    self.client = client 
    self.address = address 
    self.size = 1024

def run(self):
    self.client.connect(('localhost',50000))
    c.client.setblocking(0)
    running = True 
    while running:

        line = sys.stdin.readline()
        if line == "exit":
            self.client.close()
        else:   
            self.client.sendall(line)

    self.client.close()

if __name__ == "__main__":
c = Client((socket.socket(socket.AF_INET, socket.SOCK_STREAM),'localhost'))
c.run()

PS: Nevermind the indentation on some loops. Something happened when i copy-pasted my code. PPS: Note that we don't get any error messages when running this on either computer. It just acts differently.

Thanks in advance

1

1 Answers

1
votes

Admittedly, I have zero experience with non-blocking sockets, so I can't really help with that. However, the behaviour you described seems to be exactly what your code does - the server accepts a connection request, creates a new Client object, appends it to a list - and then listens to all the clients in that same list, one by one. That means the first client is able to connect and send messages, but the next client can't connect because the server is receiving data from the first client.

Also, this code is never reached:

    self.server.close()
    for c in self.threads:
        c.join()

because the server never stops accepting requests. What's more, you don't seem to start the threads, you're just creating them.

Anyway, if you modify the server's run function like this, it should work:

from threading import Thread #this line is at the top of the file, of course

def run(self):
    self.openSocket()
    self.running = True #make sure to implement some code that'll actually set this variable to False!
    while self.running:
        c= client.Client(self.server.accept())
        t= Thread(target=self.listenToClient,args=[c])
        t.daemon= True #this makes sure to kill the thread when the main thread exits
        self.threads.append(t)
        t.start()

    #close threads
    self.server.close()
    for c in self.threads:
        c.join()

def listenToClient(self, c):
    print "Client " + str(c.address) + " connected"
    while self.running:
        try:
            data = c.client.recv(self.size)
            if data=='': #if the client disconnected
                return
            print data
        except socket.error, (value,message):   
            continue

Either I'm completely wrong or I have no clue why your code works on Mac OSX.

P.S: make sure not to use Python 3. That'd require some quirks.