我的程序使用套接字和线程有问题。
我已经创建了一个在线程中添加客户端的套接字服务器,但客户端线程永远不会启动...
这是我的代码:
套接字服务器
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)
抱歉收缩不好,这是表格,不是我的档案。
请帮帮我:(
提前谢谢(对不起英语不好我是法国人......)
答案 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中的代码;其他实现可能略有不同。