I'm writing an application with Twisted/GTK to manage a serial device,
for use on both Windows and Linux. Unfortunately I can't use serial
comms directly under Windows[1], so my workaround is to launch
socat[2] in a subprocess to relay data between a local TCP port and
the serial port. The socat command I use is:
socat TCP4-LISTEN:31415 /dev/com4,raw,echo=0,b57600
I can then send and receive data over localhost:31415 same as I would
for a serial port.
The trouble is, a transaction that takes a minute under Linux (using
*either* direct serial comms or the same socat subprocess approach)
now takes more than 50 times as long under Windows.
I've attached the script I'm using to test, mainly so it's clear what
I'm doing — although it's useless unless you happen to have a serial
device using a protocol you can shoehorn into that script. I've also
attached the output where you can see the difference between different
reactors on different platforms. (In my app I use
reactor.spawnProcess(...), but I'm not doing that here.)
Basically, under Linux either approach takes 4 seconds; under Windows
the SelectReactor takes 30s, and the gtk2reactor takes 220s!
Profiling the "reactor.run()" call on the different platforms (both
for the select reactor) showed that (a) most time was spent in the
select.select call, and (b) the time spent in select was 8 times more
under Windows than Linux. I have no idea if this is useful information
or not.
I'm pretty sure it's not a socat-specific issue, because I get the
same problem if I use com2tcp[3]. It could be Cygwin related, but I
don't know how determine that for sure. Also, the fact that it runs
slower using the gtk2reactor (vs. selectreactor) suggests to me that
it could be my code, not theirs.
I found an old Cygwin thread[4] that claimed setting NODELAY on the
listening socket helped with a similar problem, but using
> socat TCP4-LISTEN:31415,nodelay [etc]
...made no difference.
So I'm a bit stuck. Does anyone know how I can narrow it down further?
Has someone else noticed slow TCP behaviour under Windows? Or Cygwin
utilities? Have I made some glaringly obvious mistake in my script
there?
Any help appreciated. :)
Cheers,
Jason
[1] http://twistedmatrix.com/trac/ticket/4862
[2] http://www.dest-unreach.org/socat/
[3] http://com0com.sourceforge.net/
[4] http://www.mail-archive.com/[email protected]/msg66791.html
====
WINDOWS
====
>python socat_test.py
Using reactor: SelectReactor
Connection made!
Progress: 0.00
Progress: 0.10
Progress: 0.20
Progress: 0.30
Progress: 0.40
Progress: 0.50
Progress: 0.60
Progress: 0.70
Progress: 0.80
Progress: 0.90
Progress: 1.00
Time: 28.279000
>python socat_test.py
Using reactor: PortableGtkReactor
Connection made!
Progress: 0.00
Progress: 0.10
Progress: 0.20
Progress: 0.30
Progress: 0.40
Progress: 0.50
Progress: 0.60
Progress: 0.70
Progress: 0.80
Progress: 0.90
Progress: 1.00
Time: 222.143000
====
LINUX
====
$ python socat_test.py
Using reactor: SelectReactor
Connection made!
Progress: 0.00
Progress: 0.10
Progress: 0.20
Progress: 0.30
Progress: 0.40
Progress: 0.50
Progress: 0.60
Progress: 0.70
Progress: 0.80
Progress: 0.90
Progress: 1.00
Time: 4.030676
$ python socat_test.py
Using reactor: Gtk2Reactor
Connection made!
Progress: 0.00
Progress: 0.10
Progress: 0.20
Progress: 0.30
Progress: 0.40
Progress: 0.50
Progress: 0.60
Progress: 0.70
Progress: 0.80
Progress: 0.90
Progress: 1.00
Time: 4.032430
import time
from twisted.internet.protocol import ClientCreator, BaseProtocol
from twisted.internet import defer
SYNC = "*"
PROMPT = ">"
class SyncProtocol(BaseProtocol):
def __init__(self, number, progress_cb=None):
self.deferred = defer.Deferred()
self.total = number
self.number = 0
if progress_cb is not None:
self.progress = progress_cb
def progress(self, frac):
pass
def check(self):
return self.deferred
def dataReceived(self, data):
for octet in data:
if data != PROMPT:
self.sessionError(Exception("Non-prompt character"))
break
else:
self.number += 1
self.progress(float(self.number) / self.total)
if self.number >= self.total:
self.sessionComplete()
break
self.transport.write(SYNC)
def connectionMade(self):
print "Connection made!"
self.start = time.time()
self.progress(0.)
self.transport.write(SYNC)
def sessionComplete(self):
self.finishSession()
self.deferred.callback(self.end - self.start)
def sessionError(self, err):
self.finishSession()
self.deferred.errback(err)
def finishSession(self):
self.end = time.time()
self.transport.loseConnection()
def connectionLost(self, reason):
pass
class ProgressPrinter(object):
inc = 0.1
def __init__(self):
self.milestone = 0
def __call__(self, frac):
if frac > self.milestone:
print "Progress: %.2f" % frac
self.milestone += self.inc
def print_error(failure):
print failure.getErrorMessage()
return failure
def print_time(duration):
print "Time: %f" % duration
def go(reac):
print "Using reactor: %s" % type(reac).__name__
prc = ClientCreator(reac, SyncProtocol, 1000, ProgressPrinter())
d = prc.connectTCP("localhost", 31415)
d.addCallback(lambda prot: prot.check())
d.addCallback(print_time)
d.addErrback(print_error)
d.addBoth(lambda _: reac.stop())
# In Windows: socat TCP4-LISTEN:31415 /dev/com4,raw,echo=0,b57600
# In Linux: socat TCP4-LISTEN:31415 /dev/ttyUSB0,raw,echo=0,b57600
if __name__ == "__main__":
# from twisted.internet import gtk2reactor
# gtk2reactor.install()
from twisted.internet import reactor
reactor.callWhenRunning(go, reactor)
reactor.run()
_______________________________________________
Twisted-Python mailing list
[email protected]
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python