python twisted simple server client - python

I am trying to build a simple "quote of the day" server and client modified from the twisted tutorial documentation. I want the "quote of the day" to be printed from the client to prove the communication. However, from what I can tell the client is not connecting. Here is my code.
Server
from twisted.python import log
from twisted.internet.protocol import Protocol
from twisted.internet.protocol import Factory
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.internet import reactor
class QOTD(Protocol):
def connectionMade(self):
self.transport.write("An apple a day keeps the doctor away\r\n")
self.transport.loseConnection()
class QOTDFactory(Factory):
def buildProtocol(self, addr):
return QOTD()
# 8007 is the port you want to run under. Choose something >1024
endpoint = TCP4ServerEndpoint(reactor, 8007)
endpoint.listen(QOTDFactory())
reactor.run()
Client
import sys
from twisted.python import log
from twisted.internet import reactor
from twisted.internet.protocol import Factory, Protocol
from twisted.internet.endpoints import TCP4ClientEndpoint
class SimpleClient(Protocol):
def connectionMade(self):
log.msg("connection made")
#self.transport.loseConnection()
def lineReceived(self, line):
print "receive:", line
class SimpleClientFactory(Factory):
def buildProtocol(self, addr):
return SimpleClient()
def startlog():
log.startLogging(sys.stdout)
point = TCP4ClientEndpoint(reactor, "localhost", 8007)
d = point.connect(SimpleClientFactory)
reactor.callLater(0.1, startlog)
reactor.run()

pass instance of SimpleClientFactory, not the class itself to point.connect()
subclass SimpleClient from twisted.protocol.basic.LineReceiver instead of Protocol if you use lineReceived
call addErrback on the results of endpoint.listen and point.connect to handle errors

Related

Python - share sockets between processes

Is it possible to share socket objects between 2 running processes?
To simplify the issue, assume that we have two files s1.py and s2.py. Both are tcp servers listening on different ports.
s1.py body
from twisted.internet import reactor, protocol
CLIENTS = []
class Echo(protocol.Protocol):
def connectionMade(self):
CLIENTS.append(self)
def dataReceived(self, data):
self.transport.write(data)
def connectionLost(self):
CLIENTS.remove(self)
def main():
factory = protocol.ServerFactory()
factory.protocol = Echo
reactor.listenTCP(8000, factory)
reactor.run()
main()
and s2.py
from twisted.internet import reactor, protocol
class Echo(protocol.Protocol):
def dataReceived(self, data):
for socket in list_of_serialized_redis_CLIENTS_socket_object:
socket.transport.write(data)
def main():
factory = protocol.ServerFactory()
factory.protocol = Echo
reactor.listenTCP(8001, factory)
reactor.run()
main()
Is it possible to share CLIENTS from s1.py with s2.py?
Maybe there is some way to serialize CLIENTS and store it in redis or so.
Using socket.share() might not be as straight forward as you might think.
First off, it's a Windows exclusive feature and Windows is the one platform that gets the least attention by Twisted.
Getting access to the low-level socket object in Twisted can be tricky because there are a lot of things happening behind the scenes and might cause strange outcomes if changed.
So in other words, it's certainly possible to go this route, but it's not recommended.
If you're open to new solutions, I think an RPC-style server will solve your issue.
My thinking is, since the connection objects live in s1, simply create a remote process that will "do stuff" to those connections.
Other processes can connect to that server and execute functions on objects local to s1. Twisted's Perspective Broker is a ready-made solution you could leverage.
s1.py
from twisted.internet import reactor, protocol, endpoints
from twisted.spread import pb
class Echo(protocol.Protocol):
def connectionMade(self):
self.factory.client_set.add(self)
def connectionLost(self, reason):
self.factory.client_set.remove(self)
class Remote(pb.Root):
def __init__(self, client_set):
self.client_set = client_set
def remote_s1_write(self, data):
msg_tmpl = 'From s1...{0}'
for client in self.client_set:
response = msg_tmpl.format(data).encode('utf-8')
client.transport.write(response)
def main():
client_set = set() # container will be shared between the 2 servers
# setup Echo server
factory = protocol.ServerFactory()
factory.protocol = Echo
factory.client_set = client_set
echo_server = endpoints.TCP4ServerEndpoint(reactor, interface='localhost', port=8000)
echo_server.listen(factory)
# setup PB server
pb_root = Remote(client_set)
pb_factory = pb.PBServerFactory(pb_root)
pb_server = endpoints.TCP4ServerEndpoint(reactor, interface='localhost', port=7999)
pb_server.listen(pb_factory)
reactor.run()
main()
s2.py
from twisted.internet import reactor, protocol, endpoints
from twisted.spread import pb
class Echo(protocol.Protocol):
def dataReceived(self, data):
remote = self.factory.pb_factory.getRootObject()
remote.addCallback(lambda obj: obj.callRemote('s1_write', data))
remote.addCallback(lambda echo: 's1 said: {0}'.format(data))
remote.addErrback(lambda reason: 'error: {0}'.format(str(reason.value)))
remote.addCallback(lambda response: print(response))
self.transport.write(data)
def main():
# connect to s1's PB server
pb_factory = pb.PBClientFactory()
pb_connection = endpoints.TCP4ClientEndpoint(reactor, host='localhost', port=7999)
pb_connection.connect(pb_factory)
# setup Echo server
factory = protocol.ServerFactory()
factory.protocol = Echo
factory.pb_factory = pb_factory
echo_server = endpoints.TCP4ServerEndpoint(reactor, interface='localhost', port=8001)
echo_server.listen(factory)
reactor.run()
main()
I've taken the liberty and updated the syntax. Try this code and see if it works and ask if you have any issues.

