Python multiprocessing报NameError: name 'server'未定义
错误产生原因
- Python
multiprocessing在Windows、macOS系统下默认使用spawn模式创建子进程:子进程启动时会重新从头导入主脚本,不会执行if __name__ == "__main__"判断块内的代码,你在这个块里定义的server变量根本不会在子进程中被创建,add_users函数运行时自然找不到server,抛出NameError。 - 你代码里声明的
global users、global client等全局变量也存在同样问题:就算解决了server的定义问题,子进程里这些变量也没有被初始化,会继续抛同类错误。 - 代码里还有两处基础逻辑错误:一是
server.listen(100)不需要放在while True循环里重复调用,整个服务生命周期只需要执行一次;二是for t in len(users)写法不合法,len()返回整数,不能直接用于迭代。 - 多进程之间内存完全隔离,就算你在各子进程里分别定义了同名的
users变量,也是各自独立的副本,一个进程新增的连接用户不会同步到另一个进程,直接用全局变量没法实现数据共享。
修复方案
核心原则:子进程依赖的资源要么在子进程内部初始化,要么通过函数参数显式传入;跨进程共享数据必须用multiprocessing提供的进程安全组件,不能用普通全局变量。
- 把socket初始化、监听逻辑移到
add_users所属的子进程内部执行,不需要在主进程提前创建server对象。 - 用
multiprocessing.Queue作为进程间通信的通道,add_users进程接到新客户端连接后,把连接信息放到队列里,mid进程从队列读取连接信息维护用户列表。 - 修正原有语法错误,把递归实现的输入循环改成
while True写法避免栈溢出。
修正后的可运行核心代码如下:
import socket import os import time import multiprocessing import colorama from colorama import colored def add_users(users_queue, host, port): # 子进程内部初始化服务端socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind((host, port)) server.listen(100) # listen仅需调用一次 print(colored('[+] Listening For Client Connection ...', 'magenta')) while True: client, client_addr = server.accept() print(colored(f'[+] {client_addr} Client connected to the server', 'yellow')) users_queue.put([client, client_addr]) # 新连接写入进程安全队列 def exec_cmd(c, cA): # 原有命令执行逻辑 pass def mid(users_queue): users = [] while True: # 先从队列拉取所有新连接,更新本地用户列表 while not users_queue.empty(): users.append(users_queue.get()) a = input(colored("#-> ", 'green')) if a == 'ls': if len(users) != 0: print(colored(users, 'yellow')) else: print(colored("No user connected.", 'yellow')) elif a.split(' ')[0] == 'connect': if len(a.split(' ')) < 2: print(colored("Please input target address", 'yellow')) continue target_addr = a.split(' ')[1] for user in users: if str(user[1]) == target_addr: exec_cmd(user[0], user[1]) break else: print(colored("Target user not connected", 'yellow')) if __name__ == "__main__": colorama.init() os.system('color') # 打印启动banner print(colored(' ______ __ __ __ __ ______ __ __ ______ _____ ', 'red')) time.sleep(1) print(colored('/\ == \ /\ \_\ \ /\ "-./ \ /\ __ \ /\ \/\ \ /\ __ \ /\ __-. ', 'red')) time.sleep(1) print(colored('\ \ __< \ \____ \ \ \ \-./\ \ \ \ \/\ \ \ \ \_\ \ \ \ __ \ \ \ \/\ \ ', 'red')) time.sleep(1) print(colored(' \ \_____\ \/\_____\ \ \_\ \ \_\ \ \_____\ \ \_____\ \ \_\ \_\ \ \____- ', 'red')) time.sleep(1) print(colored(' \/_____/ \/_____/ \/_/ \/_/ \/_____/ \/_____/ \/_/\/_/ \/____/ \n', 'red')) HOST = '0.0.0.0' PORT = 8081 print(colored('[+] Server Started', 'magenta')) # 初始化进程安全队列,用于跨进程传递用户连接信息 users_queue = multiprocessing.Queue() # 启动两个子进程,通过args传入需要的参数 p1 = multiprocessing.Process(target=mid, args=(users_queue,)) p2 = multiprocessing.Process(target=add_users, args=(users_queue, HOST, PORT)) p1.start() p2.start() # 等待子进程结束 p1.join() p2.join()
补充说明:不要在
if __name__ == "__main__"块内使用global声明变量,该块本身处于模块顶层作用域,直接赋值就会生成模块级全局变量,但spawn模式下子进程不会执行该块代码,因此这里定义的变量无法被子进程读取。
内容的提问来源于stack exchange,提问作者Mouad Essalim
相关产品推荐
相关产品推荐

