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()
Related
I am learning the socketserver module and I am following the example but I modified the handle function a bit
class CustomServer(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print(f">{self.client_address[0]}: {self.data}")
def send(self, targets=[]):
if not targets:
return
if __name__ == "__main__":
HOST, PORT = "localhost", 6666
with socketserver.TCPServer((HOST, PORT), CustomServer) as server:
server.serve_forever()
Now when I try to use netcat and send sth to the server I don't see anything being outputted to the console
nc -v 10.0.0.112 6666
How do you properly edit the handle method so that it will print the address of the client each time
It is really important to understand the OOP concept and how to use it
Looking at the source code for socketserver I realized that I can create a class that inherits the BaseRequestHandler than I modified the handler method and passed my class to the TCPServer
class CustomHandler(BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print(f">{self.client_address[0]}: {self.data}")
if __name__ == "__main__":
HOST, PORT = "0.0.0.0", 6666
server = TCPServer(((HOST, PORT)), CustomHandler)
server.serve_forever()
I'm very new to python and twisted. I tried to execute simple server client program. Am doing in localhost, both are in different trminal. Here what I need is, I want to call that server function "myfunc" from client terminal and also to print the value of "x" in client terminal. For that I don't know how to do and what commands to write in server and client. Please help, both codes are mentioned below. Thanks in advance.
Server.py
#!/usr/bin/env python
from twisted.internet import reactor, protocol
import time
PORT = 5000
abc='Hi, Your Message Is Received'
class Server(protocol.Protocol):
def dataReceived(self, data):
print ""
time.sleep(3)
print 'From Client:', data
self.transport.write(abc)
def myfunc(x):
print "",x
class ServerFactory(protocol.Factory):
protocol = Server
def main():
f = ServerFactory()
reactor.listenTCP(PORT, f)
reactor.callInThread(myfunc, 'Server is Ready....!')
reactor.run()
if '__main__'==__name__:
main()
Client.py
from twisted.internet.protocol import ClientFactory, Protocol
from twisted.internet import reactor
import time
HOST = 'localhost'
PORT = 5000
msg = 'Hello Server'
class Client(Protocol):
def connectionMade(self):
print ""
print 'Connection is successful'
print ""
print ""
self.transport.write(msg)
def dataReceived(self, abc):
print 'From Server:', abc
def func(nam, idn):
print'Client name:',nam
print'Client Id:', idn
class ClientFactory(ClientFactory):
protocol = Client
def main():
f = ClientFactory()
reactor.connectTCP(HOST, PORT, f)
reactor.callInThread(func,'ras','90')
reactor.run()
if '__main__'==__name__:
main()
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.
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?
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