BlooMeth
BlooMeth

Reputation: 101

Looking to emulate the functionality of socat in Python

I need to send a string to a particular port on localhost using python.

I can achieve this by using socat on the command line like such:

cat <text file containing string to send> | socat stdin tcp-connect:127.0.0.1:55559

I don't want to run the socat command as a subcommand so I'd like to know how to get this working using a python module.

My attempts at using sockets failed. Below is a script I was using which wasn't achieving the desired effect.

    import socket
    HOST, PORT = "127.0.0.1", 55559
    jsonString = '{"name":"VideoStreamStatus","parameters":{"name":"video_0_0"}}'

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        # Connect to server and send data
        print "Attempting connection"
        sock.connect((HOST, PORT))
        print "Sending json string"
        sock.sendall(jsonString)
        print "String sent"

    finally:
        sock.close()

    print "Sent:     {}".format(jsonString)

The python script was executing without any errors but there wasn't any effect on the process which was receiving the socat data. Python script output below:

    Attempting connection
    Sending json string
    Sent:     {"name":"VideoStreamStatus","parameters":{"name":"video_0_0"}}

Is there something I'm doing wrong in the socket script or is there a different method I can use in Python? I'd appreciate any advice. Thanks.

Edit: The server I'm trying to send strings to is part of a C++ program using boost asio acceptor to listen for tcp connections on a specific address and port.

Upvotes: 6

Views: 9953

Answers (3)

BlooMeth
BlooMeth

Reputation: 101

Turned out to be something really simple. The string I was sending using socat (which the server was receiving and processing successfully) had a newline on the end of it. The string I was sending over the python socket didn't have a new line. After adding a new line to the end of the python script the server receives the string as expected.

I only realised that there was a newline after one and not the other after running a python server script listening on a different port and sending the string using both methods.

Upvotes: 2

Serge Ballesta
Serge Ballesta

Reputation: 149145

You shall never close a socket immediately after writing into a socket: the close could swallow still unsent data.

The correct workflow is what is called a graceful shutdown:

  • the part that wants to close the connection uses a shutdown-write on the socket and keeps on reading
  • the other part detects a 0 bytes read from the socket which is sees as an end of file. It closes its socket
  • the initiator detects a 0 bytes read and can safely close the socket

So the finally part becomes:

finally:
  try:
    sock.shutdown(socket.SHUT_WR)
    while True:
        dummy = sock.recv(1024)
        if len(dummy) == 0: break
  finally:
    sock.close()

Upvotes: 0

Robᵩ
Robᵩ

Reputation: 168806

According to the socat man page,

When one channel has reached EOF, the write part of the other channel is shut down. Then, socat waits timeout seconds before terminating. Default is 0.5 seconds.

Perhaps you need to defer your close by 0.5 seconds, like so:

finally:
    #sock.close()
    sock.shutdown(socket.SHUT_WR)
    time.sleep(0.5)
    sock.close()

Or, perhaps you need to absorb the server's shutdown message, like so:

finally:
    #sock.close()
    sock.shutdown(socket.SHUT_WR)
    sock.recv(999999)
    sock.close()

Upvotes: 0

Related Questions