0

I am using TCP with Python sockets, transfering data from one computer to another. However the recv command reads more than it should in the serverside, I could not find the issue.

client.py

while rval:
    image_string = frame.tostring()
    sock.sendall(image_string)
    rval, frame = vc.read()

server.py

while True:
    image_string = ""
    while len(image_string) < message_size:
        data = conn.recv(message_size)
        image_string += data

The length of the message is 921600 (message_size) so it is sent with sendall, however when recieved, when I print the length of the arrived messages, the lengths are sometimes wrong, and sometimes correct.

921600
921600
921923 # wrong
922601 # wrong
921682 # wrong
921600
921600
921780 # wrong

As you see, the wrong arrivals have no pattern. As I use TCP, I expected more consistency, however it seems the buffers are mixed up and somehow recieving a part of the next message, therefore producing a longer message. What is the issue here ?

I tried to add just the relevant part of the code, I can add more if you wish, but the code performs well on localhost but fails on two computers, so there should be no errors besides the transmitting part.

Edit1: I inspected this question a bit, it mentions that all send commands in the client may not be recieved by a single recv in the server, but I could not understand how to apply this to practice.

Rockybilly
  • 2,938
  • 1
  • 13
  • 38

2 Answers2

1

TCP is a stream protocol. There is ABSOLUTELY NO CONNECTION between the sizes of the chunks of data you send, and the chunks of data you receive. If you want to receive data of a known size, it's entirely up to you to only request that much data: you're currently requesting the total length of the data each time, which is going to try to read too much except in the unlikely event of the entire data being retrieved by the first .recv() call. Basically, you need to do something like data = conn.recv(message_size - len(image_string)) to reflect the fact that the amount of remaining data is decreasing.

jasonharper
  • 9,450
  • 2
  • 18
  • 42
  • If it is as you say, I dont even need that, `recv(message_size)` would be enough ? – Rockybilly Oct 23 '17 at 17:54
  • There's no guarantee that a single `recv()` will read all of the requested bytes - it will return less if that's all that have been received over the network so far. – jasonharper Oct 23 '17 at 17:56
  • if it is stream based, shouldn't it read until the buffer is filled with the messages ? And is there a resource where I can read more about this subject, Python documentation falls short in this manner. – Rockybilly Oct 23 '17 at 18:12
  • @Rockybilly Think about it. Messages arrive at your socket. You issue `recv(X)` and you read up to `X` bytes. Less if not enough bytes arrived at your socket. The amount of bytes that arrive at the socket is neither under server's control nor client's control. Client only sends `X` bytes and the TCP guarantees that eventually all those bytes will arrive at the server in correct order. But not when. – freakish Oct 23 '17 at 19:24
  • @freakish So I should recieve in a for loop, where I recieve fixed sizes, and exit when total is reached. But what is the good buffer size, in terms of speed, what is the maximum size ? Is it 64K ? Say I picked 4096, how can I be sure, in a slow connection, all 4096 bytes will be recieved in a single recv operation? – Rockybilly Oct 23 '17 at 23:14
  • @Rockybilly you can't. And as youve already seen recv often returns less then expected. That's why you need another protocol on top of TCP. Say you want to send 10000bytes. What you do is you send two bytes (values up to 64k) with value 10000 and then the content. The server reads first two bytes and now knows that the client is going to send 10000bytes so you recv(4096) in a loop until youve received 10000bytes. – freakish Oct 24 '17 at 06:12
1

Think of TCP as a raw stream of bytes. It is your responsibility to track where you are in the stream and interpret it correctly. Buffer what you read and only extract what you currently need.

Here's an (untested) class to illustrate:

class Buffer:
    def __init__(self,socket):
        self.socket = socket
        self.buffer = b''
    def recv_exactly(self,count):
        #  Could return less if socket closes early...
        while len(self.buffer) < count:
            data = self.socket.recv(4096)
            if not data: break
            self.buffer += data
        ret,self.buffer = self.buffer[:count],self.buffer[count:]
        return ret

The recv always requests the same amount of data and queues it in a buffer. recv_exactly only returns the number of bytes requested and leaves any extra in the buffer.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251