LoopingCall AMP commands - python

Why do I get an error while trying to implement LoopingCall function calling AMP commands?
from twisted.protocols.amp import AMP
from twisted.python.log import startLogging, err
from twisted.internet.task import LoopingCall
from twisted.internet import reactor
from sys import stdout
import commands
startLogging(stdout)
class MyAMP:
def __init__(self, host, port):
destination = TCP4ClientEndpoint(reactor, host, port)
self.protocol = AMP()
self.d = connectProtocol(destination, self.protocol)
def say(self):
return self.protocol.callRemote(commands.Say,
phrase='Hello world')
def loop(myamp):
myamp.say()
def main(host, port):
myamp = MyAMP(host, port)
lc = LoopingCall(loop, myamp=myamp)
lc.start(4.0)
reactor.run()
main('127.0.0.1', 12345)
Error while calling myamp.say() within loop:
2013-08-16 12:28:58-0400 [-] Starting factory <twisted.internet.endpoints.OneShotFactory instance at 0x92273ec>
2013-08-16 12:28:58-0400 [-] Unhandled error in Deferred:
2013-08-16 12:28:58-0400 [-] Unhandled Error
Traceback (most recent call last):
File "lib/client.py", line 35, in <module>
main('127.0.0.1', 12345)
File "lib/client.py", line 32, in main
lc.start(4.0)
File "/usr/local/lib/python2.7/site-packages/twisted/internet/task.py", line 173, in start
self()
File "/usr/local/lib/python2.7/site-packages/twisted/internet/task.py", line 218, in __call__
d = defer.maybeDeferred(self.f, *self.a, **self.kw)
--- <exception caught here> ---
File "/usr/local/lib/python2.7/site-packages/twisted/internet/defer.py", line 137, in maybeDeferred
result = f(*args, **kw)
File "lib/client.py", line 26, in loop
myamp.say()
File "lib/client.py", line 22, in say
phrase='Hello world')
File "/usr/local/lib/python2.7/site-packages/twisted/protocols/amp.py", line 821, in callRemote
return co._doCommand(self)
File "/usr/local/lib/python2.7/site-packages/twisted/protocols/amp.py", line 1778, in _doCommand
self.requiresAnswer)
File "/usr/local/lib/python2.7/site-packages/twisted/protocols/amp.py", line 752, in _sendBoxCommand
box._sendTo(self.boxSender)
File "/usr/local/lib/python2.7/site-packages/twisted/protocols/amp.py", line 577, in _sendTo
proto.sendBox(self)
exceptions.AttributeError: 'NoneType' object has no attribute 'sendBox'
2013-08-16 12:28:58-0400 [Uninitialized] AMP connection established (HOST:IPv4Address(TCP, '127.0.0.1', 50457) PEER:IPv4Address(TCP, '127.0.0.1', 12345))

You're trying to callRemote before the connection is established. A LoopingCall will, by default, immediately run its function when you start it. Instead of doing lc.start(4.0), do lc.start(4.0, now=False). This will wait four second before the first call.

In the normal environment, where the network connection is rock stable, the way of #habnabit will be working, but in the real world, the connection latency can not be estimated as you expect. The better solution for this problem, the looping call must be executed after the amp client is connected like this.
from twisted.protocols.amp import AMP
from twisted.python.log import startLogging, err
from twisted.internet.task import LoopingCall
from twisted.internet import reactor, endpoints
from sys import stdout
import commands
startLogging(stdout)
class MyAMP:
def __init__(self, host, port):
destination = endpoints.TCP4ClientEndpoint(reactor, host, port)
self.protocol = AMP()
self.d = endpoints.connectProtocol(destination, self.protocol)
def loop (proto, ) :
return proto.callRemote(commands.get_user, key='Hello world')
def main(host, port):
def _cb_connected (proto, ) :
lc = LoopingCall(loop, proto, )
lc.start(4.0)
return
myamp = MyAMP(host, port)
myamp.d.addCallback(_cb_connected, )
reactor.run()
return
main('127.0.0.1', 12345, )

Related

Why does eventlet/socketio not work when the server is run from a function?

