Python Chat Client not Remaining Open - python

I have recently started developing a server-client chat protocol for learning purposes (later I would like to do more with this communication, but for now this suffices. Needless to say, I am still early into the learning phase of this portion of Python, but I have modified some examples to a server and a client that I found online. The communication works well from what I've seen so far, but I have to relaunch the client every time I want to send a message to the server.
Here is the code:
server:
from twisted.internet import reactor, protocol
from twisted.protocols import basic
class Echo(protocol.Protocol):
def dataReceived(self, data):
"As soon as any data is received, write it back."
self.transport.write(data)
class MyChat(basic.LineReceiver):
def connectionMade(self):
print "Got new client!"
self.factory.clients.append(self)
def connectionLost(self, reason):
print "Lost a client!"
self.factory.clients.remove(self)
def dataReceived(self, data):
print "received", repr(data)
for c in self.factory.clients:
c.message(data)
def message(self, message):
self.transport.write(message + '\n')
def main():
"""This runs the protocol on port 8000"""
factory = protocol.ServerFactory()
factory.protocol = MyChat
factory.clients = []
reactor.listenTCP(8000,factory)
reactor.run()
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()
client:
from twisted.internet import reactor, protocol
# a client protocol
class EchoClient(protocol.Protocol):
"""Once connected, send a message, then print the result."""
def connectionMade(self):
self.transport.write("hello, world!")
def dataReceived(self, data):
"As soon as any data is received, write it back."
print "Server said:", data
self.transport.loseConnection()
def connectionLost(self, reason):
print "connection lost"
class EchoFactory(protocol.ClientFactory):
protocol = EchoClient
def clientConnectionFailed(self, connector, reason):
connector.connect()
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
connector.connect()
print "Connection lost - goodbye!"
reactor.stop()
# this connects the protocol to a server runing on port 8000
def main():
f = EchoFactory()
client = EchoClient()
reactor.connectTCP("localhost", 8000, f)
reactor.run()
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()
What am I forgetting to add so that I can have multiple clients connected to the server and stay connected?
I looked here and here (the first one seems to be the same type of question) but I'm still confused as to how to fix this issue. Any suggestions are appreciated. Thanks!

Related

Python Twiested, where to read client input

I have the following code:
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
An example client. Run simpleserv.py first before running this.
"""
from twisted.internet import reactor, protocol
# a client protocol
class EchoClient(protocol.Protocol):
"""Once connected, send a message, then print the result."""
def connectionMade(self):
self.transport.write("Welcome to Calculator!")
# data = ''
def dataReceived(self, data):
"As soon as any data is received, write it back."
print "Server said:\n", data
# self.transport.loseConnection()
def connectionLost(self, reason):
print "connection lost"
class EchoFactory(protocol.ClientFactory):
protocol = EchoClient
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
reactor.stop()
# this connects the protocol to a server running on port 8000
def main():
help(protocol.Protocol)
exit()
f = EchoFactory()
reactor.connectTCP("localhost", 8000, f)
reactor.run()
print 'here'
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()
What is the correct way to read client input and send it to server? I want to read the data using data = input() and after it send to server self.transport.write(data). But, where I have to put it on my code, do I have to create another method or use connectionMade?
Remembering, that is a persistent connection where client send something to server, then the server process it and send something to client. And again client send something to server, server process it and send to client... (repeatedly)
def connectionMade(self):
# Asks user for their name
name = input('What is your name?')
# sends name to server
self.transport.write(name)
This will be speicifc to each client/connection, so you should only have one connectionMade method.

Having trouble with a simple Twisted chat server

When I try and run this (see code below) I get the "connection made" response from the server and the command prompt to write an input. However when I try and enter the input it just hangs and the server doesn't seem to receive the message. Anyone know why this is?
Thanks, please say if this isn't clear enough
Here is my chat server:
from twisted.protocols import basic
class MyChat(basic.LineReceiver):
def connectionMade(self):
print "Got new client!"
self.factory.clients.append(self)
def connectionLost(self, reason):
print "Lost a client!"
self.factory.clients.remove(self)
def lineReceived(self, line):
print "received", repr(line)
for c in self.factory.clients:
c.message(line)
def message(self, message):
self.transport.write(message + '\n')
from twisted.internet import reactor, protocol
from twisted.application import service, internet
factory = protocol.ServerFactory()
factory.protocol = MyChat
factory.clients = []
reactor.listenTCP(8004, factory)
reactor.run()
and here is my client:
from twisted.internet import reactor, protocol
# a client protocol
class EchoClient(protocol.Protocol):
def sendData(self):
data = raw_input("> ")
if data:
print "sending %s...." % data
self.transport.write(data)
else:
self.transport.loseConnection()
def connectionMade(self):
self.sendData()
def dataReceived(self, data):
print data
self.sendData()
def connectionLost(self, reason):
print "connection lost"
class EchoFactory(protocol.ClientFactory):
protocol = EchoClient
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
reactor.stop()
# this connects the protocol to a server runing on port 8000
def main():
f = EchoFactory()
reactor.connectTCP("localhost", 8004, f)
reactor.run()
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()
You made a mistake in the client. Basically, server expects to receive lines, meaning data terminated by newline. However, client sends data without newline character at the end.
So, to fix client, just add \r\n to the data:
self.transport.write(data + "\r\n")
Here is client protocol:
class EchoClient(protocol.Protocol):
def sendData(self):
data = raw_input("> ")
if data:
print "sending %s...." % data
self.transport.write(data + "\r\n")
else:
self.transport.loseConnection()
def connectionMade(self):
self.sendData()
def dataReceived(self, data):
print data
self.sendData()
def connectionLost(self, reason):
print "connection lost"

How do I pipe stdout of cmd1 to stdin of cmd2 in twisted where cmd1 process is on a twisted client and cmd2 is a process on a server?

How do i connect the stdout of a spawnProcess to the stdin of another spawnProcess in twisted, where spawnProcess with the stdout is on the client and the stdin spawnprocess on the server? The CLI command in bash is btrfs send #mysubvol | btrfs receive /some/path/. With rfd, wrd = os.pipe() I managed to pipe process1 to process2 on the server side. (Now I want to pipe it from the client to the server instead). Following code shows the processes piped on the same side:
Following code
from twisted.internet import protocol
from twisted.internet import reactor
import os
class Writer(protocol.ProcessProtocol):
def connectionMade(self):
print "Writer -- connection made"
self.transport.closeChildFD(0)
def childDataReceived(self, fd):
pass
def processEnded(self, status):
pass
class Reader(protocol.ProcessProtocol):
def __init__(self):
pass
def connectionMade(self):
print "Reader -- connection made"
pass
def childDataReceived(self, fd):
print "Reader -- childDataReceived"
def processEnded(self, status):
print "process ended, got:"
def test2():
rfd, wfd = os.pipe()
p1 = reactor.spawnProcess(Writer(), "btrfs", ["btrfs", "send", "/#mySubvol"],env=None, childFDs={0:"w", 1: wfd })
p2 = reactor.spawnProcess(Reader(), "btrfs", ["btrfs", "receive", "/subvolContainer/"], env=None, childFDs={0: rfd, 1: "r"})
os.close(rfd)
os.close(wfd)
reactor.run()
test2()
I tried:
server.py
from twisted.internet.protocol import Protocol, Factory, ClientFactory
from twisted.internet import protocol
from twisted.internet import reactor
import os
class Reader(protocol.ProcessProtocol):
def __init__(self):
pass
def connectionMade(self):
print "Reader -- connection made"
pass
def childDataReceived(self, fd):
print "Reader -- childDataReceived"
def processEnded(self, status):
print "process ended, got:"
class EchoClientFactory(ClientFactory):
protocol = Reader
def clientConnectionFailed(self, connector, reason):
print 'connection failed:', reason.getErrorMessage()
reactor.stop()
def clientConnectionLost(self, connector, reason):
print 'connection lost:', reason.getErrorMessage()
reactor.stop()
def main():
f = Factory()
reactor.listenTCP(8000, f)
rfd = os.pipe()
p2 = reactor.spawnProcess(Reader(), "btrfs", ["btrfs", "receive", "/"], env=None, childFDs={0: rfd, 1: "r"})
os.close(rfd)
reactor.run()
if __name__ == '__main__':
main()
client.py
from twisted.internet import reactor
from twisted.internet import protocol
import sys
import os
class Writer(protocol.ProcessProtocol):
def connectionMade(self):
print "Writer -- connection made"
self.transport.closeChildFD(0)
def childDataReceived(self, fd):
pass
def processEnded(self, status):
pass
class EchoClientFactory(protocol.ClientFactory):
protocol = Writer
def clientConnectionFailed(self, connector, reason):
print 'connection failed:', reason.getErrorMessage()
reactor.stop()
def clientConnectionLost(self, connector, reason):
print 'connection lost:', reason.getErrorMessage()
reactor.stop()
def main():
factory = EchoClientFactory()
rfd, wfd = os.pipe()
p1 = reactor.spawnProcess(Writer(), "btrfs", ["btrfs", "send", "/home/philipp/testEnv/a2a/#a2"], env=None, childFDs={0:"w", 1: wfd })
reactor.connectTCP('localhost', 8000, factory)
os.close(wfd)
reactor.run()
if __name__ == '__main__':
main()
Obviously my attempt is wrong, because the server does not know about the client's stdout pipe, but I don't know how to pipe the client's spawnProcess stdout to the server.
Update 01:
Following Jean-Paul's answer I created two protocols on the client and server (ProcessProtocol and TCP-Protocol on each side). I could successfully send a snapshot from the client to the server. On the client I had to start the ProcessProtocol with the instance of my TCP-Protocol, so that they are both interconnected. See: http://twistedmatrix.com/trac/wiki/FrequentlyAskedQuestions#HowdoImakeinputononeconnectionresultinoutputonanother
client.py
from twisted.internet.protocol import Protocol, Factory, ClientFactory, ProcessProtocol
from twisted.internet import reactor
import sys
import os
class Writer(Protocol):
def connectionMade(self): # Called when a connection is made
print "connec made"
proc = MyProcessProtocol(self)
p1 = reactor.spawnProcess(proc, "btrfs", ["btrfs", "send", "/home/user/testEnv/a2a/#a2"])
class EchoClientFactory(ClientFactory):
protocol = Writer
def clientConnectionFailed(self, connector, reason):
print 'connection failed:', reason.getErrorMessage()
reactor.stop()
def clientConnectionLost(self, connector, reason):
print 'connection lost:', reason.getErrorMessage()
reactor.stop()
class MyProcessProtocol(ProcessProtocol):
def __init__(self, instance):
self.w = instance
def outReceived(self, data): # Some data was received from stdout
self.w.transport.write(data) # Write some data to the physical connection, in sequence, in a non-blocking fashion
def main():
factory = EchoClientFactory()
reactor.connectTCP('localhost', 8000, factory)
reactor.run()
if __name__ == '__main__':
main()
server.py
from twisted.internet.protocol import Protocol, Factory, ClientFactory, ProcessProtocol, ServerFactory
from twisted.internet import reactor
import os
class Reader(Protocol):
def connectionMade(self):
print "connected"
self.r2 = Reader2()
p1 = reactor.spawnProcess(self.r2, "btrfs", ["btrfs", "receive", "/"])
def dataReceived(self, data):
print "dataReceived"
self.r2.transport.write(data)
class Reader2(ProcessProtocol):
def connectionMade(self):
print "connectionMade!"
def processEnded(self, reason):
print "quitting"
def main():
f = ServerFactory()
f.protocol = Reader
reactor.listenTCP(8000, f)
reactor.run()
if __name__ == '__main__':
main()
You cannot set up a pipe across machines. You can't do this in a shell either. The shell expression:
btrfs send #mysubvol | btrfs receive /some/path/
runs two btrfs processes on a single machine with a pipe connecting them.
Pipes are purely local. They cannot be shared across machines. For this, you need something else. For example, a TCP connection.
You have taken a couple steps in the right direction. Your server starts a TCP server. Your client attempts to establish a new connection to that server.
But your server doesn't define any behavior for handling connections it accepts. And your client uses a ProcessProtocol to define its behavior - when a TCP connection is not a process.
One idea you may have missed is that on your client you will need two protocols. One protocol is connected to the btrfs send process and reads its stdout. Another protocol is connected to your server and can write that btrfs send output to the TCP connection.
And your server will need two protocols as well. One of them handles the connection from the client and reads the data the client is writing to that connection. The other is connected to a btrfs receive process and writes the data read from the TCP connection to that process's stdin.
You've found the childFDs feature of spawnProcess which is a nice local optimization. And while you can technically use it to help you connect two processes on two different machines, it involves extra steps that you probably don't want to bother with (at least not until you're comfortable with a "regular" forwarder).
Instead, you just want a handful of protocols that receive data from objects nearer the sending side of your setup (via childDataReceived and dataReceived) and hand it off to objects nearer the receiving side of your setup (via transport.write).

Twisted client connection lost

I have a very simple client program:
class EchoClient(Int32StringReceiver):
def connectionMade(self):
print 'connection made.'
str = "<request><commands><dbtest /></commands></request>"
self.sendString(str)
print 'message sent'
def stringReceived(self, line):
print "receive:", line
self.transport.loseConnection()
class EchoClientFactory(ClientFactory):
def buildProtocol(self, addr):
return EchoClient()
def clientConnectionFailed(self, connector, reason):
print 'connection failed:', reason.getErrorMessage()
reactor.stop()
def clientConnectionLost(self, connector, reason):
print 'connection lost:', reason.getErrorMessage()
reactor.stop()
def main():
factory = EchoClientFactory()
reactor.connectTCP('localhost', 3604, factory)
reactor.run()
I connect to a Java service implemented in Apache CXF (and some proprietary company code).
It connects fine, sends the message, the service receives it and produces a response.
Sadly, this client does not wait for the server to produce its message, but disconnects right after the message is sent. So the output I get from the client is:
connection made.
message sent
connection lost: Connection was closed cleanly.
And of course the Java service throws an exception complaining about the connection being already closed.
What am I missing here?
EDIT: adding this line shows that the message is received, as it prints it correctly:
def dataReceived(self, data):
print(data)
self.transport.loseConnection()
So the real problem is that the stringReceived() function is not called. Maybe I have the wrong signature for this function?
I'm onto something here:
def lengthLimitExceeded(self, length):
print('length limit exceeded: {}'.format(length))
prints:
length limit exceeded: 2147483793
length limit exceeded: 2147483793
which is 0x80000091, so it seems that our propietary API is implementing the NString protocol in a strange way (maybe uses the MSB for something else).

Python Twisted Client

I have this simple Twisted Client which connects to a Twisted server & queries an index.
If you see fn. connectionMade() in class SpellClient, the query is hard-coded. Did that for testing purposes. How would one pass this query from outside to this class?
The code -
from twisted.internet import reactor
from twisted.internet import protocol
# a client protocol
class SpellClient(protocol.Protocol):
"""Once connected, send a message, then print the result."""
def connectionMade(self):
query = 'abased'
self.transport.write(query)
def dataReceived(self, data):
"As soon as any data is received, write it back."
print "Server said:", data
self.transport.loseConnection()
def connectionLost(self, reason):
print "connection lost"
class SpellFactory(protocol.ClientFactory):
protocol = SpellClient
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
reactor.stop()
# this connects the protocol to a server runing on port 8000
def main():
f = SpellFactory()
reactor.connectTCP("localhost", 8090, f)
reactor.run()
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()
Protocols, like SpellClient, have access to their factory as self.factory.
...so there would be a number of ways to do this, but one way would be to create another method on SpellFactory, such as setQuery, and then access that from the client...
#...in SpellFactory:
def setQuery(self, query):
self.query = query
#...and in SpellClient:
def connectionMade(self):
self.transport.write(self.factory.query)
...so in main:
f = SpellFactory()
f.setQuery('some query')
...
...or you could just create an _init_ method for SpellFactory, and pass it in there.

Categories