How to correctly relay TCP traffic between sockets?
Question: I'm trying to write some Python code that will establish an invisible relay
between two TCP sockets. My current technique is to set up two threads, each
one reading and subsequently writing 1kb of data at a time in a particular
direction (i.e. 1 thread for A to B, 1 thread for B to A).

This works for some applications and protocols, but it isn't foolproof -
sometimes particular applications will behave differently when running through
this Python-based relay. Some even crash.

I think that this is because when I finish performing a read on socket A, the
program running there considers its data to have already arrived at B, when in
fact I - the devious man in the middle - have yet to send it to B. In a
situation where B isn't ready to receive the data (whereby `send()` blocks for
a while), we are now in a state where A believes it has successfully sent data
to B, yet I am still holding the data, waiting for the `send()` call to
execute. I think this is the cause of the difference in behaviour that I've
found in some applications, while using my current relaying code. Have I
missed something, or does that sound correct?

If so, my real question is: is there a way around this problem? Is it possible
to only read from socket A when we know that B is ready to receive data? Or is
there another technique that I can use to establish a truly 'invisible' two-
way relay between [already open & established] TCP sockets?


Answer: > Is it possible to only read from socket A when we know that B is ready to
> receive data?

Sure: use
[select.select](http://docs.python.org/library/select.html?highlight=select#select.select)
on both sockets A and B (if it returns saying only one of them is ready, use
it on the other one), and only read from A and write to B when you know
they're both ready. E.g.:

    
    
    import select
    
    def fromAtoB(A, B):
        r, w = select.select([A], [B], [])
        if not r: select.select([A], [], [])
        elif not w: select.select([], [B], [])
        B.sendall(A.recv(4096))
    

