Home > Back-end >  ONLY one client receive message in multithreaded server for a multiplayer game
ONLY one client receive message in multithreaded server for a multiplayer game

Time:11-01

My target is print the message from function result on the client's screen. But only ONE client can received the message I think the problem is about the socket.recv, but I have no idea how to handle it.


The client.py is here

    #!/usr/bin/python3


#   *********************************************************   #
#
#   Function: Authentication
#   get user input
#   pass to server to check that the input is valid or not.
#
#   *********************************************************   #
def Authentication(clientSocket):
    user_name = input("Please input your user name: ")
    password = input("Please input your password: ")
    username_with_password = str("/login "   user_name   " "   password)
    clientSocket.send(username_with_password.encode())
    back_msg = clientSocket.recv(1024).decode()
    print(back_msg)
    if(back_msg == "1001 Authentication successful"):
        clientSocket.send("Yes".encode())
        return True
    else:
        clientSocket.send("NO".encode())
        return False

#   *********************************************************   #
#
#   Function: List_request
#   call when user input /list
#   print the room list
#
#   *********************************************************   #
def List_request(clientSocket):
    clientSocket.send("list".encode())
    back_msg = pickle.loads(clientSocket.recv(1024))
    for i in back_msg:
        print(i, end=' ')
    print("")

#   *********************************************************   #
#
#   Function: exit_request
#   call when user input /exit
#   print the exit message
#
#   *********************************************************   #
def exit_request(clientSocket):
    clientSocket.send("exit".encode())
    back_msg = clientSocket.recv(1024).decode()
    print(back_msg)

#   *********************************************************   #
#
#   Function: enter_request
#   call when user input /enter
#   first split /enter and x, where x is the room number
#
#   *********************************************************   #
def enter_request(clientSocket, num):
    willsend = "enter " str(num)
    clientSocket.send(willsend.encode())
    back_msg_1 = clientSocket.recv(1024).decode()

    print(back_msg_1)
    if(back_msg_1 == '3011 Wait'):
        back_msg_2 = clientSocket.recv(1024).decode()
        print(back_msg_2)
        return 'wait2'
    elif(back_msg_1 == "3012 Game started. Please guess true or false"):
        return 'wait'
    else:
        return '2'

#   *********************************************************   #
#
#   Function: OtherMsg
#   call when user invalid input
#
#   *********************************************************   #
def OtherMsg(clientSocket):
    msg = "other"
    clientSocket.send(msg.encode())
    msg_back = clientSocket.recv(1024).decode()
    print(msg_back)

#   *********************************************************   #
#
#   Function: GrabCommand_Game_Hall
#   Call when user go into game hall
#   handle different command by different function
#
#   *********************************************************   #
def GrabCommand_Game_Hall(command, num, clientSocket):
    if(command == "/list"):
        List_request(clientSocket)

    elif(command == "/exit"):
        exit_request(clientSocket)
        return '1'

    elif(command == "/enter"):
        c = enter_request(clientSocket, num)
        return str(c)

    else:
        OtherMsg(clientSocket)
        return '2'

#   *********************************************************   #
#
#   Function: PlayGame
#   call when user in the game
#   split the /guess true_false command to true / false
#   determine the input is valid or invalid
#
#   *********************************************************   #
def PlayGame(clientSocket, msg):
    invalid = "NO!"
    if ("/guess " in msg):
        msg1 = msg.split(" ")[1]
        if msg1 == "true" or msg1 == "false":
            clientSocket.send(msg1.encode())
            print(clientSocket.recv(2048).decode())
            return '1'

        else:
            clientSocket.send(invalid.encode())
            print(clientSocket.recv(1024).decode())
            return '2'
    elif msg == "":
        return '2'
    else:
        clientSocket.send(invalid.encode())
        print(clientSocket.recv(1024).decode())
        return '2'


def main(argv):
    serverName = str(sys.argv[1])
    serverPort = int(sys.argv[2])
    try:
        clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        clientSocket.connect((serverName, serverPort))
    except socket.error as err:
        print("Socket error: ", err)
        sys.exit(1)

    # Authentication until successes
    while( Authentication(clientSocket) != True):
        {}


    Input = ""
    num = 0
    #   In the game hall, only leave when user input /exit.
    while(Input != '/exit' ):

        Input = input()
        if(" " in Input):   #   used to split /enter command
            Input, num = Input.split(" ")[0], Input.split(" ")[1]

        var = GrabCommand_Game_Hall(Input, num, clientSocket)
        if(var == '1'): #   terminae
            Input = '/exit'

        elif(var == '2'):
            {}

        elif (var == 'wait'):   #   the "3001" command printed, wait for other player
            msg = ""
            while (PlayGame(clientSocket, msg) != '1'):
                msg = input()

        elif (var == 'wait2'):  # the "3002 command printed, go to play game.
            msg = ""
            while (PlayGame(clientSocket, msg) != '1'):
                msg = input()
        else:
            {}

    print("Client ends")
    clientSocket.close()
    exit(1)

if __name__ == '__main__':
    main(sys.argv)

The server.py is there

