Search code examples
pythonflutterdartsocketstcp

Why does my Python TCP server only receive the data once the client socket connection is closed?


I am trying to send an image from a Flutter app to my TCP server written in Python. I have this tcp server code:

import socket
import os

# Server configuration
HOST = '192.168.1.67'
PORT = 8000

def save_image(data):
    len_files = len([name for name in os.listdir('.') if os.path.isfile(name)])
    filename = f'received_image-{len_files}.jpg'
    with open(filename, 'wb') as f:
        f.write(data)
    print('Image saved to', filename)
    

# Create a TCP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Bind the socket to the specified host and port
server_socket.bind((HOST, PORT))

# Listen for incoming connections (1 connection at a time)
server_socket.listen(1)
print('Server listening on', HOST, 'port', PORT)

# Wait for a client to connect
print('Waiting for a connection...')
client_socket, address = server_socket.accept()
print('Accepted connection from', address)

# Receive and save the images from the client
received_data = b''
while True:
    try:
        data = client_socket.recv(1024)
        print('Receiving data...')
        print('Length of data received:', len(data))
    except Exception as e:
        print('An error occurred while receiving data:', e)
        break
    if not data:
        break

    # Save the received data
    received_data += data

print('Data received')

# Save the received image
save_image(received_data)

# Close the client connection
client_socket.close()
print('Connection closed')

Then, I send an image from my flutter app like this:

if (_socket == null) {
  print("Socket not connected");
  return;
}

print("Taking picture...");
final img = await _controller.takePicture();
print("Picture taken");
final imgBytes = await img.readAsBytes();
print("Sending image...");
_socket!.add(imgBytes);
print("Image sent");

Currently the expected result is the following:

  1. I start the TCP server
  2. I connect to the TCP server from the Flutter app
  3. I take one picture at a time in the app then instantly send that one to the TCP server, which then saves it in the current directory
  4. I repeat step 3. a few times
  5. Once I'm done I close the app and the socket connection.

However, at the moment, once the server receives an image, it starts logging receiving data and the length of the current chunk, but once it reaches the last chunk stops, and continues only when the connection is closed from the app side, which can either be done by closing the app, or adding the following line of code:

_socket!.close();

I could solve this by creating a new socket connection every time I want to send a picture and then handling every picture separately, however, I don't understand why it is not working the way I expect it to? Thanks in advance (I believe other parts of the Flutter code is not neccessary since the connection happens and I am able to send the pictures to the server)


Solution

  • ... but once it reaches the last chunk stops

    The server does not know that this is the last chunk. It only knows when it receives data and it knows when the connection is closed, i.e. not data.

    if not data:
        break
    

    Since it does not know when the image is done it can only wait until more data come in (which are assumed to belong to the same image) or the connection is closed.

    If you want to send multiple images over the same TCP connection you need to implement an application protocol on top of TCP which allows to distinguish between multiple messages (the images) within the same byte stream. There are many questions here about this.