The code below works as expected and runs without problem:
import socketio
import eventlet
port = 5000
sio = socketio.Server()
app = socketio.WSGIApp(sio, static_files={})
#sio.event
def connect(sid, environ):
print('Connect')
eventlet.wsgi.server(eventlet.listen(('', port)), app) # Note this line
However, as soon as the final line is wrapped in a function, an error occurs
import socketio
import eventlet
port = 5000
sio = socketio.Server()
app = socketio.WSGIApp(sio, static_files={})
#sio.event
def connect(sid, environ):
print('Connect')
def run():
eventlet.wsgi.server(eventlet.listen('', port), app) # Note this line
run()
This is the full error message:
Traceback (most recent call last):
File "/home/thatcoolcoder/coding/micro-chat/tester3.py", line 16, in <module>
run()
File "/home/thatcoolcoder/coding/micro-chat/tester3.py", line 14, in run
eventlet.wsgi.server(eventlet.listen('', port), app)
File "/usr/lib/python3.9/site-packages/eventlet/convenience.py", line 49, in listen
sock = socket.socket(family, socket.SOCK_STREAM)
File "/usr/lib/python3.9/site-packages/eventlet/greenio/base.py", line 136, in __init__
fd = _original_socket(family, *args, **kwargs)
File "/usr/lib/python3.9/socket.py", line 232, in __init__
_socket.socket.__init__(self, family, type, proto, fileno)
OSError: [Errno 97] Address family not supported by protocol
How can I prevent this? I'm building a small chat app and to keep things clean, I need to create and run the server from within a function (specifically a class method).
The issue is in this line:
eventlet.wsgi.server(eventlet.listen('', port), app)
This line should be:
eventlet.wsgi.server(eventlet.listen(('', port)), app)
Updated Code:
import socketio
import eventlet
port = 5000
sio = socketio.Server()
app = socketio.WSGIApp(sio, static_files={})
#sio.event
def connect(sid, environ):
print('Connect')
def run():
eventlet.wsgi.server(eventlet.listen(('', port)), app) # Note this line
run()
Output:
Explanation:
The eventlet.listen() is a param of eventlet.wsgi.server(), and the eventlet.listen() means listen which address and port.
The ('', 8000) combine the address and port. If we do not set the first param, it will be default 0.0.0.0.
If we set the localhost it will be look back address 127.0.0.1 and we also can set a IP address of our computer.

How to construct proxy objects from multiprocessing.managers.SyncManager?

I have long running file I/O tasks which I'd like to be able to move into a daemon/server process. A CLI tool would be used to queue new jobs to run, query the status of running jobs, and wait for individual jobs. Python's multiprocessing.managers looks like a nice simple way to handle the IPC. I'd like to be able to construct a SyncManager.Event for the client to wait on without blocking the server, but attempting to do so results in triggers a "server not yet started" assertion. Ironically this assertion gets sent from the server to the client, so obviously the server is started, somewhere.
Here's the minimal example:
#!/usr/bin/env python3
import time
import sys
import concurrent.futures
from multiprocessing.managers import SyncManager
def do_work(files):
"""Simulate doing some work on a set of files."""
print(f"Starting work for {files}.")
time.sleep(2)
print(f"Finished work for {files}.")
# Thread pool to do work in.
pool = concurrent.futures.ProcessPoolExecutor(max_workers=1)
class Job:
job_counter = 1
def __init__(self, files):
"""Setup a job and queue work for files on our thread pool."""
self._job_number = self.job_counter
Job.job_counter += 1
print(f"manager._state.value = {manager._state.value}")
self._finished_event = manager.Event()
print(f"Queued job {self.number()}.")
future = pool.submit(do_work, files)
future.add_done_callback(lambda f : self._finished_event.set())
def number(self):
return self._job_number
def event(self):
"""Get an event which can be waited on for the job to complete."""
return self._finished_event
class MyManager(SyncManager):
pass
MyManager.register("Job", Job)
manager = MyManager(address=("localhost", 16000), authkey=b"qca-authkey")
if len(sys.argv) > 1 and sys.argv[1] == "server":
manager.start()
print(f"Manager listening at {manager.address}.")
while True:
time.sleep(1)
else:
manager.connect()
print(f"Connected to {manager.address}.")
job = manager.Job(["a", "b", "c"])
job.event().wait()
print("Done")
If I run the client I see:
$ ./mp-manager.py
Connected to ('localhost', 16000).
Traceback (most recent call last):
File "./mp-manager.py", line 54, in <module>
job = manager.Job(["a", "b", "c"])
File "/usr/lib/python3.8/multiprocessing/managers.py", line 740, in temp
token, exp = self._create(typeid, *args, **kwds)
File "/usr/lib/python3.8/multiprocessing/managers.py", line 625, in _create
id, exposed = dispatch(conn, None, 'create', (typeid,)+args, kwds)
File "/usr/lib/python3.8/multiprocessing/managers.py", line 91, in dispatch
raise convert_to_error(kind, result)
multiprocessing.managers.RemoteError:
---------------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3.8/multiprocessing/managers.py", line 210, in handle_request
result = func(c, *args, **kwds)
File "/usr/lib/python3.8/multiprocessing/managers.py", line 403, in create
obj = callable(*args, **kwds)
File "./mp-manager.py", line 24, in __init__
self._finished_event = manager.Event()
File "/usr/lib/python3.8/multiprocessing/managers.py", line 740, in temp
token, exp = self._create(typeid, *args, **kwds)
File "/usr/lib/python3.8/multiprocessing/managers.py", line 622, in _create
assert self._state.value == State.STARTED, 'server not yet started'
AssertionError: server not yet started
---------------------------------------------------------------------------
The server output is:
$ ./mp-manager.py server
Manager listening at ('127.0.0.1', 16000).
manager._state.value = 0

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)
Fully working version below. I made a few changes:
I used threads instead of processes, as suggested by #Dunes.
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.)
I handled KeyboardExceptions in the main thread so you can Ctrl+C to exit.
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".)
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?)
I fixed the spelling of "receiver" everywhere because it was bothering me. :-)
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())
These are processes not threads. The problem occurs because in the background Python has to send a copy of your Communication object to the child process. However, your object contains socket objects that cannot be serialised. Use threading and the Thread object in place of Process and you won't run into this problem. This is because threads run in the same process.

