Multiple call/response messages between Twisted server/client - python

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()

Related

lineReceived() not called, only dataReceived

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()

How to be sure that server received full data fron the client (and if not - resend it) using python - twisted?

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) ```

Server client using python twisted

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()

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.

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?

Categories

Resources