Python套接字聊天:客户端recv()挂起
我尝试构建的聊天程序有一个小问题: 有两个客户机和一个服务器,一个客户机发送一条消息,服务器接收它,并将它广播给每个人(通常是两个客户机,但可以有更多)。问题是,当一个客户机(通过服务器)向另一个客户机发送数据时,接收客户机必须自己发送一些东西才能看到消息 这是我的server.py: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
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
onsys.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
onsys.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
一个字符,打印收到的