Search code examples
pythonjsonsocketstcpstreaming

Error when sending json data with python sockets


I can't seem to find the problem here. Please help. I want to include each word in json format and stream it to the server and the server should stream it to all clients connected. Its not being streamed to the server or other clients.

Here is my client side code:

import socket
import json
import threading

# Define the server's IP address and port
SERVER_IP = '44.216.25.181'
SERVER_PORT = 55555

# Create a socket for the client
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((SERVER_IP, SERVER_PORT))

# Function to receive messages from the server
def receive_messages():
    while True:
        try:
            message = client_socket.recv(1024)
            if not message:
                break

            decoded_message = json.loads(message.decode())
            message_type = decoded_message.get('type')
            data = decoded_message.get('data')`
        
            
            if message_type == 'stream_text':
                # Handle streamed text
                print(f"Received streamed text: {data.get('chunk')}")

        except:
            break

# Start a thread to receive messages from the server
receive_thread = threading.Thread(target=receive_messages)
receive_thread.start()

# Example method for sending a JSON message to the server
def send_message(type, data):
    try:
        message = {'type': type, 'data': data}
        json_data = json.dumps(message)
        print(json_data)
        client_socket.send(json_data.encode())

    except Exception as e:
        print("e")
        print("Connection to the server is closed.")
        client_socket.close()
        
def printEvenLengthWords(s):
# splitting the words in a given string
words = s.split() # same as s.split(' ')
for word in words:
        print(word)
        send_message('stream_text', word)

# input string
user_input = input("Enter the text : ")

# calling the function
printEvenLengthWords(user_input)

Here is my server side code:

import socket
import threading
import json

# Define the server's IP address and port
SERVER_IP = '0.0.0.0'
SERVER_PORT = 55555

# Create a socket for the server
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((SERVER_IP, SERVER_PORT))
server_socket.listen()

# List to store connected clients
clients = []

# Function to broadcast messages to all connected clients
def broadcast(message):
    for client in clients:
        client.send(message)

# Function to handle client connections
def handle_client(client_socket):
    while True:
        try:
            # Receive data from the client
            message = client_socket.recv(1024)
            if not message:
                break

            # Decode the JSON message
            decoded_message = json.loads(message.decode())

            # Differentiate between message types
            message_type = decoded_message.get('type')
            data = decoded_message.get('data')

            if message_type == 'stream_text':
                # Handle streamed text
                generated_text = data.get('data')
                print(f"Received streamed text: {generated_text}")

                # Broadcast the streamed text to all clients
                chunk_size = 5
                chunks = [generated_text[i:i + chunk_size] for i in range(0, len(generated_text), chunk_size)]

                for chunk in chunks:
                    chunk_message = json.dumps({'type': 'stream_text', 'data': {'chunk': chunk}})
                    broadcast(chunk_message)
        except:
            # Remove the client from the list if there's an error or disconnection
            clients.remove(client_socket)
            client_socket.close()
            break

# Accept incoming connections and start a new thread for each client
while True:
    client_socket, client_address = server_socket.accept()
    clients.append(client_socket)
    client_thread = threading.Thread(target=handle_client, args=(client_socket,))
    client_thread.start()

When I run both the client side code and server side, I only see the output of json like this:

$ python3 client1.py 
Enter the text : this is a test
this
{"type": "stream_text", "data": "this"}
is
{"type": "stream_text", "data": "is"}
a
{"type": "stream_text", "data": "a"}
test
{"type": "stream_text", "data": "test"}

Solution

  • As mentioned in comments you'll need to buffer receives and extract complete JSON messages. Below is an example that sends a complete JSON message as individual newline-terminated lines of text (JSON Lines format) by using socket.makefile to buffer the socket by wrapping it in a file-like object and using its .readline() method:

    server.py

    import socket
    import json
    
    with socket.socket() as sock:
        sock.bind(('', 5000))
        sock.listen()
        while True:
            client, addr = sock.accept()
            print(f'{addr}: connected')
            # makefile wraps a socket in a file-like object for buffering
            with client, client.makefile('r', encoding='utf8') as infile:
                while True:
                    # Extract a line containing JSON
                    line = infile.readline()
                    if not line: break
                    print(f'{addr}: {json.loads(line)}')
            print(f'{addr}: disconnected')
    

    client.py

    import socket
    import json
    
    def send(sock, data):
        # Send JSON without `indent` parameter so it is a single line.
        # Terminate with a newline for the makefile wrapper in server.
        encoded = json.dumps(data).encode() + b'\n'
        sock.sendall(encoded)
    
    with socket.socket() as sock:
        sock.connect(('localhost', 5000))
        send(sock, {'type': 'whatever', 'data': 'something'})
        send(sock, {'type': 'withnewlines', 'data': 'abc\ndef\nghi\n'})
        send(sock, {'type': 'example', 'data': 123.456})
    

    Run the server and then the client a couple of times:

    ('127.0.0.1', 10320): connected
    ('127.0.0.1', 10320): {'type': 'whatever', 'data': 'something'}
    ('127.0.0.1', 10320): {'type': 'withnewlines', 'data': 'abc\ndef\nghi\n'}
    ('127.0.0.1', 10320): {'type': 'example', 'data': 123.456}
    ('127.0.0.1', 10320): disconnected
    ('127.0.0.1', 10321): connected
    ('127.0.0.1', 10321): {'type': 'whatever', 'data': 'something'}
    ('127.0.0.1', 10321): {'type': 'withnewlines', 'data': 'abc\ndef\nghi\n'}
    ('127.0.0.1', 10321): {'type': 'example', 'data': 123.456}
    ('127.0.0.1', 10321): disconnected