I am looking at the example of the UDP echo server:
import asyncio
class EchoServerProtocol:
def connection_made(self, transport):
self.transport = transport
def datagram_received(self, data, addr):
message = data.decode()
print('Received %r from %s' % (message, addr))
print('Send %r to %s' % (message, addr))
self.transport.sendto(data, addr)
loop = asyncio.get_event_loop()
print("Starting UDP server")
# One protocol instance will be created to serve all client requests
listen = loop.create_datagram_endpoint(
EchoServerProtocol, local_addr=('127.0.0.1', 9999))
transport, protocol = loop.run_until_complete(listen)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
transport.close()
loop.close()
It seems that the call...
loop.create_datagram_endpoint(EchoServerProtocol, local_addr=('127.0.0.1', 9999))
...is doing all the work here. The method documentation states the following for the first argument (protocol_factory):
protocol_factory must be a callable returning a protocol instance.
My questions:
What defines a protocol instance?
Is returning a protocol instance a different wording for initiating a protocol object?
How does the EchoServerProtocol in the example fulfill this requirement?
What defines a protocol instance?
A protocol is a class you define that implements one of the interfaces defined in the Protocols section, i.e. provides implementations for a set of callbacks, e.g. Connection Callbacks.
So for the UDP echo server example you have posted, the EchoServerProtocol user defined class actually defines a protocol by implementing the connection_made and datagram_received.
In summary, if you implement one of those callbacks in a class, you are said to be defining a Protocol. So an instance/object of that class would be a protocol instance.
Is returning a protocol instance a different wording for initiating a protocol object?
Formally YES. Before you would return a protocol instance, you would have initialized it. So basically one is a prerequisite of the other.
How does the EchoServerProtocol in the example fulfill this requirement?
So first of all, as answered the first question, the EchoServerProtocol defines a protocol. Thus the next thing is to provide a protocol_factory, which is defined as:
protocol_factory must be a callable returning a protocol instance.
So to satisfy this requirement, you could just have this simple method:
def my_protocol_factory():
return EchoServerProtocol()
Note, that this factory first initializes the protocol instance and then returns it.
So the thing that might confuse you in the example, is that the class EchoServerProtocol itself is passed as the protocol_factory, but if you summarize what I've said, you will see that the EchoServerProtocol is actually a callable, and when it gets called, i.e. EchoServerProtocol() it actually initializes a EchoServerProtocol instance, and returns it.
So yep, the example fulfills the requirement.
Related
I'm trying to pass a TCP connection to a Twisted subprocess with adoptStreamConnection, but I can't figure out how to get the Process disposed in the main process after doing that.
My desired flow looks like this:
Finish writing any data the Protocol transport has waiting
When we know the write buffer is empty send the AMP message to transfer the socket to the subprocess
Dispose the Protocol instance in the main process
I tried doing nothing, loseConnection, abortConnection, and monkey patching _socketClose out and using loseConnection. See code here:
import weakref
from twisted.internet import reactor
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.python.sendmsg import getsockfam
from twisted.internet.protocol import Factory, Protocol
import twisted.internet.abstract
class EchoProtocol(Protocol):
def dataReceived(self, data):
self.transport.write(data)
class EchoFactory(Factory):
protocol = EchoProtocol
class TransferProtocol(Protocol):
def dataReceived(self, data):
self.transport.write('main process still listening!: %s' % (data))
def connectionMade(self):
self.transport.write('this message should make it to the subprocess\n')
# attempt 1: do nothing
# everything works fine in the adopt (including receiving the written message), but old protocol still exists (though isn't doing anything)
# attempt 1: try calling loseConnection
# we lose connection before the adopt opens the socket (presumably TCP disconnect message was sent)
#
# self.transport.loseConnection()
# attempt 2: try calling abortConnection
# result is same as loseConnection
#
# self.transport.abortConnection()
# attempt 3: try monkey patching the socket close out and calling loseConnection
# result: same as doing nothing-- adopt works (including receiving the written message), old protocol still exists
#
# def ignored(*args, **kwargs):
# print 'ignored :D'
#
# self.transport._closeSocket = ignored
# self.transport.loseConnection()
reactor.callLater(0, adopt, self.transport.fileno())
class ServerFactory(Factory):
def buildProtocol(self, addr):
p = TransferProtocol()
self.ref = weakref.ref(p)
return p
f = ServerFactory()
def adopt(fileno):
print "does old protocol still exist?: %r" % (f.ref())
reactor.adoptStreamConnection(fileno, getsockfam(fileno), EchoFactory())
port = 1337
endpoint = TCP4ServerEndpoint(reactor, port)
d = endpoint.listen(f)
reactor.run()
In all cases the Protocol object still exists in the main process after the socket has been transferred. How can I clean this up?
Thanks in advance.
Neither loseConnection nor abortConnection tell the reactor to "forget" about a connection; they close the connection, which is very different; they tell the peer that the connection has gone away.
You want to call self.transport.stopReading() and self.transport.stopWriting() to remove the references to it from the reactor.
Also, it's not valid to use a weakref to test for the remaining existence of an object unless you call gc.collect() first.
As far as making sure that all the data has been sent: the only reliable way to do that is to have an application-level acknowledgement of the data that you've sent. This is why protocols that need a handshake that involves changing protocols - say, for example, STARTTLS - have a specific handshake where the initiator says "I'm going to switch" (and then stops sending), then the peer says "OK, you can switch now". Another way to handle that in this case would be to hand the data you'd like to write to the subprocess via some other channel, instead of passing it to transport.write.
I have a twisted web socket client protocol and I have another socket server in this reactor loop
how can I access to sendMessage method from socket server?
I see this link but I didn't get what should I do. I try this but I get some error:
reactor.callFromThread(WebSocketClientProtocol.sendMessage, protocol, 'data')
exceptions.TypeError: unbound method sendMessage() must be called with WebSocketClientProtocol instance as first argument (got module instance instead)
my websocket client:
class WebSocketProtocol(WebSocketClientProtocol):
def sendHello(self):
self.sendMessage("something")
def onOpen(self):
self.sendHello()
def onMessage(self, msg, binary):
print msg
websocket_factory = WebSocketClientFactory("ws://localhost:1025/ws")
websocket_factory.protocol = WebSocketProtocol
connectWS(websocket_factory)
I solve It by this code:
class WebSocketProtocol(WebSocketClientProtocol):
def onOpen(self):
self.factory.data = []
self.factory.data.append(self)
reactor.callFromThread(WebSocketClientProtocol.sendMessage, websocket_factory.data[0], send)
callFromThread is only for use when you have multiple threads in your program. Just because you have multiple servers doesn't mean you have multiple threads. In fact, Twisted is largely oriented towards running multiple servers (and/or clients) without using any extra threads.
The specific error you've encountered is about how you need to call an instance method on an instance, though.
WebSocketClientProtocol is a class and WebSocketClientProtocol.sendMessage is an unbound method. This is like trying to write:
class Foo(object):
def bar(self):
print "Foo.bar:", self
Foo.bar()
This doesn't work any better than what you tried because of course you need to have an instance:
foo = Foo()
...
foo.bar()
I am trying to implement a simple server reply in Perspective Broker.
Possible implementation (please suggest others if possible):
Client requests server to execute a server method, Server executes then replies (by executing a client method whose sole purpose is to print a message):
[Client-side]:
class ClientPrint(pb.Referenceable):
def remote_clientprint(self, message):
print "Printing the message from the server: ", message
[Server-side]:
class RootServerObject(pb.Root):
def remote_OneFunc(self, ...):
...
print "Now sending the reply..."
*get ClientPrint object?*
clientprintobj.callRemote("clientprint", "this is the reply!")
How can I implement the grabbing of client-side objects? Is there a better way to implement server replies than grabbing a client-side object and calling a print-only client method?
Here is the full code where I am trying to implement the replies:
[Client-side]:
from twisted.internet import reactor
from twisted.spread import pb
class Client():
def __init__(self, addr, port, spec):
self.addr = None
self.port = None
self.SomeData = None
def connect(self, addr, port):
factory = pb.PBClientFactory()
reactor.connectTCP(addr, port, factory)
def1 = factory.getRootObject()
def1.addCallbacks(self.got_obj, self.err_obj)
def got_obj(self, rootsrvobj):
print "Got root server obj:", rootsrvobj
self.server = rootsrvobj
def2 = self.server.callRemote("SomeFunc", SomeData)
def err_obj(self, reason):
print "Error getting root server obj:", reason
self.quit()
def cmdsub(addr, port, SomeData):
c = Client(addr, port, SomeData)
c.connect(addr, port)
[Server-side]:
class RootServerObject(pb.Root):
def __init__(self):
self.DataOut = None
def remote_SomeFunc(self, SomeData):
self.DataOut = hash(SomeData)
print "Now sending reply..."
*implement a reply?*
Perhaps there are some more advanced Twisted (or Twisted PB) features that will make this simpler.
Documentation: https://twistedmatrix.com/documents/12.3.0/core/howto/pb-usage.html#auto3
Thanks.
The simplest way to do this is to take the client-side object that the server needs to use and pass it to the server. Almost any solution I can think of has this at its core.
Change your client's got_obj method to be something more like this:
def got_obj(self, rootsrvobj):
print "Got root server obj:", rootsrvobj
self.server = rootsrvobj
def2 = self.server.callRemote("SomeFunc", self, SomeData)
And change the implementation of remote_SomeFunc to be something more like this:
def remote_SomeFunc(self, client, SomeData):
self.DataOut = hash(SomeData)
print "Now sending reply..."
client.callRemote("client_print", "Here is your reply")
You might want to investigate Twisted Cred as a more structured way to manage references to your client object - but cred is just building on this exact feature of Perspective Broker to provide its more abstract, more featureful interface.
However, notice that I said "almost" above...
Keep in mind that Twisted's implementation of Perspective Broker has well-integrated support for Deferreds. If a remote_ method returns a Deferred then no response will be sent to the method call until the Deferred fires and then the result will be sent as the result of the method call. You might consider putting the logic of client_print into a callback on the Deferred returned by self.server.callRemote("SomeFunc", SomeData) and making the server's remote_SomeFunc return the reply, either synchronously or asynchronously (as a Deferred).
An example of my code is as follows. I would like to arbitrarly send data at various points in the program. Twisted seems great for listening and then reacting, but how to I simply send data.
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
import os
class listener(DatagramProtocol):
def __init__(self):
def datagramReceived(self, data, (host, port)):
print "GOT " + data
def send_stuff(data):
self.transport.write(data, (host, port))
reactor.listenUDP(10000, listener())
reactor.run()
##Some things happen in the program independent of the connection state
##Now how to I access send_stuff
Your example already includes some code that sends data:
def send_stuff(data):
self.transport.write(data, (host, port))
In other words, the answer to your question is "call send_stuff" or even "call transport.write".
In a comment you asked:
#Now how to I access send_stuff
There's nothing special about how you "access" objects or methods when you're using Twisted. It's the same as in any other Python program you might write. Use variables, attributes, containers, function arguments, or any of the other facilities to maintaining references to objects.
Here are some examples:
# Save the listener instance in a local variable
network = listener()
reactor.listenUDP(10000, network)
# Use the local variable to connect a GUI event to the network
MyGUIApplication().connect_button("send_button", network.send_stuff)
# Use the local variable to implement a signal handler that sends data
def report_signal(*ignored):
reactor.callFromThread(network.send_stuff, "got sigint")
signal.signal(signal.SIGINT, report_signal)
# Pass the object referenced by the local variable to the initializer of another
# network-related object so it can save the reference and later call methods on it
# when it gets events it wants to respond to.
reactor.listenUDP(20000, AnotherDatagramProtocol(network))
And so on.
I create little SimpleXMLRPCServer for check ip of client.
I try this:
Server
import xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer
server = SimpleXMLRPCServer(("localhost", 8000))
def MyIp():
return "Your ip is: %s" % server.socket.getpeername()
server.register_function(MyIp)
server.serve_forever()
Client
import xmlrpclib
se = xmlrpclib.Server("http://localhost:8000")
print se.MyIp()
Error
xmlrpclib.Fault: :(107, 'Transport endpoint is not connected')">
How make client_address visible to all functions?
If you want for example to pass client_address as the first argument to every function, you could subclass SimpleXMLRPCRequestHandler (pass your subclass as the handler when you instantiate SimpleXMLRPCServer) and override _dispatch (to prepend self.client_address to the params tuple and then delegate the rest to SimpleXMLRPCRequestHandler._dispatch). If this approach is OK and you want to see code, just ask!
I'm not sure how you'd safely use anything but the function arguments to "make client_address visible" -- there's no client_address as a bare name, global or otherwise, there's just the self.client_address of each instance of the request handler class (and hacks such as copying it to a global variables feel really yucky indeed -- and unsafe under threading, etc etc).