I know a similar question has been asked here, but I am still battling with the following issue:
I am using putty as a telnet client and am using Win10. The code is given below. When I start the reactor and then connect a client, I get a response after each character is typed which is printed using the dataReceived function. However I can never seem to get the function lineReceived() to fire. I also tried a simple chat server example which worked fine using the lineReceived() function (and that example had no dataReceived() function. I tried commenting out dataReceived(), thinking perhaps it was masking out lineReceived().
In the code below, I cannot get lineReceived() to fire , only dataReceived() fires after each character is typed.
#! C:/Python37/python.exe
from twisted.internet import reactor
from twisted.internet.protocol import Factory, Protocol
from datetime import datetime, tzinfo, timedelta
from twisted.protocols.basic import LineReceiver
class Echo(Protocol):
def dataReceived(self, data):
self.transport.write(data)
class LineReceiver(Protocol):
print("Starting.......")
delimiter = "\n"
TIMEOUT = 300 # Client timeout period in seconds
def timeOut(self):
print("Client: %s. %s" % (self.addr, "Connection Timed out"))
self.transport.loseConnection()
def lineLengthExceeded(self, line):
return self.transport.loseConnection()
def connectionMade(self):
print("Connected......")
self.transport.write(b"hell...")
self.timeout = reactor.callLater(
self.TIMEOUT, self.timeOut
) # start client timeout timer
self.addr = self.transport.getPeer().host
addr = self.addr
self.addr_test = self.transport.getPeer().host
self.factory.NUM_CLIENTS += 1
def connectionLost(self, reason):
print("Lost.......")
# self.sendMsg("- %s left." % self.name)
self.transport.write(b"a client left")
self.factory.NUM_CLIENTS -= 1
print("Client disconnected: - " + str(self.addr))
print("Number of connections = %s" % self.factory.NUM_CLIENTS)
# def dataReceived(self, data): # this runs a few times after an initial connection
# self.transport.write(b"you typed: " + data +b"\n" + b"\r")
# print(data) # prints to log file with byte order marks
def lineReceived(self, line):
self.sendLine(b"Welcome, %s!" % (name,))
self.transport.write(b"line rx function...")
class DataFactory(Factory):
protocol = LineReceiver
NUM_CLIENTS = 0
def main():
print("Started...Listening for incoming connections.")
if __name__ == "__main__":
main()
reactor.listenTCP(10003, DataFactory())
reactor.run()
You overwrote LineReceiver with your own Protocol subclass, which does not have a lineReceived() method. You just gave me a good reason for using the module path as a namespace instead of importing a specific object :D
from twisted.protocols import basic
from twisted.internet import endpoints, protocol, reactor
class LnRcv(basic.LineReceiver):
def lineReceived(self, line):
print(line)
class DataFactory(protocol.Factory):
protocol = LnRcv
server = endpoints.TCP4ServerEndpoint(reactor, 10003)
server.listen(DataFactory())
reactor.run()
Update
I had some spare time to fix your code. You have string/bytes mismatching all over and unreferenced objects that were not in your original code. Here's an example that should work and give you a base to off of.
from uuid import uuid4
from twisted.internet import reactor
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.internet.protocol import Factory, Protocol
from twisted.protocols.basic import LineReceiver
class LnRcv(LineReceiver):
# Client timeout period in seconds
TIMEOUT = 10
def timeOut(self):
print ("Client: %s. %s" % (self.addr, 'Connection Timed out' ))
self.transport.loseConnection()
def connectionMade(self):
self.factory.NUM_CLIENTS += 1
print(f"Connected......number of connections = {self.factory.NUM_CLIENTS}")
peer = self.transport.getPeer()
self.addr = f"{peer.host}:{peer.port}"
self.name = uuid4().hex[:9].upper()
self.transport.write(f"Hello! I temporarily set your ID to {self.name}. What is your name? ".encode("utf8"))
# Start client timeout timer
self.timeout = reactor.callLater(self.TIMEOUT, self.timeOut)
def connectionLost(self, reason):
self.factory.NUM_CLIENTS -= 1
print(f"- {self.name} left because:\n{reason.getErrorMessage()}")
print(f"- Client disconnected: - {self.addr}")
print(f"- Number of connections = {self.factory.NUM_CLIENTS}")
def lineReceived(self, line):
self.sendLine(f"Welcome, {line.decode('utf8')}!".encode("utf8"))
class DataFactory(Factory):
protocol = LnRcv
NUM_CLIENTS = 0
def main():
print("Started...Listening for incoming connections.")
server = TCP4ServerEndpoint(reactor, 10003)
server.listen(DataFactory())
reactor.run()
if __name__ == "__main__":
main()
Related
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()
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 have set up a TCP server using the twisted example (with some modifications).
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
from os import path
import yaml
class User(LineReceiver):
def __init__(self,users):
self.users = users
self.name = None
def connectionMade(self):
print 'new connection'
self.sendLine('username:')
def connectionLost(self,reason):
print 'connection lost'
if not self.name == None:
msg = '%s has disconnected' % (self.name)
print msg
self.toAll(msg,None)
del self.users[self.name]
def lineRecieved(self,line):
print line
if self.name == None:
self.setName(line)
else:
self.toChat(line)
def toAll(self,msg,to_self):
for name, protocol in self.users.iteritems():
if protocol == self and not to_self == None:
self.sendLine(to_self)
else:
protocol.sendLine(msg)
def setName(self,name):
if self.users.has_key(name):
self.sendLine('username in use')
return
elif ' ' in name:
self.sendLine('no spaces!')
return
print 'new user %s' % (name)
self.sendLine('logged in as %s' % (name))
self.name = name
self.users[name] = self
def toChat(self,message):
msg = '<%s> %s' % (self.name,message)
print msg
to_self = '<%s (you)> %s' % (self.name,message)
self.toAll(msg,to_self)
class Main(Factory):
def __init__(self,motd=None):
self.users = {}
self.motd = motd
print 'loaded, waiting for connections...'
def buildProtocol(self,addr):
return User(self.users)
if not path.isfile('config.yml'):
open('config.yml','w').write('port: 4444\nmotd: don\'t spam')
with open('config.yml','r') as f:
dump = yaml.load(f.read())
motd = dump['motd']
port = dump['port']
reactor.listenTCP(port,Main(motd=motd))
reactor.run()
I was wondering how I would be able to connect to it? I've tried adapting their example Echo client and Echo server, but my server only gives a giant error when data is sent back to it.
(The echo server is here and the echo client is here)
The client I am using is
from twisted.internet import reactor
from twisted.internet.protocol import Protocol,ClientFactory
class Main(Protocol):
def dataReceived(self,data):
print data
self.transport.write(data)
class MainFactory(ClientFactory):
def buildProtocol(self,addr):
print 'connected'
return Main()
def clientConnectionLost(self,connector,reason):
print 'connection lost'
def clientConnectionFailed(self,connector,reason):
print 'connection failed'
reactor.connectTCP('localhost',4444,MainFactory())
reactor.run()
Here is a picture of the error
What do I need to do to send data back to the server? What class do I need to inherit from?
The problem is a simple typo.
LineReceiver calls its lineReceived method on each line. You're supposed to override that. But you don't, you define lineRecieved instead. So, you get the default implementation, which raises NotImplemented.
If you fix that, your code is still more than a little odd. Trace through the communication.
The client connects, which calls the server's User.connectionMade, which does this:
self.sendLine('username:')
So the client gets that in Main.dataReceived and does this:
self.transport.write(data)
So, it's sending the prompt back as a response.
The server will receive that in lineReceived (once you fix the name) and do this:
if self.name == None:
self.setName(line)
So, you're going to set the username to 'username:'.
Here is my current code:
#!/usr/bin/env python
from twisted.application import internet, service
from twisted.application.service import IServiceMaker, MultiService
from twisted.protocols import basic
from twisted.internet import reactor, protocol, defer
from twisted.internet.protocol import DatagramProtocol
import datetime
class WebPUSH(basic.LineReceiver):
logTemplate = '''
<script type="text/javascript">
pushHandler.addLi('%s')
</script>
'''
def __init__(self):
self.gotRequest = False
def lineReceived(self, line):
if not self.gotRequest:
self.startResponse()
self.gotRequest = True
def startResponse(self):
self.sendLine('HTTP/1.1 200 OK')
self.sendLine('Content-Type: text/html; charset=utf-8')
self.sendLine('')
f = open('index.html', 'r')
self.transport.write( ''.join(f.read()) )
f.close()
self.logTime()
def logTime(self):
self.sendLine( self.logTemplate % datetime.datetime.now() )
#reactor.callLater(2, self.logTime)
class Echo(DatagramProtocol):
def datagramReceived(self, data, (host, port)):
WebPUSH.logTime()
print "received %r from %s:%d" % (data, host, port)
self.transport.write(data, (host, port))
if __name__ == '__main__':
f = protocol.ServerFactory()
f.protocol = WebPUSH
reactor.listenTCP(8080, f)
reactor.listenUDP(9999, Echo())
reactor.run()
As you can see, I am trying to call a method in WebPUSH from Echo when data is received. Because I never actually instantiate WebPUSH it doesn't look like I can easily call this method. I tried converting this to use a multiservice method but that didn't seem to work although I am sure I am doing something wrong.
There aren't (as far as I could google) any good examples on multiservice with twisted or atleast one like this.
Any help will be appreciated.
Echo instance needs an instance of WebPUSH protocol to call .logTime().
You need to maintain loggers list, e.g., in main():
...
f.loggers = []
echo = Echo()
echo.loggers = f.loggers
...
Add to it in WebPUSH.connectionMade, remove in WebPUSH.connectionLost:
def connectionMade(self):
self.factory.loggers.append(self)
def connectionLost(self, reason):
self.factory.loggers.remove(self)
then in Echo.datagramReceived:
for time_logger in self.loggers:
time_logger.logTime()
user970077, the example in my blog was meant to be a simplified demo to show how the webpush work. Here is what you are trying to do (incorporating JFB suggestions):
#!/usr/bin/env python
from twisted.protocols import basic
from twisted.internet import reactor, protocol, defer
import datetime
class WebPUSH(basic.LineReceiver):
logTemplate = '''
<script type="text/javascript">
pushHandler.addLi('%s')
</script>
'''
def connectionMade(self):
self.factory.listeners.append(self)
self.startResponse()
def connectionLost(self, reason):
self.factory.listeners.remove(self)
def lineReceived(self, line):
self.sendLine( self.logTemplate % line )
def startResponse(self):
self.sendLine('HTTP/1.1 200 OK')
self.sendLine('Content-Type: text/html; charset=utf-8')
self.sendLine('')
with open('index.html', 'r') as f:
self.transport.write( ''.join(f.read()) )
class WebPushFactory(protocol.ServerFactory):
protocol = WebPUSH
def __init__(self):
self.listeners = []
class Echo(protocol.DatagramProtocol):
def __init__(self, listeners):
self.listeners = listeners
def datagramReceived(self, data, (host, port)):
msg = '[%s:%s] %s' % (host, port, data)
for listener in self.listeners:
# udp is not necessarily line-oriented
# so we can:
# 1) feed dataReceived and wait until the line
# delimiter arrives in an udp package
# triggering lineReceived:
#listener.dataReceived( msg )
# or 2) fake a line by calling lineReceived direclty:
listener.lineReceived( msg )
print msg
self.transport.write(data, (host, port))
if __name__ == '__main__':
web = WebPushFactory()
reactor.listenTCP(8080, web)
reactor.listenUDP(9999, Echo(web.listeners))
reactor.run()
And a client to test it (taken from UDP client and server with Twisted Python):
#!/usr/bin/env python
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
from twisted.internet.task import LoopingCall
import sys, time
class HeartbeatSender(DatagramProtocol):
def __init__(self, name, host, port):
self.name = name
self.loopObj = None
self.host = host
self.port = port
def startProtocol(self):
# Called when transport is connected
# I am ready to send heart beats
self.loopObj = LoopingCall(self.sendHeartBeat)
self.loopObj.start(2, now=False)
def stopProtocol(self):
"Called after all transport is teared down"
pass
def datagramReceived(self, data, (host, port)):
print "received %r from %s:%d" % (data, host, port)
def sendHeartBeat(self):
self.transport.write(self.name, (self.host, self.port))
if __name__ == '__main__':
sender = HeartbeatSender("sender", "127.0.0.1", 9999)
reactor.listenMulticast(9998, sender, listenMultiple=True)
reactor.run()
I've written a Twisted based server and I'd like to test it using twisted as well.
But I'd like to write a load test starting a bunch of request at the same time.
But I believe that I didn't get the concepts of Twisted, mainly client side, because I'm stucked with this problem:
from twisted.internet import reactor, protocol
from threading import Thread
from twisted.protocols.basic import LineReceiver
__author__="smota"
__date__ ="$30/10/2009 17:17:50$"
class SquitterClient(LineReceiver):
def connectionMade(self):
self.sendLine("message from " % threading.current_thread().name);
pass
def connectionLost(self, reason):
print "connection lost"
def sendMessage(self, msg):
for m in [ "a", "b", "c", "d", "e"]:
self.sendLine(msg % " - " % m);
class SquitterClientFactory(protocol.ClientFactory):
protocol = SquitterClient
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
reactor.stop()
def createAndRun():
f = SquitterClientFactory()
reactor.connectTCP("localhost", 4010, f)
reactor.run(installSignalHandlers=0)
# this connects the protocol to a server runing on port 8000
def main():
for n in range(0,10):
th=Thread(target=createAndRun)
th.start()
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()
socket_client.py:35:
DeprecationWarning: Reactor already
running! This behavior is deprecated
since Twisted 8.0
reactor.run(installSignalHandlers=0)
What am I missing?
How to test it?
Thank you,
Samuel
The direct cause for your failure is that you attemp to call run() on the reactor multiple times. You are supposed to ever only call run() once. I think you are expecting to have multiple reactors, each in its own thread, but actually you only have one. The bad thing is that having multiple reactors is difficult or impossible - the good thing is that it's also unnecessary. In fact you don't even need multiple threads. You can multiplex multiple client connections in one reactor almost as easily as you can listen for multiple connections.
Modifying your sample code, something like the following should work. The key idea is that you don't need multiple reactors to do things concurrently. The only thing that could ever be concurrent with the regular Python implementation is I/O anyway.
from twisted.internet import reactor, protocol
from twisted.protocols.basic import LineReceiver
__author__="smota"
__date__ ="$30/10/2009 17:17:50$"
class SquitterClient(LineReceiver):
def connectionMade(self):
self.messageCount = 0
# The factory provides a reference to itself, we'll use it to enumerate the clients
self.factory.n += 1
self.name = "Client %d" %self.factory.n
# Send initial message, and more messages a bit later
self.sendLine("Client %s starting!" % self.name);
reactor.callLater(0.5, self.sendMessage, "Message %d" %self.messageCount)
def connectionLost(self, reason):
print "connection lost"
def sendMessage(self, msg):
for m in [ "a", "b", "c", "d", "e"]:
self.sendLine("Copy %s of message %s from client %s!" % (m, msg, self.name))
if self.factory.stop:
self.sendLine("Client %s disconnecting!" % self.name)
self.transport.loseConnection()
else:
self.messageCount += 1
reactor.callLater(0.5, self.sendMessage, "Message %d" %self.messageCount)
class SquitterClientFactory(protocol.ClientFactory):
protocol = SquitterClient
def __init__(self):
self.n = 0
self.stop = False
def stopTest():
self.stop = True
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
# this connects the protocol to a server running on port 8000
def main():
# Create 10 clients
f = SquitterClientFactory()
for i in range(10):
reactor.connectTCP("localhost", 8000, f)
# Schedule end of test in 10 seconds
reactor.callLater(10, f.stopTest)
# And let loose the dogs of war
reactor.run()
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()