#   *********************************************************   #
#
#   Function: ReadUserInfo
#   Read and split the username and password from UserInfo.txt
#   store the username and password in the list
#
#   *********************************************************   #
def ReadUserInfo(filepath,username, password):
    with open(str(filepath)) as f:
        lines = f.readlines()
        for line in lines:
            username.append((str(line).split(':')[0]))
            password.append(str(line).split(':')[1].rstrip('\n'))

#   *********************************************************   #
#
#   Function: authenticates
#   Check whether username, password is correct or not.
#
#   *********************************************************   #
def authenticates(username, password, username_list, password_list):
    if username in username_list:
        if password in password_list:
            if username_list.index(str(username)) != password_list.index(str(password)):
                return "1002 Authentication failed"
            else:
                return "1001 Authentication successful"
        else:
            return "1002 Authentication failed"
    else:
        return "1002 Authentication failed"

#   *********************************************************   #
#   
#   Initialize the variable
#
#   *********************************************************   #

NUM_OF_ROOM = 10
serverPort = int(sys.argv[1])
UserInfo_path = str(sys.argv[2])
username = []   #store all username
password = []   #store all password
addr_l = []     #store all address
name_list = []
current_rm_num = []

guess_box = []
guess = bool(random.randint(0, 1))

room = [NUM_OF_ROOM]   [0] * 10 #There 10 rooms
room[0] = NUM_OF_ROOM   #the first element of room list is 10 (num of room).
print(room)
ReadUserInfo(UserInfo_path, username, password)
state = [0] * len(username)

#   *********************************************************   #
#
#   Function: RecevFromClient
#   get client message (input)
#   use for auth. users
#   Check the input is valid or not
#
#   *********************************************************   #
def RecvFromClient(connectionSocket):
    message1 = connectionSocket.recv(2048).decode()
    name_list.append(str(message1).split(" ")[1])
    msg_return = authenticates(str(message1).split(" ")[1], str(message1).split(" ")[2], username, password)
    connectionSocket.send(msg_return.encode())
    message = connectionSocket.recv(2048).decode()
    if message == 'Yes':
        state[username.index(str(message1).split(" ")[1])] =1
    return str(message)

#   *********************************************************   #
#
#   GameHallMsg
#   Receive command in game hall
#
#   *********************************************************   #

def GameHallMsg(connectionSocket, addr, addr_l):
    message1 = connectionSocket.recv(1024).decode()

    if(message1 == 'list'):
        room_1 = ["3001"]   room
        data = pickle.dumps(room_1)
        connectionSocket.send(data)
        return 'ok'

    elif(message1 == 'exit'):
        rtn_msg = "4001 Bye bye"
        connectionSocket.send(rtn_msg.encode())
        state[addr_l.index(addr)-1] -= 1
        return 'bye'

    elif(message1.split(" ")[0] == 'enter'):
        num = int(message1.split(" ")[1])
        if(num > len(room)-1 or num == 0):
            msg = "Please input the /enter command again! "
            connectionSocket.send(msg.encode())
            return 'ok'


        elif (room[num] == 0):
            rtn_msg_1 = "3011 Wait"
            room[int(num)]  = 1
            state[addr_l.index(addr)] = int(num)
            current_rm_num.append(num)
            print("Player name:",name_list[addr_l.index(addr)],"joined room", num)

            connectionSocket.send(rtn_msg_1.encode())
            return 'wait'

        elif (room[int(num)] == 1):
            room[int(num)]  = 1
            state[addr_l.index(addr)]  = 1
            rtn_msg_2 = "3012 Game started. Please guess true or false"
            current_rm_num.append(num)
            print("Player name:", name_list[addr_l.index(addr)], "joined room", num)
            connectionSocket.send(rtn_msg_2.encode())
            return 'jump'


        elif (room[int(num)] >= 2):
            rtn_msg_3 = "3013 The room is full"
            connectionSocket.send(rtn_msg_3.encode())
            return 'ok'


    else:
        return 'NO'

#   *********************************************************   #
#
#   Function: result
#   check the result.. i.e. win / lose / tie
#
#   *********************************************************   #

def result(connectionSocket, guess_box, addr, addr_l):
    a = 0
    c = addr[1]
    print(addr)
    print(addr_l)
    if(guess_box[0] == guess_box[1]):
        msg = "Tie!!"
        connectionSocket.sendto(msg.encode(), addr)
        return '2'

    elif(guess_box[addr_l.index(c)] == guess):
        msg1 = "You are Winner!"
        a =1
        connectionSocket.sendto(msg1.encode(), addr)
        return '2'
    elif(guess_box[addr_l.index(c)] != guess):
        msg2 = "You lose the game."
        a =1
        connectionSocket.sendto(msg2.encode(), addr)
        return '2'

#   *********************************************************   #
#
#   Function: TF
#   convert the /guess true or /guess false --> true or false
#
#   *********************************************************   #

def TF(connectionSocket, var, guess_box):
    msg = connectionSocket.recv(1024).decode()
    print("recv:",msg)
    if(msg == 'true'):
        msg = 'True'
        var = str(var)
        msg = bool(msg == var)
        guess_box.append(msg)
        return 'ok'
    elif(msg == 'false'):
        msg = 'False'
        var = str(var)
        msg = bool(msg == var)
        guess_box.append(msg)
        return 'ok'
    else:
        print(msg)
        statement = "4002 Unrecognized message!!"
        connectionSocket.send(statement.encode())
        return 'again'

