I am trying to create a twisted server that would accept an http post request and then write the info of that post request to a tcp connection. Right now I have just modified the multiple echo server/client given in the tutorial:
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor
class MultiClientEcho(Protocol):
def __init__(self, factory):
self.factory = factory
def connectionMade(self):
self.factory.clients.insert(self.factory.id, self)
self.factory.id += 1
self.ip = self.transport.getPeer()
def dataReceived(self, data):
self.factory.clients[0].transport.write(data)
def connectionLost(self, reason):
self.factory.clients.remove(self)
class MultiClientEchoFactory(Factory):
def __init__(self):
self.clients = []
self.id = 0
def buildProtocol(self, addr):
return MultiClientEcho(self)
reactor.listenTCP(8000, MultiClientEchoFactory())
reactor.run()
I've been led to understand that Twisted can handle these kinds of things. If so, can someone point me in the right direction with some code or a simple example? I've been hitting my head against the wall on this one for a while.
Thanks
Related
I have a python Twisted server application interfacing with a legacy client application and each client has been assigned a specific port on which to connect to the server. So I have set up listeners on all those ports on the server and it is working beautifully, but I need to build in some safeguards to disallow more than one client connecting on the same server port. There are too many things on the client application that break when another client is connected to the same port, and I can't update that application right now. I gotta live with how it has been operating.
I know that I could build in some logic to the connectionMade() function to see if someone already exists on that port and if so, close this new connection. But I'd rather there be a way to reject it to begin with, so the client is not even allowed to connect. Then the client will know they made a mistake and they can alter which port they are trying to connect on.
Here is a stripped down version of my server code if that helps.
from twisted.internet.protocol import Factory
from twisted.internet.protocol import Protocol
from twisted.internet import reactor
from twisted.internet import task
import time
class MyServerTasks():
def someFunction(msg):
#Do stuff
def someOtherFunction(msg):
#Do other stuff
class MyServer(Protocol):
def __init__(self, users):
self.users = users
self.name = None
def connectionMade(self):
#Depending on which port is connected, go do stuff
def connectionLost(self, reason):
#Update dictionaries and other global info
def dataReceived(self, line):
t = time.strftime('%Y-%m-%d %H:%M:%S')
d = self.transport.getHost()
print("{} Received message from {}:{}...{}".format(t, d.host, d.port, line)) #debug
self.handle_GOTDATA(line)
def handle_GOTDATA(self, msg):
#Parse the received data string and do stuff based on the message.
#For example:
if "99" in msg:
MyServerTasks.someFunction(msg)
class MyServerFactory(Factory):
def __init__(self):
self.users = {} # maps user names to Chat instances
def buildProtocol(self, *args, **kwargs):
protocol = MyServer(self.users)
protocol.factory = self
protocol.factory.clients = []
return protocol
reactor.listenTCP(50010, MyServerFactory())
reactor.listenTCP(50011, MyServerFactory())
reactor.listenTCP(50012, MyServerFactory())
reactor.listenTCP(50013, MyServerFactory())
reactor.run()
When a client connected to the server, twisted use the factory to create
a protocol (by calling its buildProtocol method) instance to handle the client request.
therefore you can maintain a counter of connected client in your MyServerFactory,
if the counter has reached maximum allowed connected client you can return None
instead of creating new protocol for that client.
Twisted will close the client connection if the factory doesn't return a protocol from
its buildProtocol method.
you can see here
class MyServer(Protocol):
def __init__(self, users):
self.users = users
self.name = None
def connectionMade(self):
#Depending on which port is connected, go do stuff
def connectionLost(self, reason):
#Update dictionaries and other global info
self.factory.counter -= 1
def dataReceived(self, line):
t = time.strftime('%Y-%m-%d %H:%M:%S')
d = self.transport.getHost()
print("{} Received message from {}:{}...{}".format(t, d.host, d.port, line)) #debug
self.handle_GOTDATA(line)
def handle_GOTDATA(self, msg):
#Parse the received data string and do stuff based on the message.
#For example:
if "99" in msg:
MyServerTasks.someFunction(msg)
class MyServerFactory(Factory):
MAX_CLIENT = 2
def __init__(self):
self.users = {} # maps user names to Chat instances
self.counter = 0
def buildProtocol(self, *args, **kwargs):
if self.counter == self.MAX_CLIENT:
return None
self.counter += 1
protocol = MyServer(self.users)
protocol.factory = self
protocol.factory.clients = []
return protocol
I'm new in twisted and in web programming itself.
What I want is to implement server and client (and pass some string data between them). The problem is, that the data is important, so I want client to resend it to server in case of loosing connection or in case it wasn't full on the server side. But I don't want to resend it in case it was fully received, so I don't think that just adding the logic to def connectionLost() will do. How could this be done?
This is my server (just the same as in doc examples) and (after ------------) is the client:
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.internet import reactor
class ConsoleReceiver(Protocol):
def connectionMade(self):
self.transport.write(
"Welcome!\n".encode('utf-8'))
def dataReceived(self, data):
self.transport.write('Data received, thanks'.encode('utf-8'))
data = data.decode('ascii')
print(data)
self.transport.loseConnection()
class ServerFactory(Factory):
def buildProtocol(self, addr):
return ConsoleReceiver()
if __name__ == '__main__':
endpoint = TCP4ServerEndpoint(reactor, 21285)
endpoint.listen(ServerFactory())
reactor.run()```
-----------------------------------------------------------------------------
#some_flask_route.route('/test/')
urgent_information = <getting some urgent information from db with flask_sqlalchemy>
reactor.connectTCP('localhost', 21285, ShiftInfoSenderFactory(urgent_information))
reactor.run()
class ShiftInfoSender(Protocol):
def __init__(self, urgent_information):
self.urgent_information = urgent_information
def connectionMade(self):
self.transport.write('\nInformation to be red:{}\n'.format(self.urgent_information[1]).encode('utf-8'))
for i in self.urgent_information[2]:
self.transport.write('Some unpacked information: {}'.format(i).encode('utf-8')
def dataReceived(self, data):
print(data.decode('ascii'))
class ShiftInfoSenderFactory(ClientFactory):
def __init__(self, urgent_information):
self.urgent_information = urgent_information
def startedConnecting(self, connector):
print('Started to connect')
def buildProtocol(self, addr):
print('Connected')
return ShiftInfoSender(self.urgent_information)
def clientConnectionLost(self, connector, reason):
print('Lost connection. Reason:', reason)
def clientConnectionFailed(self, connector, reason):
print('Connection failed. Reason:', reason) ```
I have a basic server and client implemented in Twisted. My goal is to have the client process some data, report its progress back to the server, and repeat until all the data is processed. The client is able to send an initial message to the server but it is not receiving the server's response letting it know it is ok to start processing that data. Here is the code I have.
Server:
from twisted.internet import reactor, protocol
PORT = 9000
progress = 0
class MyServer(protocol.Protocol):
def dataReceived(self, data):
global progress
print(data)
if data != "Ready?":
progress = int(data)
self.transport.write("Got it.")
self.transport.loseConnection()
def connectionLost(self, reason):
global progress
if progress == 10:
print("Completed.")
reactor.stop()
class MyServerFactory(protocol.Factory):
protocol = MyServer
factory = MyServerFactory()
reactor.listenTCP(PORT, factory)
reactor.run()
Client:
from twisted.internet import reactor, protocol
import time
HOST = 'localhost'
PORT = 9000
progress = 0
class MyClient(protocol.Protocol):
def connectionMade(self):
self.transport.write("Ready?")
self.transport.loseConnection()
def dataReceived(self, data):
global progress
progress += 1
print(progress)
self.transport.write(str(progress))
self.loseConnection()
def connectionLost(self, reason):
global progress
if progress == 10:
print("Completed.")
reactor.stop()
class MyClientFactory(protocol.ClientFactory):
protocol = MyClient
factory = MyClientFactory()
reactor.connectTCP(HOST, PORT, factory)
reactor.run()
I figured out that my issue was that I was prematurely closing the connection. In some earlier testing I was trying to send multiple messages within the dataReceived function which were getting concatenated into a single message. This led me to believe that you must lose the connection for each message to go through. Rather, you simply need to let the function finish before sending another message. Here is the updated code that is working as intended.
Server:
from twisted.internet import reactor, protocol
PORT = 9000
progress = 0
class MyServer(protocol.Protocol):
def dataReceived(self, data):
global progress
print(data)
if data != "Ready?":
progress = int(data)
self.transport.write("Got it")
if progress == 10:
self.transport.loseConnection()
def connectionLost(self, reason):
print("Completed.")
reactor.stop()
class MyServerFactory(protocol.Factory):
protocol = MyServer
factory = MyServerFactory()
reactor.listenTCP(PORT, factory)
reactor.run()
Client:
from twisted.internet import reactor, protocol
import time
HOST = 'localhost'
PORT = 9000
progress = 0
class MyClient(protocol.Protocol):
def connectionMade(self):
self.transport.write("Ready?")
def dataReceived(self, data):
global progress
progress += 1
print(progress)
self.transport.write(str(progress))
if progress == 10:
self.transport.loseConnection()
def connectionLost(self, reason):
print("Completed.")
reactor.stop()
class MyClientFactory(protocol.ClientFactory):
protocol = MyClient
factory = MyClientFactory()
reactor.connectTCP(HOST, PORT, factory)
reactor.run()
from twisted.internet import protocol, reactor, endpoints
class Server(protocol.Protocol):
def connectionLost(self, *args):
print('Connection lost')
def dataReceived(self, data):
self.transport.write(data)
class buildFactory(protocol.Factory):
def buildProtocol(self, addr):
return Server()
if __name__ == '__main__':
reactor.listenTCP(8000, buildFactory())
reactor.run()
How can I set and get server/client headers?
I've googled all over but the answers I've found required you to create a http server.
The documentation here:
https://twistedmatrix.com/documents/8.0.0/api/twisted.web.http.Request.html
You are looking for the particular getHeader section
https://twistedmatrix.com/documents/8.0.0/api/twisted.web.http.Request.html#getHeader
def getAllHeaders(self): (source)
Return dictionary of all headers the request received.
I'm trying to solve a simple Twisted problem.
I'm using the Python 2.7 Twisted Chat.py example for simplicity.
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
class Chat(LineReceiver):
def __init__(self, users):
self.users = users
self.name = None
self.state = "GETNAME"
def connectionMade(self):
self.sendLine("What's your name?")
def connectionLost(self, reason):
if self.users.has_key(self.name):
del self.users[self.name]
def lineReceived(self, line):
if self.state == "GETNAME":
self.handle_GETNAME(line)
else:
self.handle_CHAT(line)
def handle_GETNAME(self, name):
if self.users.has_key(name):
self.sendLine("Name taken, please choose another.")
return
self.sendLine("Welcome, %s!" % (name,))
self.name = name
self.users[name] = self
self.state = "CHAT"
def handle_CHAT(self, message):
message = "<%s> %s" % (self.name, message)
for name, protocol in self.users.iteritems():
if protocol != self:
protocol.sendLine(message)
class ChatFactory(Factory):
def __init__(self):
self.users = {} # maps user names to Chat instances
def buildProtocol(self, addr):
return Chat(self.users)
reactor.listenTCP(8123, ChatFactory())
reactor.run()
What I'm trying to do is use twisted.internet task to create a Task that runs every 60 seconds and sends data to all connected sessions.
semi Pseudocode
def broadcastmsg():
for client in factory:
client.protocol.transport.write("I am a Test\n\r")
event = task.LoopingCall(broadcastmsg)
event.start(60)
The issue is I can't get Twisted to behave correctly. I can make it happen on a per session basis. But then for every use that connects it spams twice as much etc.
how are you setting up looping call?
As you know protocols are created and managed by factories. Sending some message to all users periodically looks like a task that should be put into factory. You can create loopingCall in factory __init__ method and start it immediately after init. Since you have one factory per all connections this should send notifications only once per 60 seconds
class ChatFactory(Factory):
def __init__(self):
self.users = {} # maps user names to Chat instances
def broadcast_msg():
for name in self.users:
self.users[name].sendLine("looping call send to users: {}".format(self.users.keys()))
self.looping_call = task.LoopingCall(broadcast_msg)
self.looping_call.start(60)
this should work ok, at least it does for me.