簡體   English   中英

Python線程將無法開始使用套接字

[英]Python thread won't start using socket

我對使用套接字和線程的程序有問題。

我已經做了一個套接字服務器,它在一個線程中添加了客戶端,但是客戶端線程永遠不會啟動...

這是我的代碼:

套接字服務器

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)

很抱歉縮進不好,它是表格,而不是我的文件。

請幫我 :(

在此先感謝您(抱歉英語不好,我是法語。...)

您的Server實例start功能中包含以下代碼行:

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

threading.Threadtarget=參數。 threading.Thread必須是可調用的函數。 這里ClientThread是類ClientThread的構造函數的名稱,因此它可調用的函數,返回該類的實例。 請注意,它實際上尚未被調用! args=參數通常是一個元組,但實際上可以使用列表。 當您使用此特定線程模型時,這些參數將在最終調用后傳遞給target函數。 (您還可以使用kwargs=和字典來傳遞關鍵字參數。)

現在發生的事情有些棘手。 現在已經評估了兩個參數( target=args= ),Python運行時將創建一個新的threading.Thread類實例。 目前,這個新實例只是一個數據對象。

如果我們添加一個print語句/函數(尚不清楚這是py2k還是py3k代碼),我們可以看到對象本身:

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

它將打印類似: 1

new_client id is 34367605072

接下來,將其添加到列表中,然后調用其start

self.CLIENTS.append(new_client)
new_client.start()

列表添加非常簡單,但是start非常棘手。

start調用本身實際上會創建一個新的OS /運行時線程(其ID與數據對象的ID不相關-原始線程ID是內部實現細節)。 這個新線程開始以其run方法run 2默認的run方法實際上是: 3

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 ,即ClientThread類實例創建功能。

因此, 現在 ,在新線程中,Python創建一個ClientThread對象的實例,並使用保存在new_thread實例中的self.__argsself.__kwargs調用它的__init__ (該實例本身在原始Python和新線程之間共享)。

這個新的ClientThread對象執行其__init__代碼並返回。 這等效於讀取run方法:

def run(self):
    ClientThread(**saved_args)

請注意,這不是

def run(self):
    tmp = ClientThread(**saved_args)
    tmp.run()

也就是說, 永遠不會調用 ClientThread實例的run方法。 僅調用threading.Thread實例的run方法。 如果修改ClientThread__init__方法以輸出 ID,您將看到此ID與threading.Thread實例的ID不同:

class ClientThread:
    def __init__(self, client_id, client_ip, client_port, socket):
        print('creating', id(self), 'instance')

它將打印一個不同的ID(並且肯定 new_client id is line 之后打印):

new_client id is 34367605072
creating 34367777464 instance

如果將其他print添加到run方法中,將看到它永遠不會被調用。

該怎么辦

您在這里有兩個主要選擇。

  • 您可以使ClientThread成為threading.Thread子類

     class ClientThread(threading.Thread): def __init__(self, client_id, client_ip, client_port, socket): ... threading.Thread.__init__(self) 

    在這種情況下,您可以自己創建客戶端對象,而不是使用threading.Thread來創建它:

     new_thread = ClientThread(...) ... new_thread.start() 

    .start方法將threading.Thread.start因為你還沒有覆蓋這一點,那么該方法會造成實際的OS /運行線程,然后打電話給你的run方法,該方法,因為你沒有覆蓋它,將是你的 run

  • 或者,您可以創建一個標准的threading.Thread對象,為它提供一個target ,並讓該target調用對象的run方法,例如:

     new_client = ClientThread(...) new_thread = threading.Thread(target=new_client.run, ...) ... new_thread.start() 

    選擇是您的:子類化,或使用單獨的對象。


1實際ID高度依賴於實現。

2它到達此run功能的路徑有些復雜,通過引導代碼進行內部初始化,然后為您調用self.run ,不傳遞任何參數。 您只能保證以某種方式輸入了self.run 您不應該依賴“如何”。

3至少這是Python 2.7和3.4中的代碼; 其他實現可能會略有不同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM