通过套接字从另一个线程发送数据在Python中不起作用
这是我的“游戏服务器”。没什么大不了的,我认为这是学习python和套接字的好方法 首先,server类初始化服务器。 然后,当有人连接时,我们创建一个客户端线程。在这个线程中,我们不断地监听我们的套接字 一旦某个命令进入(例如I12345001001),它就会生成另一个线程 最后一个线程的目的是向客户端发送更新。 但是,即使我看到服务器正在执行这段代码,数据实际上并没有被发送 谁能告诉我哪里出了问题? 好像我必须先收到一些东西才能发送。所以我想在某个地方我错过了什么通过套接字从另一个线程发送数据在Python中不起作用,python,sockets,multithreading,Python,Sockets,Multithreading,这是我的“游戏服务器”。没什么大不了的,我认为这是学习python和套接字的好方法 首先,server类初始化服务器。 然后,当有人连接时,我们创建一个客户端线程。在这个线程中,我们不断地监听我们的套接字 一旦某个命令进入(例如I12345001001),它就会生成另一个线程 最后一个线程的目的是向客户端发送更新。 但是,即使我看到服务器正在执行这段代码,数据实际上并没有被发送 谁能告诉我哪里出了问题? 好像我必须先收到一些东西才能发送。所以我想在某个地方我错过了什么 #!/usr/bin/e
#!/usr/bin/env python
"""
An echo server that uses threads to handle multiple clients at a time.
Entering any line of input at the terminal will exit the server.
"""
import select
import socket
import sys
import threading
import time
import Queue
globuser = {}
queue = Queue.Queue()
class Server:
def __init__(self):
self.host = ''
self.port = 2000
self.backlog = 5
self.size = 1024
self.server = None
self.threads = []
def open_socket(self):
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((self.host,self.port))
self.server.listen(5)
except socket.error, (value,message):
if self.server:
self.server.close()
print "Could not open socket: " + message
sys.exit(1)
def run(self):
self.open_socket()
input = [self.server,sys.stdin]
running = 1
while running:
inputready,outputready,exceptready = select.select(input,[],[])
for s in inputready:
if s == self.server:
# handle the server socket
c = Client(self.server.accept(), queue)
c.start()
self.threads.append(c)
elif s == sys.stdin:
# handle standard input
junk = sys.stdin.readline()
running = 0
# close all threads
self.server.close()
for c in self.threads:
c.join()
class Client(threading.Thread):
initialized=0
def __init__(self,(client,address), queue):
threading.Thread.__init__(self)
self.client = client
self.address = address
self.size = 1024
self.queue = queue
print 'Client thread created!'
def run(self):
running = 10
isdata2=0
receivedonce=0
while running > 0:
if receivedonce == 0:
print 'Wait for initialisation message'
data = self.client.recv(self.size)
receivedonce = 1
if self.queue.empty():
print 'Queue is empty'
else:
print 'Queue has information'
data2 = self.queue.get(1, 1)
isdata2 = 1
if data2 == 'Exit':
running = 0
print 'Client is being closed'
self.client.close()
if data:
print 'Data received through socket! First char: "' + data[0] + '"'
if data[0] == 'I':
print 'Initializing user'
user = {'uid': data[1:6] ,'x': data[6:9], 'y': data[9:12]}
globuser[user['uid']] = user
print globuser
initialized=1
self.client.send('Beginning - Initialized'+';')
m=updateClient(user['uid'], queue)
m.start()
else:
print 'Reset receivedonce'
receivedonce = 0
print 'Sending client data'
self.client.send('Feedback: ' +data+';')
print 'Client Data sent: ' + data
data=None
if isdata2 == 1:
print 'Data2 received: ' + data2
self.client.sendall(data2)
self.queue.task_done()
isdata2 = 0
time.sleep(1)
running = running - 1
print 'Client has stopped'
class updateClient(threading.Thread):
def __init__(self,uid, queue):
threading.Thread.__init__(self)
self.uid = uid
self.queue = queue
global globuser
print 'updateClient thread started!'
def run(self):
running = 20
test=0
while running > 0:
test = test + 1
self.queue.put('Test Queue Data #' + str(test))
running = running - 1
time.sleep(1)
print 'Updateclient has stopped'
if __name__ == "__main__":
s = Server()
s.run()
我不理解您的逻辑——特别是,为什么您故意设置两个线程在同一个套接字上同时写入(它们都称为
self.client
),而没有任何同步或协调,这种安排似乎肯定会导致问题
lock = threading.Lock()
def myfunction(arg):
with lock:
arg.do_something()
无论如何,代码中的一个明确错误是您使用了send
方法——您似乎认为它保证发送所有参数字符串,但这肯定不是的情况,请参见:
返回发送的字节数。
应用程序负责
检查所有数据是否已发送;
如果能提供一些数据就好了
传输时,应用程序需要
尝试交付剩余的
数据
是您可能想要的方法:
与send()不同,此方法继续
从字符串发送数据,直到
所有数据已发送或出现错误
发生
其他问题包括
updateClient
显然被设计为永远不会终止(与其他两个线程类不同——当它们终止时,updateClient
实例不会终止,它们只会继续运行并保持进程活动)、冗余的global
语句(无害,只是令人困惑),一些线程试图读取dict(通过iteritems
方法),而其他线程正在更改dict,同样没有任何锁定或协调,等等——我相信可能会有更多的bug或问题,但是,在发现几个bug或问题后,人们的眼睛往往会变得呆滞;-) 您有三个主要问题。第一个问题很可能是你问题的答案
阻塞(问题问题)
socket.recv
正在阻塞。这意味着执行被暂停,线程进入睡眠状态,直到它可以从套接字读取数据为止。因此,您的第三个更新线程只会填满队列,但只有在收到消息时才会清空队列。队列也会一次清空一条消息
这可能就是除非您发送数据,否则它不会发送数据的原因
流上消息协议协议
您正在尝试像消息流一样使用套接字流。我的意思是你有:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
SOCK_流
部分表示它是一个流,而不是SOCK_DGRAM
之类的消息。但是,TCP不支持消息。因此,您需要做的是构建消息,例如:
data =struct.pack('I', len(msg)) + msg
socket.sendall(data)
然后,接收端将查找长度字段并将数据读入缓冲区。一旦缓冲区中有足够的数据,它就可以抓取整个消息
您当前的设置正在运行,因为您的消息足够小,可以全部放入同一个数据包,也可以一起放入套接字缓冲区。但是,一旦您开始使用socket.send
或socket.sendall>通过多个调用发送大数据,您将开始读取多条消息和部分消息,除非您在socket字节流之上实现消息协议
线程
尽管线程在启动时更容易使用,但它们会带来很多问题,如果使用不当,可能会降低性能,尤其是在Python中。我喜欢线,所以不要误会我。Python还存在GIL(全局解释器锁)问题,所以在使用CPU受限的线程时,性能会很差。您的代码目前主要受I/O限制,但将来可能会受CPU限制。您还必须担心线程锁定问题。线程可以快速修复,但可能不是最佳修复。在某些情况下,线程是中断某些耗时进程的最简单方法。因此,不要丢弃邪恶或可怕的线。在Python中,它们被认为是不好的,主要是因为GIL,而在其他语言(包括Python)中,则是因为并发性问题,所以大多数人建议您在Python中使用多个进程或使用异步代码。使用或不使用线程的主题非常复杂,因为它取决于语言(代码的运行方式)、系统(单个或多个处理器)、争用(试图通过锁定共享资源)和其他因素,但通常异步代码更快,因为它使用更多的CPU,开销更少,特别是在不受CPU限制的情况下
解决方案是在Python中使用select
模块或类似的东西。它将告诉您套接字何时有数据要读取,并且您可以设置超时参数
通过执行异步工作(异步套接字),可以获得更高的性能。要将套接字转换为异步模式,只需调用socket.settimeout(0)
,这样它就不会阻塞。然而,在等待数据的过程中,您会不断地消耗CPU的旋转速度。选择
模块和好友将阻止您旋转
一般来说,为了提高性能,您希望尽可能多地执行异步(同一线程),然后扩展更多同样执行异步的线程。然而,正如前面提到的,Python是这个规则的一个例外,因为GIL(全局解释器锁)可以执行actu