#   *********************************************************   #
#
#   Thread
#   for every client
#
#   *********************************************************   #

class ServerThread(threading.Thread):
    def __init__(self, client):
        threading.Thread.__init__(self)
        self.client = client

    def run(self):
        connectionSocket, addr = self.client
        addr_l.append(addr[1])
        while(RecvFromClient(connectionSocket) == "NO"):
            {}
        print(addr_l)
        a = ''
        while(a != 'ex'):
            var = GameHallMsg(connectionSocket, addr[1], addr_l)
            if var == 'bye':
                break
            elif (var == 'wait'):
                while(a != 'b' ):
                    if(len(current_rm_num) == 2):
                        rtn_msg_2 = "3012 Game started. Please guess true or false"
                        connectionSocket.send(rtn_msg_2.encode())
                        print("guess is:", guess)
                        while (len(guess_box) != 2):
                            print(guess_box)
                            TF(connectionSocket, guess, guess_box)

                        a = 'b'

                        result(connectionSocket, guess_box, addr, addr_l)


            elif (var == 'NO'):
                rtn_msg_4 = "4002 Unrecognized message"
                connectionSocket.send(rtn_msg_4.encode())
            elif(var == 'ok'):
                {}
            elif(var == 'jump'):
                while (a != 'b'):
                    if (len(current_rm_num) == 2):
                        while (len(guess_box) != 2):
                            print(guess_box)
                            TF(connectionSocket, guess, guess_box)
                        result(connectionSocket, guess_box, addr, addr_l)

                        a = 'b'

class ServerMain:
    def server_run(self):
        serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            serverSocket.bind(("localhost", serverPort))
        except socket.error as err:
            print("Binding error: ", err)
            sys.exit(1)
        serverSocket.listen(len(username))


        while True:
            client = serverSocket.accept()
            t = ServerThread(client)
            t.start()


if __name__ == '__main__':
    server = ServerMain()
    server.server_run()

The content of UserInfo.txt is username and password with format Username:password


The expected input - output are:

For Server's terminal

 % python3 server.py 12345 [path_of_userinfo.txt]

For Client_1's terminal (input starts with "*")

% python3 client.py localhost 12345
Please input your user name:*username
Please input your password:* password
1001 Authentication successful
*/enter room_num
3011 Wait
3012 Game started. Please guess true or false
*/guess true
You are the loser! # If the random variable is false, not equal to true

For Client_2's terminal (input starts with "*")

   % python3 client.py localhost 12345
    Please input your user name: *username_2
    Please input your password: *password_2
    1001 Authentication successful
    */enter room_num
    3012 Game started. Please guess true or false 
    */guess false
    You are winner! # If the random variable is false

CodePudding user response:

Regarding only the one problem you address:

print the message from function result on the client's screen. But only ONE client can received the message

The problem comes from the use of a different thread for each client. The thread which receives a guess as first stays in its

                        while (len(guess_box) != 2):
                            print(guess_box)
                            TF(connectionSocket, guess, guess_box)

loop and waits for another message, which doesn't come. The thread which receives a guess as second sends the result to its own client only.

I don't think there's a sensible way to fix this while keeping this dthreaded approach.

Can I change the structure of my code by using those functions I implemented?

Here's a substitute for the while True loop in server_run that doesn't require changes in those functions other than server_run.

        from select import select
        connections = []
        room_connection = {}
        for reads in iter(lambda: select([serverSocket] connections, [], [])[0], []):
            for ready in reads: # for each socket which select reports is readable
                if ready == serverSocket:   # it's the server socket, accept new client
                    connectionSocket, addr = serverSocket.accept()
                    connections.append(connectionSocket)# store the connection socket
                    while RecvFromClient(connectionSocket) == "NO": pass
                else:           # message (or disconnect) from a client
                    try: var = GameHallMsg(ready, ready, connections)
                    except socket.error: var = 'bye'
                    if var == 'bye':    # client finished, remove from list
                        connections.remove(ready)
                        ready.close()
                    elif var == 'wait': # store first player's connection
                        room_connection[current_rm_num.pop()] = ready
                    elif var == 'NO':
                        rtn_msg_4 = "4002 Unrecognized message"
                        ready.send(rtn_msg_4.encode())
                    elif var == 'jump':
                        readyroom = current_rm_num.pop()
                        # find and notify other player in the room
                        other = room_connection.pop(readyroom)
                        rtn_msg_2 = "3012 Game started. Please guess true or false"
                        other.send(rtn_msg_2.encode())
                        print("guess is:", guess)
                        # query and inform both players
                        guess_box = []
                        while TF(ready, True, guess_box) != 'ok': pass
                        while TF(other, True, guess_box) != 'ok': pass
                        result(ready, guess_box, ('', 0), [0])
                        result(other, guess_box, ('', 1), [0, 1])
                        room[readyroom] = 0
  • Related