1

I have to write a small python program which implement client-server connection (TCP/IP) and play the Twenty Questions game. The server generates a random number between 1 and 100 and then multiple clients can make connection with it. The clients make its guess one by one and the server sent a feedback to them.

For example a client sends a struct ('<', 50) so its tipp is the number smaller than 50. The server receives the struct, checks if it's true or not and sends back a character ('I' - yes, 'N' - no, 'Y' - win, 'V' - lose, 'K' - lose in case of there's at least another client still playing the game). The clients make it's tipps based on binary search. I didn't handle yet those cases when the correct number is the range that client tipps, but now not this is why I'm asking for help.

How can I make the server handle multiple clients? At now, if a client connects to the server, it plays the entire game and not waiting for other clients to connect.

server.py:

from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
import select
import struct
import random

server_addr = ('', 10000)
unpacker = struct.Struct('c I')

correct_num = random.randint(1, 100)
answer = ''

print('Correct number:', correct_num)

with socket(AF_INET, SOCK_STREAM) as server:
    server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    server.bind(server_addr)
    server.listen(5)

    sockets = [server]

    while True:
        readables, writables, exceptionals = select.select(sockets, sockets, sockets, 1)

        if not (readables or writables or exceptionals):
            continue

        for s in readables:
            if s is server: # new client connect
                client_conn, client_addr = s.accept()
                client_conn.setblocking(0)

                print('Connected:', client_addr)
                
                sockets.append(client_conn)
            else:           # handle client
                data = s.recv(unpacker.size)
                if not data:
                    sockets.remove(s)
                    s.close()
                    print('Exited')
                else:
                    print('Received:', data)
                    unp_data = unpacker.unpack(data)
                    print('Unpack:', unp_data)
                    if answer == 'Y':
                        answer = 'V'
                    else:
                        if unp_data[0].decode() == '=':
                            if int(unp_data[1]) == correct_num:
                                answer = 'Y'
                            else:
                                answer = 'K'
                        elif unp_data[0].decode() == '>':
                            if correct_num > int(unp_data[1]):
                                answer = 'I'
                            else:
                                answer = 'N'
                        elif unp_data[0].decode() == '<':
                            if correct_num < int(unp_data[1]):
                                answer = 'I'
                            else:
                                answer = 'N'
                    print('Evaluated and sent back', answer)
                    s.sendall(str(answer).encode())

client.py:

from socket import socket, AF_INET, SOCK_STREAM
import struct
import random

server_addr = ('localhost', 10000)
packer = struct.Struct('c I')

operators = ['<', '>', '=']
last_num = last_n_num = last_i_num = 0
last_op = last_n_op = last_i_op = ''

with socket(AF_INET, SOCK_STREAM) as client:
    client.connect(server_addr)

    lowest_num = 1
    highest_num = 100
    mid = lowest_num + (highest_num - lowest_num) // 2
    sent_op = operators[mid % (len(operators) - 1)]
    range_nums = [mid]

    packed_data = packer.pack(sent_op.encode(), int(mid))
    print('Client make starting guess')
    client.sendall(packed_data)

    while True:
        data = client.recv(1).decode()
        if data == 'V':
            print('Someone else won\nClient exits')
            client.close()
            exit(0)
        elif data == 'Y':
            print('Client won')
            client.close()
            exit(0)
        elif data == 'K':
            print('Client lose')
            client.close()
            exit(0)
        else:
            if data == 'I':
                if sent_op == '<':
                    highest_num = mid - 1
                elif sent_op == '>':
                    lowest_num = mid + 1
            if data == 'N':
                if sent_op == '<':
                    lowest_num = mid + 1
                elif sent_op == '>':
                    highest_num = mid - 1
            
            mid = lowest_num + (highest_num - lowest_num) // 2

            if mid in range_nums:
                sent_op = '='
            else:
                range_nums.append(mid)
            
            if lowest_num >= highest_num:
                sent_op = '='
            else:
                sent_op = operators[mid % (len(operators) - 1)]
            
            print('Client make another guess')
            packed_data = packer.pack(sent_op.encode(), int(mid))
            client.sendall(packed_data)
Zoltán Orosz
  • 303
  • 1
  • 8
  • You can create new thread for each connected client and then handle that connection in this thread. Please see https://docs.python.org/3/library/threading.html. – maciek97x Oct 14 '22 at 20:24
  • There are many examples right here on SO. Just scroll down the questions that have [python] and [sockets] as tags. – President James K. Polk Oct 14 '22 at 21:11

0 Answers0