简体   繁体   中英

Python: Synchronize Input and Output Between Threads

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?

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!

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM