Python套接字聊天:客户端recv()挂起

Python套接字聊天:客户端recv()挂起,python,sockets,networking,Python,Sockets,Networking,我尝试构建的聊天程序有一个小问题: 有两个客户机和一个服务器,一个客户机发送一条消息,服务器接收它,并将它广播给每个人(通常是两个客户机,但可以有更多)。问题是,当一个客户机(通过服务器)向另一个客户机发送数据时,接收客户机必须自己发送一些东西才能看到消息 这是我的server.py: import socket from thread import start_new_thread import threading def thread(c,clients, c_lock,buf=1024

我尝试构建的聊天程序有一个小问题: 有两个客户机和一个服务器,一个客户机发送一条消息,服务器接收它,并将它广播给每个人(通常是两个客户机,但可以有更多)。问题是,当一个客户机(通过服务器)向另一个客户机发送数据时,接收客户机必须自己发送一些东西才能看到消息

这是我的server.py:

import socket
from thread import start_new_thread
import threading


def thread(c,clients, c_lock,buf=1024,):
    c.send("You are connected! \n")
    while True:
        try:
            data = c.recv(buf)
            print data
            if not data:
                break
            broadcast(clients,c_lock,data)
        except socket.error as e:
            exit(0)
    c.close()

def broadcast(clients,c_lock,data):
    with c_lock:
        for c in clients:
            c.sendall("- " + data)

def main():
    host = ''
    port = 10000
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    clients = set()
    clients_lock = threading.Lock()
    s.bind((host, port))
    s.listen(5)
    print "%s listening on %s" % (host, str(port))
    while True:
            c, addr = s.accept()
            print "got connection from ", addr
            with clients_lock:
                clients.add(c)
            start_new_thread(thread, (c, clients, clients_lock, ))
    s.close()

if __name__ == "__main__":
    main()
和client.py:

import socket
import time
from sys import exit

def main():
    host = socket.gethostname()
    port = 10000
    name = str(raw_input("Enter your name: "))


    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect(('192.168.1.12', port))
    except Exception as e:
        print 'Connection not working... %s' % (e)
        exit()

    print s.recv(1024)  # greeting msg
    print "Send q to stop the connection \n"
    out = ''
    while out != 'q':
        out = str(raw_input('->'))
        recv =  s.recv(1024)
        s.send(name + ": " + out)
        time.sleep(0.1)
        print recv
    s.close()
if __name__ == '__main__':
    main()
感谢您的帮助:) (请原谅我弄得一团糟:/)

你有两个问题

recv()
是一个阻塞调用。
raw\u input()
/
input()
也是一个阻塞调用

这意味着客户端将“挂起”这两个调用(
recv()
是异常如果管道中有数据,则不会阻塞)

来绕过这个问题,就像你的例子一样。或者您可以像在服务器中那样使用
线程
,使用一个类/实例从套接字中检索数据,另一个读取用户输入。或者在Linux上,您可以使用
select.select
select.epoll
轮询套接字(如果数据在管道中),然后调用
recv()
)。对于输入,最好使用
select/epoll
on
sys.stdin
或使用线程

但是如果没有可读取的数据,
recv()。
raw\u input()
/
input()
也是如此

下面是一个简短的示例:

from select import epoll, EPOLLIN
import sys

polly = epoll()
polly.register(s.fileno(), EPOLLIN)
polly.register(sys.stdin.fileno(), EPOLLIN)

for fid, eid in polly.poll(1):
    # s is your socket, s.fileno() on linux is the file number that socket has been assigned to.
    # And epoll returns a list of all filenumbers that has data waiting.
    # So we can check if the fid from epoll == the sockets filenumber.
    if fid == s.fileno(): 
        data = s.recv(8192)
    # of if it's sys.stdin's filenumber
    # (stdin is user input, this is where raw_input normally gets its data from)
    elif fid == sys.stdin.fileno():
        user_input = sys.stdin.readline()
        print('User wrote:', user_input)
或者,如果要使用线程,请执行以下操作:

from threading import Thread, enumerate as t_enum
class userInput(Thread):
    def __init__(self, socket, username):
        Thread.__init__(self)
        self.socket = socket
        self.username = username
        self.start()

    def run(self):
        mainThread = None
        for thread in t_enum():
            if thread.name == 'MainThread':
                mainThread = thread
                break

        while mainThread and mainThread.isAlive():
            out = raw_input('->') # str() is redundant, it's type(str) already.
            self.socket.send(self.username + ": " + out)
再往下看你的代码:

name = str(raw_input("Enter your name: "))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    s.connect(('192.168.1.12', port))
except Exception as e:
    print 'Connection not working... %s' % (e)
    exit()

userInput(s, name)
请注意,这不是正确的线程处理。但这只是一个很小的例子,它将向你展示如何解决这个问题的一般要点

作为最后一点,也是一个不错的选择

未来提示的进一步编程 使用
print(…)
而不是
print…

使用
“连接不工作…”。。。{}.格式(e)
而不是连接不工作…%s“%”(e)

总的来说,尝试使用Python3而不是Python2。只有在旧环境中充满了遗留内容(如:debian、旧开发的应用程序等)的情况下,才应该真正使用Python2

recv()
是一个阻塞调用。
raw\u input()
/
input()
也是一个阻塞调用

这意味着客户端将“挂起”这两个调用(
recv()
是异常如果管道中有数据,则不会阻塞)

来绕过这个问题,就像你的例子一样。或者您可以像在服务器中那样使用
线程
,使用一个类/实例从套接字中检索数据,另一个读取用户输入。或者在Linux上,您可以使用
select.select
select.epoll
轮询套接字(如果数据在管道中),然后调用
recv()
)。对于输入,最好使用
select/epoll
on
sys.stdin
或使用线程

但是如果没有可读取的数据,
recv()。
raw\u input()
/
input()
也是如此

下面是一个简短的示例:

from select import epoll, EPOLLIN
import sys

polly = epoll()
polly.register(s.fileno(), EPOLLIN)
polly.register(sys.stdin.fileno(), EPOLLIN)

for fid, eid in polly.poll(1):
    # s is your socket, s.fileno() on linux is the file number that socket has been assigned to.
    # And epoll returns a list of all filenumbers that has data waiting.
    # So we can check if the fid from epoll == the sockets filenumber.
    if fid == s.fileno(): 
        data = s.recv(8192)
    # of if it's sys.stdin's filenumber
    # (stdin is user input, this is where raw_input normally gets its data from)
    elif fid == sys.stdin.fileno():
        user_input = sys.stdin.readline()
        print('User wrote:', user_input)
或者,如果要使用线程,请执行以下操作:

from threading import Thread, enumerate as t_enum
class userInput(Thread):
    def __init__(self, socket, username):
        Thread.__init__(self)
        self.socket = socket
        self.username = username
        self.start()

    def run(self):
        mainThread = None
        for thread in t_enum():
            if thread.name == 'MainThread':
                mainThread = thread
                break

        while mainThread and mainThread.isAlive():
            out = raw_input('->') # str() is redundant, it's type(str) already.
            self.socket.send(self.username + ": " + out)
再往下看你的代码:

name = str(raw_input("Enter your name: "))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    s.connect(('192.168.1.12', port))
except Exception as e:
    print 'Connection not working... %s' % (e)
    exit()

userInput(s, name)
请注意,这不是正确的线程处理。但这只是一个很小的例子,它将向你展示如何解决这个问题的一般要点

作为最后一点,也是一个不错的选择

未来提示的进一步编程 使用
print(…)
而不是
print…

使用
“连接不工作…”。。。{}.格式(e)
而不是连接不工作…%s“%”(e)


总的来说,尝试使用Python3而不是Python2。只有在旧环境中充满了遗留内容(如debian、旧开发的应用程序等)时,才应该真正使用Python2。

您的客户大部分时间都花在
raw\u input()
,不可能收到任何东西。如果您在单独的线程中进行输入/发送和接收,事情会正常进行,但是如果用户在键入时收到消息,那么结果将是屏幕上一片混乱。你真的需要一个图形用户界面,或者一个像“诅咒”这样的文本界面,这样用户输入和传入的消息就在屏幕的不同区域,不会相互干扰。好吧,那么你告诉我的是,我应该等待另一个客户端的响应吗?@LiorDahan不,他说你应该使用一个可以在显示输出的同时处理输入的界面。诅咒或gui。或者,当数据可用时,您可以一次读取一个字符,打印接收到的数据并重新打印当前用户输入状态。但实际上,如果您在linux上,请使用。如果您使用的是windows,只需使用tkinter/wxPython/qt/gtk+或任何其他windows框架即可。或者只是使用线程,暂时使用加扰的输入/输出。您的客户端大部分时间都花在
raw\u input()
,不可能接收任何内容。如果您在单独的线程中进行输入/发送和接收,事情会正常进行,但是如果用户在键入时收到消息,那么结果将是屏幕上一片混乱。您确实需要GUI或文本界面,如
curses
,这样用户输入和传入的消息就在屏幕的不同区域,不能相互干扰。好吧,您告诉我的是,我应该等待另一个客户端的响应?@LiorDahan No,他说你应该使用一个可以在显示输出的同时处理输入的界面。诅咒或gui。或者,当数据可用时,您可以一次读取
sys.stdin
一个字符,打印收到的