Search code examples
pythonsocketstcp

Select returns empty sockets list after a few messages from each client


I am trying to make a basic terminal-based chat application with python. I used select() to update readable/writable connections. After two messages from each client however, the server returns an empty readable list (sockets_list).

I already tried removing code that deletes connections from my sockets list.

this is server.py

import socket
import select
import time

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

# host, port
host = 'localhost'
port = 5555

# bind and listen for connections
sock.bind((host, port))
sock.listen(2)

print('Server started on. {} {}'.format(host, port))

# select lists
sockets_list = [sock]
outputs = []

clients = {}


def receive_message(conn):
    try:
        msg = conn.recv(4096)

        if not len(msg):
            return False

        return msg

    except conn.error as e:
        return False


while True:
    print('\n waiting for the next event')

    readable, writable, exceptions = select.select(sockets_list, outputs, [], 10)

    print(readable, writable)

    for s in readable:
        if s == sock:
            connection, address = s.accept()

            user = receive_message(connection)

            if user is False:
                continue

            sockets_list.append(connection)
            clients[connection] = user

            print(f'Accepted new connection from {user.decode()}')
        else:
            arr = s.recv(4096).decode().split(':')
            if len(arr) < 2:
                arr = s.recv(4096).decode().split(':')

            elif len(arr) >= 2:
                message = arr[1]
                # if message is False:
                #     print(f'Connection closed with {s}')
                #
                #     sockets_list.remove(s)
                #     del clients[s]
                #
                #     continue
                user = clients[s]
                print(f'Received message from {user.decode()}: {message}')

                for client in clients:
                    # But don't sent it to sender
                    if client != s:
                        # Send user and message (both with their headers)
                        # client.send(user['header'] + user['data'] + message['header'] + message['data'])
                        client.send(f"{user.decode()}:{message}".encode())
                        print(f'Sending {message} to {clients[client].decode()}')

This is client.py:

import socket
import select
import errno
import sys

IP = "localhost"
PORT = 5555
current_user = input("Username: ")

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

client.connect((IP, PORT))

# client.setblocking(False)

username = current_user.encode()
client.send(username)

while True:
    message = input(f'{current_user} > ')
    if message:
        # Encode message to bytes, prepare header and convert to bytes, like for username above, then send
        client.send(f'{current_user}:{message}'.encode())

    try:
        while True:

            arr = client.recv(4096).decode().split(':')
            username = arr[0]
            if not username:
                print('Connection closed by the server')
                sys.exit()

            message = arr[1]
            # Print message
            print(f'{username} > {message}')
            message = input(f'{current_user} > ')

    except IOError as e:
        print(e)
        sys.exit()

    except Exception as e:
        print(e)

I expect the server to receive/send messages from both clients. Meaning whatever client 1 sent, client 2 will see.


Solution

  • The issue was that I wasn't sending/receiving the data properly with the client. So I tweaked it a little bit and it works now!

    while True:
        message = input(f'{current_user} > ')
        if message:
            client.send(f'{current_user}:{message}'.encode())
    
        arr = client.recv(4096).decode().split(':')
    
        if arr:
            username = arr[0]
            message = arr[1]
            # Print message
            print(f'{username} > {message}')
    

    Putting a forever loop in a forever loop didn't make sense so I got rid of it.

    I checked if the client received data with if arr: and printed the data. Since arr was in the forever loop, it ALWAYS checked for data.

    To send data, it was pretty simple, get input, send it via socket.