Twisted proxy for proxies

I have list of proxies.
I want to write server (based on Twisted) which can connect user to random proxy from my list. So for example i have these list of proxies:
187.17.19.156:48111
201.173.81.110:43542
183.245.147.14:80
183.245.147.138:80
93.174.55.82:8080
My server IP is 55.55.55.55:1234
When user using my server as proxy than my server will redirect any traffic to one of the proxy from list.
Right now i'm ending up with code like that. But cant get response back and cant switching proxies dynamic.
from twisted.internet.protocol import Protocol, Factory
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.internet import reactor
class ServerProtocol(Protocol):
def dataReceived(self,data):
print("REquest" + str(data))
def clientProtocol():
return ClientProtocol(data)
endpoint = TCP4ClientEndpoint(reactor, "127.0.0.1", 80)
endpoint.connect(Factory.forProtocol(clientProtocol))
class ClientProtocol(Protocol):
def __init__(self, dataToSend):
self.dataToSend = dataToSend
def connectionMade(self):
self.transport.write(self.dataToSend)
def dataReceived(self,data):
print("Reply" + str(data))
reactor.listenTCP(3333,
Factory.forProtocol(ServerProtocol))
reactor.run()
Does it even possible to implement?

Twisted SSE server subscribed to Redis via pubsub

I'm trying to build a server in Twisted which would let clients connect using Server Sent Events. I would like this server also to listen to Redis and if a message comes then push it to the connected SSE clients.
I have the SSE server working. I know how to subscribe to Redis. I can't figure out how to have both pieces running without blocking each other.
I'm aware of https://github.com/leporo/tornado-redis and https://github.com/fiorix/txredisapi, which were recommended in related questions. No idea how this helps :/
How to solve this? Could you help with both: conceptual tips and code snippets?
My Twisted SSE server code:
# coding: utf-8
from twisted.web import server, resource
from twisted.internet import reactor
class Subscribe(resource.Resource):
isLeaf = True
sse_conns = set()
def render_GET(self, request):
request.setHeader('Content-Type', 'text/event-stream; charset=utf-8')
request.write("")
self.add_conn(request)
return server.NOT_DONE_YET
def add_conn(self, conn):
self.sse_conns.add(conn)
finished = conn.notifyFinish()
finished.addBoth(self.rm_conn)
def rm_conn(self, conn):
self.sse_conns.remove(conn)
def broadcast(self, event):
for conn in self.sse_conns:
event_line = "data: {}'\r\n'".format(event)
conn.write(event_line + '\r\n')
if __name__ == "__main__":
sub = Subscribe()
reactor.listenTCP(9000, server.Site(sub))
reactor.run()
My Redis subscribe code:
import redis
redis = redis.StrictRedis.from_url('redis://localhost:6379')
class RedisSub(object):
def __init__(self):
self.pubsub = redis.pubsub()
self.pubsub.subscribe('foobar-channel')
def listen(self):
for item in self.pubsub.listen():
print str(item)
This is what works for me.
I've ended up using txredis lib with a slight change to the RedisClient (added minimal subscribe capabilities).
# coding: utf-8
import os
import sys
import weakref
from txredis.client import RedisClient
from twisted.web import server, resource
from twisted.internet import reactor, protocol, defer
from twisted.python import log
from utils import cors, redis_conf_from_url
log.startLogging(sys.stdout)
PORT = int(os.environ.get('PORT', 9000))
REDIS_CONF = redis_conf_from_url(os.environ.get('REDISCLOUD_URL', 'redis://localhost:6379'))
REDIS_SUB_CHANNEL = 'votes'
class RedisBroadcaster(RedisClient):
def subscribe(self, *channels):
self._send('SUBSCRIBE', *channels)
def handleCompleteMultiBulkData(self, reply):
if reply[0] == u"message":
message = reply[1:][1]
self.sse_connector.broadcast(message)
else:
super(RedisClient, self).handleCompleteMultiBulkData(reply)
#defer.inlineCallbacks
def redis_sub():
clientCreator = protocol.ClientCreator(reactor, RedisBroadcaster, password=REDIS_CONF.get('password'))
redis = yield clientCreator.connectTCP(REDIS_CONF['host'], REDIS_CONF['port'])
redis.subscribe(REDIS_SUB_CHANNEL)
class Subscribe(resource.Resource):
isLeaf = True
sse_conns = weakref.WeakSet()
#cors
def render_GET(self, request):
request.setHeader('Content-Type', 'text/event-stream; charset=utf-8')
request.write("")
self.sse_conns.add(request)
return server.NOT_DONE_YET
def broadcast(self, event):
for conn in self.sse_conns:
event_line = "data: {}\r\n".format(event)
conn.write(event_line + '\r\n')
if __name__ == "__main__":
sub = Subscribe()
reactor.listenTCP(PORT, server.Site(sub))
RedisBroadcaster.sse_connector = sub
reactor.callLater(0, redis_sub)
reactor.run()

