NarūnasK
NarūnasK

Reputation: 4950

Close TCP socket in the Greenlet only after successful transfer

I'm trying here to close my TCP socket only after all data is successfully transferred:

from gevent import monkey; monkey.patch_all()
import gevent
import socket

host = "127.0.0.1"
port = 8888

line_term = "\r\n"

login_str = ["Action: Login",
             "ActionID: 1",
             "Username: ami",
             "Secret: password",
             ]

def login(login_str):
    conn = socket.create_connection([host, port])

    for i in login_str:
        conn.sendall(i + line_term)

    conn.send(line_term)
    # Everything works, as soon as I uncomment this line below :/
    # gevent.sleep(1)
    conn.close()

g1 = gevent.spawn(login, login_str)
g1.join(timeout=2)

It seems, that socket is closed too soon, as I always receive only first two lines of my login_str on the other end. If I uncomment gevent.sleep(1) - everything works as expected. But I doubt that would be the case, if I've tried to send 1000 lines or with the flapping connection, perhaps required timeout would be 2 sec. This is unpredictable and not desirable side effect of async. programming, as far as I see it, so I'm quite sure there must be an easy workaround, which I just can't find?

EDIT

For the sake of completeness I've also added echo server part:

from gevent.server import StreamServer

def connection_handler(socket, address):
    for l in socket.makefile('r'):
        print str(len(l)) + ' : ' + l
        socket.sendall(l)


if __name__ == '__main__':
    server = StreamServer(('0.0.0.0', 8888), connection_handler)
    server.serve_forever()

Upvotes: 1

Views: 680

Answers (1)

Mansour
Mansour

Reputation: 1810

In your server code, you are echoing back to the sender each line that you read (socket.sendall(l)). However, in your client code, you are closing the connection as soon as you have sent all the data which leaves no chance for your server to send data back. If you comment out socket.sendall(l), you will see that you indeed do receive all four lines.

If you do need to send data back, you can modify your client code to wait for server to finish sending all the data:

def login(login_str):
    conn = socket.create_connection([host, port])

    for i in login_str:
        conn.sendall(i + line_term)

    conn.send(line_term)

    # Wait until the other end has finished sending data

    while conn.recv(1024):
      pass

    # Everything works, as soon as I uncomment this line below :/
    # gevent.sleep(1)
    conn.close()

Upvotes: 1

Related Questions