【问题标题】:TypeError: context must be specified" in zmq unpicklingTypeError: context must be specified" in zmq unpickling
【发布时间】:2016-08-20 17:09:47
【问题描述】:

我正在尝试创建一个非常简单的聊天。使用 PyZMQ 库。由于套接字不是线程安全的,我使用两个套接字和一个线程在每个套接字上运行。一个检查传入消息,一个检查发送消息。

但是我的程序给了我错误信息:

    Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Python27\lib\multiprocessing\forking.py", line 381, in main
    self = load(from_parent)
  File "C:\Python27\lib\pickle.py", line 1384, in load
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Python27\lib\multiprocessing\forking.py", line 381, in main
    self = load(from_parent)
  File "C:\Python27\lib\pickle.py", line 1384, in load
    return Unpickler(file).load()
  File "C:\Python27\lib\pickle.py", line 864, in load
    return Unpickler(file).load()
  File "C:\Python27\lib\pickle.py", line 864, in load
    dispatch[key](self)
  File "C:\Python27\lib\pickle.py", line 1089, in load_newobj
    dispatch[key](self)
  File "C:\Python27\lib\pickle.py", line 1089, in load_newobj
    obj = cls.__new__(cls, *args)
  File "zmq\backend\cython\socket.pyx", line 279, in zmq.backend.cython.socket.Socket.__cinit__ (zmq\backend\cython\socket.c:3456)
TypeError: context must be specified
    obj = cls.__new__(cls, *args)
  File "zmq\backend\cython\socket.pyx", line 279, in zmq.backend.cython.socket.Socket.__cinit__ (zmq\backend\cython\socket.c:3456)
TypeError: context must be specified

我不知道为什么我会得到它们,或者如何解决它。

这也是我的逻辑错了吗?:

我们启动创建套接字并将其绑定到端口的服务器。然后它侦听该套接字以获取消息/连接。然后我们创建另一个套接字并将其绑定到同一个端口。我们创建了一个新线程,以便我们等待在第一个套接字上接收到消息,然后将其发送到第二个套接字。 然后我们有一个连接到第一个套接字的客户端。我们为它创建一个新线程,以便它可以在另一个套接字上侦听传入消息,因此它可以使用第一个线程通过第一个套接字发送消息。

server.py

from communication import Communication
from multiprocessing import Process
import zmq
import random
import sys
import time

if __name__ == '__main__':

    if len(sys.argv) > 1:
        port = sys.argv[1]
    else:
        port = "5556"

    c = Communication(port)
    c.bind()
    recieverP = Process(target=c.reciever)
    recieverP.start()
    print("first process")

    c2 = Communication(port)
    c2.connect()
    senderP = Process(target=c2.sender)
    senderP.start()
    print("second process")

client.py

from communication import Communication
from multiprocessing import Process
import zmq
import random
import sys
import time

if __name__ == '__main__':

    if len(sys.argv) > 1:
        port = sys.argv[1]
    else:
        port = "5556"

    c = Communication(port)
    c.connect()


    recieverP = Process(target=c.reciever, args=())
    senderP = Process(target=c.sender,args=())
    recieverP.start()
    senderP.start()

communications.py

import zmq

class Communication:
    def __init__(self, port):
        context = zmq.Context.instance()
        self.port = port
        self.socket = context.socket(zmq.PAIR)

    def connect(self):
        self.socket.connect("tcp://localhost:%s" % self.port)

    def bind(self):
        self.socket.bind("tcp://*:%s" % self.port)

    def sender(self):
        while True:
            msg = raw_input("> ")
            self.socket.send(msg)

    def reciever(self):
        while True:
            msg = self.socket.recv()
            print("> " + msg)

【问题讨论】:

  • ...此外,错误应在问题中以文本形式给出,而不是在场外托管的图像(问题需要直接在问题本身中包含错误认为完整)。 Microsoft 的终端确实 允许复制/粘贴,因此您没有理由无法访问该原始文本。
  • @CharlesDuffy 你是对的。我将编辑错误。我不知道给我的问题取什么标题,因为我不知道它会导致什么错误,TypeError
  • "TypeError: context must be specified" in zmq unpickling
  • ...听起来沙丘有一个答案,可以完全避免酸洗/解酸;使您的对象可腌制的答案也是可能的。
  • @CharlesDuffy 谢谢!我编辑了我的帖子。是的,我希望这会奏效。

