Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
通过套接字从另一个线程发送数据在Python中不起作用_Python_Sockets_Multithreading - Fatal编程技术网

通过套接字从另一个线程发送数据在Python中不起作用

通过套接字从另一个线程发送数据在Python中不起作用,python,sockets,multithreading,Python,Sockets,Multithreading,这是我的“游戏服务器”。没什么大不了的,我认为这是学习python和套接字的好方法 首先,server类初始化服务器。 然后,当有人连接时,我们创建一个客户端线程。在这个线程中,我们不断地监听我们的套接字 一旦某个命令进入(例如I12345001001),它就会生成另一个线程 最后一个线程的目的是向客户端发送更新。 但是,即使我看到服务器正在执行这段代码,数据实际上并没有被发送 谁能告诉我哪里出了问题? 好像我必须先收到一些东西才能发送。所以我想在某个地方我错过了什么 #!/usr/bin/e

这是我的“游戏服务器”。没什么大不了的,我认为这是学习python和套接字的好方法

首先,server类初始化服务器。 然后,当有人连接时,我们创建一个客户端线程。在这个线程中,我们不断地监听我们的套接字

一旦某个命令进入(例如I12345001001),它就会生成另一个线程

最后一个线程的目的是向客户端发送更新。 但是,即使我看到服务器正在执行这段代码,数据实际上并没有被发送

谁能告诉我哪里出了问题? 好像我必须先收到一些东西才能发送。所以我想在某个地方我错过了什么


#!/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