Autobahn Python Errno 99 cannot assign requested address

While trying to set up a Websocket server, I have encountered the following error.
The same code works fine under LAN IP '192.168.x.x', but fails to work with a public ip/domain name
Here is the error trace
Traceback (most recent call last):
File "WSServer.py", line 19, in <module>
server = loop.run_until_complete(coro)
File "/usr/lib64/python3.4/asyncio/base_events.py", line 208, in run_until_complete
return future.result()
File "/usr/lib64/python3.4/asyncio/futures.py", line 243, in result
raise self._exception
File "/usr/lib64/python3.4/asyncio/tasks.py", line 319, in _step
result = coro.send(value)
File "/usr/lib64/python3.4/asyncio/base_events.py", line 579, in create_server
% (sa, err.strerror.lower()))
OSError: [Errno 99] error while attempting to bind on address ('121.6.x.x', 9000): cannot assign requested address
Python Server Code:
from autobahn.asyncio.websocket import WebSocketServerProtocol
class MyServerProtocol(WebSocketServerProtocol):
def onMessage(self, payload, isBinary):
print("message received")
self.sendMessage(payload, isBinary)
if __name__ == '__main__':
import asyncio
from autobahn.asyncio.websocket import WebSocketServerFactory
factory = WebSocketServerFactory()
factory.protocol = MyServerProtocol
loop = asyncio.get_event_loop()
coro = loop.create_server(factory, '121.6.x.x', 9000)
server = loop.run_until_complete(coro)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
server.close()
loop.close()
Could the issue be related with the server setting? e.g. hostname, SELinux

"Unhandled Error" comes when TCP server tries to accept connections from client in twisted

from twisted.internet.protocol import Factory,Protocol
from twisted.internet import reactor
class ChatServer(Protocol):
def connectionMade(self):
print("A Client Has Connected")
factory = Factory()
reactor.listenTCP(80,factory)
print("Chat Server Started")
reactor.run()
the above code is running succesfully.but when I try to open TCP(telnet localhost 80).
Errors occurs :
Unhandled Error
Traceback (most recent call last):
File "C:\Python27\lib\site-packages\twisted\python\log.py", line 69, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "C:\Python27\lib\site-packages\twisted\python\context.py", line 118, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "C:\Python27\lib\site-packages\twisted\python\context.py", line 81, in callWithContext
return func(*args,**kw)
File "C:\Python27\lib\site-packages\twisted\internet\selectreactor.py", line 150, in _doReadOrWrite
why = getattr(selectable, method)()
--- <exception caught here> ---
File "C:\Python27\lib\site-packages\twisted\internet\tcp.py", line 718, in doRead
protocol = self.factory.buildProtocol(self._buildAddr(addr))
File "C:\Python27\lib\site-packages\twisted\internet\protocol.py", line 104, in buildProtocol
p = self.protocol()
exceptions.TypeError: 'NoneType' object is not callable
If anyone knows the solution kindly help me. I'm just new to twisted .
class ChatServer(Protocol):
def connectionMade(self):
print("A Client Has Connected")
factory = Factory()
reactor.listenTCP(80,factory)
You haven't made any association between factory and ChatServer in this code. Try inserting this line:
factory.protocol = ChatServer
In an upcoming (not yet released) version of Twisted, Factory is getting a new class method to make this set-up even easier. Using that version, this example would be even shorter:
class ChatServer(Protocol):
def connectionMade(self):
print("A Client Has Connected")
reactor.listenTCP(80, Factory.forProtocol(ChatServer))

Categories