Python: синхронизировать ввод и вывод между потоками - PullRequest
3 голосов
/ 21 декабря 2009

В настоящее время я пытаюсь сделать небольшой проект с сокетами в Python, системе чатов для двух пользователей.

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()

(надеюсь, комментарии помогут)

В любом случае, при одновременном вызове ввода и получении сообщения другого сокета у меня возникла небольшая проблема с синхронизацией. Я могу подключиться, но когда я получаю сообщение, оно не отменяет оператор ввода.

Другими словами, когда я получаю сообщение, оно говорит:

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

Чтобы не отменять оператор ввода.

Кроме того, я получаю только каждое другое сообщение.

Есть предложения?

Ответы [ 2 ]

1 голос
/ 21 декабря 2009

Хорошо, извините за такой быстрый ответ на мой вопрос, но обратные вызовы являются ВОЛШЕБНЫМИ при использовании многопоточности (по крайней мере, в модели Linux).

Во всяком случае, сделал это:

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()

Как видите, я добавил цикл сообщений в качестве обратного вызова.

Также обратите внимание, я отправляю нулевое значение, чтобы обойти проблему "любой другой".

То есть, и я использую символ новой строки в конце печати в data_recieve, чтобы отключить символ новой строки.

(Если вам нравится код, он не так хорошо работает в Windows. Это потому, что, по-видимому, модель потоков Python там не выполняется так же неосторожно. Попробуйте его на своем локальном Linux-компьютере)

1 голос
/ 21 декабря 2009

То, что у вас есть, это не столько проблема синхронизации, сколько проблема презентации / пользовательского интерфейса. Я бы посоветовал сделать вашу жизнь проще и выбрать некоторый инструментарий пользовательского интерфейса (curses, wxPython, pyqt) для взаимодействия с пользователем. Использование input () очень удобно для быстрого и грязного одноразового кода, но это не очень сложно.

Если вы сделаете это, вы увидите, что вам вообще не нужно использовать потоки (как это часто бывает), и ваши проблемы исчезнут, как по волшебству!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...