我正在尝试构建的聊天程序存在一个小问题: 有2个客户端和一个服务器,客户端发送消息,服务器接收它,并将其广播给每个人(通常是两个客户端,但可以有更多)。问题是,当客户端(通过服务器)向另一个客户端发送数据时,接收客户端必须自己发送一些信息才能看到该消息。
这是我的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()
感谢您的帮助:) (请原谅我这个烂摊子:/)
答案 0 :(得分:2)
你有两个问题。
recv()
是阻止通话
raw_input()
/ input()
也是阻止通话。
意味着客户端将“挂起”这两个调用(recv()
是异常如果管道中有数据,那么它将不会阻塞)。
要解决这个问题,就像在你的例子中一样。您可以在服务器中使用threading
来创建一个类/实例,它负责从套接字中检索数据,一个用于读取用户输入。或者在Linux上,如果数据在管道中,您可以使用select.select
或select.epoll
轮询套接字,然后调用recv()
。对于输入,您最好在select/epoll
上使用sys.stdin
或使用线程。
但如果没有可供读取的数据,recv()
将永远是Python(以及许多其他语言)的阻塞调用。 raw_input()
/ input()
也是如此。
使用epoll,这是一个简短的例子:
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)
请注意,这不是正确的线程处理。但这是一个最小的例子,它将向您展示如何解决问题的一般要点。
作为最后一个选项,fcntl也是一个不错的选择。
使用print(...)
代替print ...
使用'Connection not working... {}'.format(e)
代替'Connection not working... %s' % (e)
总的来说,尝试使用Python3而不是Python2。如果你的旧环境充满了遗留的东西(阅读:debian,旧开发的应用程序等),那么Python2应该只被使用。