Felix Rosén Felix Rosén - 3 months ago 21
Python Question

TypeError: context must be specified" in zmq unpickling

I am trying to create a very simple chat. Using the PyZMQ library. And since sockets are not threadsafe I am using two sockets and one thread running on each. One checking for incoming messages, and one to send messages.

But my program is giving me, error messages:

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


I can not figure out why I am getting them, or how to solve it.

Also is my logic wrong here?:


We start the server which creates a socket and binds it to a port. Then It listens to that sockets for messages/connections. Then we create another socket and binds it to the same port. We create a new thread that makes it so that we wait for messages to be recieved on the first socket to then be sent to the second one.
Then we have a client who connects to the first socket. We create a new thread for it so it can listen on the other socket for incoming messages, and thus it can use the first thread to send messages through the first socket.


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)

Answer

Fully working version below. I made a few changes:

  1. I used threads instead of processes, as suggested by @Dunes.
  2. I combined client.py and server.py into a single app.py with a "role" parameter. (This was just to avoid a lot of duplicate code.)
  3. I handled KeyboardExceptions in the main thread so you can Ctrl+C to exit.
  4. I dropped the "> " prefix on lines, since it led to confusing output. (When you type something in the other app, you'd get output like "> > hello".)
  5. I took out the c2 that was in server.py. I'm not sure what the purpose of that was, and I don't know if that's expected to work. (Seems like you were connecting instead of binding?)
  6. I fixed the spelling of "receiver" everywhere because it was bothering me. :-)
  7. I made port an int instead of a str, because it is. :-)

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())
Comments