标签: python multithreading sockets chat pyzmq


【解决方案1】:

下面的完整工作版本。我做了一些更改:

  1. 按照@Dunes 的建议,我使用线程而不是进程。
  2. 我将client.pyserver.py 组合成一个带有“角色”参数的app.py。 (这只是为了避免大量重复代码。)
  3. 我在主线程中处理了KeyboardExceptions,所以你可以Ctrl+C退出。
  4. 我删除了行中的“>”前缀,因为它会导致输出混乱。 (当您在其他应用程序中输入内容时,您会得到类似“> > hello”的输出。)
  5. 我取出了server.py中的c2。我不确定这样做的目的是什么,也不知道这是否可行。 (好像你是connecting 而不是binding?)
  6. 我修正了“receiver”的拼写,因为它让我很困扰。 :-)
  7. 我将port 设为int 而不是str,因为它是。 :-)

app.py:

import argparse
from threading import Thread
import time

from communication import Communication

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("role", help="either 'client' or 'server'", choices=['client', 'server'])
    parser.add_argument("--port", "-p", type=int, help="port number", default=5556)
    args = parser.parse_args()

    c = Communication(args.port)
    if args.role == 'client':
        c.connect()
    else:
        c.bind()

    receiverP = Thread(target=c.receiver)
    senderP = Thread(target=c.sender)
    receiverP.daemon = True
    senderP.daemon = True
    try:
        receiverP.start()
        senderP.start()
        while True:
            time.sleep(100)
    except (KeyboardInterrupt, SystemExit):
        pass

communication.py:

import zmq

class Communication:
    def __init__(self, port):
        self.port = port
        context = zmq.Context.instance()
        self.socket = context.socket(zmq.PAIR)

    def connect(self):
        self.socket.connect("tcp://localhost:%d" % self.port)

    def bind(self):
        self.socket.bind("tcp://*:%d" % self.port)

    def sender(self):
        while True:
            self.socket.send(raw_input())

    def receiver(self):
        while True:
            print(self.socket.recv())

【讨论】:

  • 首先,谢谢!其次:1.所以这一切都归结为处理器不能序列化?有没有可能用进程而不是线程来解决这个问题? 5. 我有c2 这样可以打开另一个套接字并在其上运行我的一个线程。是的,在那种情况下,也许我应该让binded 他们俩?或者也许我的逻辑是错误的:)。
  • 我认为不可能像您那样使用多个进程,因为每个进程都需要访问同一个 0mq 套接字(而且我看不出如何在两者之间共享套接字进程)。
  • 迟到的问题:你为什么将两个线程都用作守护线程?我的猜测是我们正在运行 3 个线程,一个用于sender,一个用于reciever,一个用于运行我们的程序。因此,当我们关闭程序(因此,“主”线程)时,其余的将关闭。但我不确定我们是否真的在做 3 个线程?如果我们只做 2 个线程,那么其中只有一个需要是守护线程,对吧?
  • 确实有三个线程。我创建了这两个守护线程,所以当主线程退出时程序会退出。来自文档:“一个线程可以被标记为一个'守护线程'。这个标志的意义是当只剩下守护线程时整个 Python 程序退出。”
【解决方案2】:

这些是进程而不是线程。出现问题是因为在后台 Python 必须将您的 Communication 对象的副本发送到子进程。但是,您的对象包含无法序列化的套接字对象。使用threadingThread 对象代替Process,您就不会遇到这个问题。这是因为线程在同一个进程中运行。

【讨论】:

  • 谢谢。我尝试改用recieverP = threading.Thread(target=c.reciever)。现在,至少,我能够创建我的线程。但我现在收到另一个错误:Internal assertation: nBytes ==.....。我用谷歌搜索,发现了这个:github.com/zeromq/libzmq/issues/1573。因此,如果我在同一个套接字上使用多个线程,似乎会发生此错误。但我不会那样做,对吧?我在这里创建一个新的实例:c2 = Communication(port),因此是一个新的套接字。虽然它们连接到同一个端口。会不会是这个问题?
  • @FelixRosén,线程与其父进程共享文件描述符表。如果一个线程打开一个文件,其他所有线程(及其父线程)也会打开它。
  • @CharlesDuffy 好的!但是我怎样才能让两个线程在两个不同的套接字上运行呢?也许我毕竟需要使用multiprocess 包。
猜你喜欢
  • 2014-11-22
  • 1970-01-01
  • 2021-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多