Python线程不会开始使用套接字

时间:2016-08-21 23:26:53

标签: python multithreading sockets

我的程序使用套接字和线程有问题。

我已经创建了一个在线程中添加客户端的套接字服务器,但客户端线程永远不会启动...

这是我的代码:

套接字服务器

import socket, threading, logging, sys
from client_thread import ClientThread

class SocketServer:
    CLIENTS = list()

    def __init__(self, server_ip, server_port, max_connections):
        try:
            self.tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.tcpsock.bind((server_ip, server_port))
            self.tcpsock.listen(10)

            logging.info('Socket server successfully started !')
    except Exception as e:
            logging.error(format(e))

    def start(self):
        from src.realmserver.core.hetwan import EmulatorState, Core

        while (Core.STATE == EmulatorState.IN_RUNNING):
            try:
                (clientsock, (ip, port)) = self.tcpsock.accept()
                new_client = threading.Thread(target=ClientThread, args=[len(self.CLIENTS), ip, port, clientsock])
                self.CLIENTS.append(new_client)
                new_client.start()
            except Exception as e:
                print format(e)

        for client in self.CLIENTS:
            client.join()

和客户端线程

import logging, string, random

class ClientThread:
    def __init__(self, client_id, client_ip, client_port, socket):
        self.client_id = client_id
        self.client_ip = client_ip
        self.client_port = client_port
        self.socket = socket
        logging.debug('(%d) Client join us !', client_id)

    def run(self):
        key = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(32))

        print self.send('HC%s' % key)

        while True:
            entry = self.socket.recv(4096)
            entry.replace("\n", "")

            if not entry:
                break
            else:
                logging.debug('(%d) Packet received : %s', self.client_id, str(entry))

        self.kill()

    def send(self, packet):
        return self.socket.send("%s\x00" % packet)

    def kill(self):
        self.socket.close()
        logging.debug('(%d) Client is gone...', self.client_id)
抱歉收缩不好,这是表格,不是我的档案。

请帮帮我:(

提前谢谢(对不起英语不好我是法国人......)

1 个答案:

答案 0 :(得分:4)

您的Server实例start函数中包含以下代码:

new_client = threading.Thread(target=ClientThread,
     args=[len(self.CLIENTS), ip, port, clientsock])

target=的{​​{1}}参数需要是可调用的函数。这里threading.Thread是类ClientThread的构造函数的名称,因此 是一个可调用的函数,返回该类的实例。请注意,它实际上还没有被调用! ClientThread参数通常是一个元组,但列表实际上是有效的。当您使用此特定线程模型时,这些参数将在最终调用时传递给args=函数。 (您也可以使用target和字典传递关键字参数。)

现在发生的事情有点棘手。现在已经评估了两个参数(kwargs=target=),Python运行时创建了一个args=类的新实例。此新实例目前只是 数据对象。

如果我们添加threading.Thread语句/函数(不清楚这是py2k还是py3k代码),我们可以看到对象本身:

print

将打印如下内容: 1

print('new_client id is', id(new_client))

接下来,将其添加到列表中,然后调用其new_client id is 34367605072

start

列表添加很简单,但self.CLIENTS.append(new_client) new_client.start() 非常棘手。

start调用本身实际上创建了一个新的OS /运行时线程(其ID与数据对象的ID无关 - 原始线程ID是内部实现细节)。这个新线程开始以start方法运行。 2 默认run方法实际上是: 3

run

由于您使用的是常规try: if self.__target: self.__target(*self.__args, **self.__kwargs) finally: # Avoid a refcycle if the thread is running a function with # an argument that has a member that points to the thread. del self.__target, self.__args, self.__kwargs 实例对象,因此您将获得此默认行为,其中threading.Thread创建新线程,然后调用默认的new_thread.start()方法,该方法调用它的run是你的self.__target类实例创建函数。

所以现在,在新线程中,Python创建了一个ClientThread对象的实例,使用ClientThread和{{1}调用其__init__保存在self.__args实例中(它本身在原始Python和新线程之间共享)。

这个新的self.__kwargs对象执行其new_thread代码并返回。这相当于读取ClientThread方法:

__init__

请注意,这是

run

也就是说,def run(self): ClientThread(**saved_args) 实例def run(self): tmp = ClientThread(**saved_args) tmp.run() 方法永远不会被称为。仅调用run实例的ClientThread方法。如果您修改run的{​​{1}}方法以打印 ID,您会看到此ID与threading.Thread实例的ID不同:

ClientThread

将打印不同的ID(并且 __init__行后肯定打印):

threading.Thread

如果您在class ClientThread: def __init__(self, client_id, client_ip, client_port, socket): print('creating', id(self), 'instance') 方法中添加其他new_client id is,则会看到它永远不会被调用。

该怎么做

这里有两个主要选项。

  • 您可以制作new_client id is 34367605072 creating 34367777464 instance 的{​​{1}} 子类

    print

    在这种情况下,您将自己创建客户端对象,而不是使用run来创建它:

    ClientThread

    threading.Thread方法将是class ClientThread(threading.Thread): def __init__(self, client_id, client_ip, client_port, socket): ... threading.Thread.__init__(self) ,因为您没有覆盖它,然后该方法将创建实际的OS /运行时线程,然后调用您的threading.Thread方法,你做了覆盖它 - 将你的 new_thread = ClientThread(...) ... new_thread.start()

  • 或者,您可以创建标准.start对象,为其提供threading.Thread.start,并让run调用对象的run方法,例如:

    threading.Thread

    选择权在于您:子类,或使用单独的对象。

1 实际ID高度依赖于实现。

2 它到达此target函数的路径有些复杂,通过执行某些内部初始化的引导代码,然后为您调用target,传递否参数。你只承诺以某种方式输入run;你不应该依赖“如何”。

3 至少,这是Python 2.7和3.4中的代码;其他实现可能略有不同。