3

Currently, I am trying to do a small project with sockets in Python, a two-user chatting system.

import socket
import threading

#Callback. Print doesn't work across threads
def data_recieved(data):
    print data

#Thread class to gather input
class socket_read(threading.Thread):
    sock = object
    def __init__(self, sock):
        threading.Thread.__init__(self)
        self.sock = sock
    def run(self):
        while True:
            data = self.sock.recv(1000)
            if (data == "\quitting\\"):
                return
            data_recieved(self.sock.recv(1000))

####################################################################################
server = False
uname = input("What's your username: ")
print "Now for the technical info..."
port = input("What port do I connect to ['any' if first]: ")
#This is the first client. Let it get an available port
if (port == "any"):
    server = True
    port = 9999
    err = True
    while err == True:
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.bind(('', port))
            err = False
        except:
            err = True
        sock.close()

    print "Bound to port #" + str(port)
    print "Waiting for client..."

    sock.listen(1)
    (channel, info) = sock.accept()
else:
    #This is the client. Just bind it tho a predisposed port
    host = input("What's the IP of the other client: ")
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, int(port)))

msg = ""
if (server == True):
    #Use the connection from accept
    reader = socket_read(channel)
else:
    #Use the actual socket
    reader = socket_read(sock)
reader.start()
while msg != 'quit':
    #Get the message...
    msg = uname + ": " + input("Message: ")
    try:
        #And send it
        if (server == True):
            #Use the connection from accept
            channel.send(msg)
        else:
            #Use direct socket
            sock.send(msg)
    except:
        break
reader.join()
channel.send("\quitting\\")
sock.close()

(I hope the comments help)

Anyhow, by calling for input at the same time, and getting the other socket's message, I've got a small syncronization issue. I can connect, but when I recieve a message, it doesn't cancel the input statement.

In other words, when I recieve a message, it says this

Message: user: I got a message
#Flashing cursor here 

So that it doesn't cancel the input statement.

Also, I only get every other message.

Any suggestions?

2
  • Minor notes: "received" is spelled wrong, the "callback" you are using is not only not a callback (it's just a regular function call) but it's useless... replace the call with print and you'll get the same results. Lastly, input() is dangerous because it calls eval() on the user input: far safer to use raw_input() instead, which does what you probably think input() does. Commented Dec 21, 2009 at 15:10
  • Thanks for the idea. I just ran into a comma bug, which raw fixed. Commented Dec 21, 2009 at 15:34

2 Answers 2

1

What you have here is not so much a synchronization issue as it is a presentation/UI issue. I would suggest making your life easier and picking some UI toolkit (curses, wxPython, pyqt) to handle interaction with the user. Using input() is very handy for quick-and-dirty one-off code, but it is not very sophisticated.

If you do this you will see you do not need to use threads at all (as is often the case), and your problems will go away as if by magic!

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the idea. However, the sockets is the reason to multithread, not the user interaction. I needed to monitor for input and send output instantaneously, not send and wait for reply.
You shouldn't have to use threads for the sockets either. For example, using QT you can get a signal when there is data waiting on the socket OR on the user input front. Then you can handle it. The event loop takes care of everything else. What you are doing here in very error-prone, as you are finding out.
1

Alright, sorry for such a quick answer to my own question, but callbacks are MAGICAL when using threading (at least on Linux's model).

Anyhow, did this:

import socket
import threading

def msg_loop(socket):
    msg = ""
    if (server == True):
        reader = socket_read(channel)
    else:
        reader = socket_read(sock)
    reader.start()
    while msg != 'quit':
        msg = uname + " said : " + input("Message: ")
        print ""
        try:
            if (server == True):
                channel.send('null')
                channel.send(msg)
            else:
                sock.send('null')
                sock.send(msg)
        except:
            break

def data_recieved(data, socket):
    print "Hold on...\n\n" + data + "\n"
    msg_loop(socket)

class socket_read(threading.Thread):
    sock = object
    def __init__(self, sock):
        threading.Thread.__init__(self)
        self.sock = sock
    def run(self):
        while True:
            data = self.sock.recv(1000)
            if (data == "\quitting\\" or data == ''):
                return
            data_recieved(self.sock.recv(1000), self.sock)

####################################################################################
server = False
uname = str(input("What's your username: "))
print "Now for the technical stuff..."
port = input("What port do I connect to ['any' if first]: ")
if (port == "any"):
    server = True
    port = 9999
    err = True
    while err == True:
        try:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.bind(('', port))
            err = False
        except:
            print "Socket #" + str(port) + " failed"
            err = True
            sock.close()
            port -= 1

    print "Bound to port #" + str(port)
    print "Waiting for client..."

    sock.listen(1)
    (channel, info) = sock.accept()
else:
    host = input("What's the IP of the other client: ")
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, int(port)))

if (server == True):
    msg_loop(channel)
else:
    msg_loop(sock)

reader.join()
channel.send("\quitting\\")
sock.close()

As you can see, I added the message loop as a callback.

Also note, I send a null value, to circumvent the "every other" problem.

That, and I use a newline at the end of printing in data_recieved to disable the newline.

(If you like the code, it doesn't run as well on Windows. This is because, apparently, Python's threading model on there doesn't execute as instentaniously. Try it on your local Linux box)

1 Comment

That doesn't look right. If you keep calling into data_recieved and socket_read without returning, you will crash out of your stack eventually.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.