python twisted client server in one thread

I have modified Jean-Paul Calderone's code from this question to achieve that two server are run asynchronously in one thread and the second server is the client of the first one (he's a broker). This is my current code:
from twisted.internet import protocol, reactor
from twisted.protocols import basic
class BaseServerProtocol(basic.LineReceiver):
def lineReceived(self, line):
self.sendLine("I can see that you said: %s" % (line,))
self.transport.loseConnection()
##############################################################
class BrokerServerProtocol(basic.LineReceiver):
def lineReceived(self, line):
factory = protocol.ClientFactory()
factory.protocol = BrokerClientProtocol
reactor.connectTCP('localhost', 12345, factory)
self.sendLine("I did connect but have nothing to return")
# TODO: return broker client's response
class BrokerClientProtocol(basic.LineReceiver):
def connectionMade(self):
self.sendLine("Hello!")
# TODO: receive the message from broker
self.transport.loseConnection()
##############################################################
def main():
import sys
from twisted.python import log
log.startLogging(sys.stdout)
factory = protocol.ServerFactory()
factory.protocol = BaseServerProtocol
reactor.listenTCP(12345, factory)
factory = protocol.ServerFactory()
factory.protocol = BrokerServerProtocol
reactor.listenTCP(12346, factory)
reactor.run()
if __name__ == '__main__':
main()
What I would like to modify here is to replace both # TODO lines - first, I'd like to fetch BaseServer's response by the BrokerClient (second todo) and then fetch it by the BrokerServer (which has just created the BrokerClient, first todo). Can anyone help?

How to write a twisted server that is also a client?

How do I create a twisted server that's also a client?
I want the reactor to listen while at the same time it can also be use to connect to the same server instance which can also connect and listen.
Call reactor.listenTCP and reactor.connectTCP. You can have as many different kinds of connections - servers or clients - as you want.
For example:
from twisted.internet import protocol, reactor
from twisted.protocols import basic
class SomeServerProtocol(basic.LineReceiver):
def lineReceived(self, line):
host, port = line.split()
port = int(port)
factory = protocol.ClientFactory()
factory.protocol = SomeClientProtocol
reactor.connectTCP(host, port, factory)
class SomeClientProtocol(basic.LineReceiver):
def connectionMade(self):
self.sendLine("Hello!")
self.transport.loseConnection()
def main():
import sys
from twisted.python import log
log.startLogging(sys.stdout)
factory = protocol.ServerFactory()
factory.protocol = SomeServerProtocol
reactor.listenTCP(12345, factory)
reactor.run()
if __name__ == '__main__':
main()

Categories

Resources