Search code examples
pythonpython-3.xsocketstcp

Send/Receive Python chat room not working (updated)


I asked a question about my server to client code because I had many problems with and someone told me that the solution to the problems I had was to make a peer to peer chat which I have now done.

Server.py

import socket, threading

host = "127.0.0.1"
port = 4000
s = socket.socket()
s.bind((host,port))
s.listen(5)
client_sockets = []
users = []
print("Listening")

def handle_client(conn):
    while True:
        try:
            data = conn.recv(512)
            for x in client_sockets:
                try:
                    x.send(data)
                except Exception as e:
                    print(e)
        except:
            pass

while True:
    conn,addr = s.accept()
    client_sockets.append(conn)
    print("Connections from", addr[0], "on port",addr[1])
    threading.Thread(target = handle_client,args = (conn,)).start()

Client.py

import socket,threading

host = "127.0.0.1"
port = 4000
s = socket.socket()
s.connect((host,port))

def echo_data(sock):
   while True:
      try:
         data = sock.recv(512)
         print(data)
      except:
         pass

while True:
   threading.Thread(target=echo_data,args=(s,)).start()
   msg = input("Enter your message : ")
   s.send(msg.encode())

The problems is that when I run the client and try talking to another client the message doesn't get sent unless the other client hits enter and also that brings me to my second problem, when the clients send messages to each other they get received in this format:

b'hi'Enter your message :

This is the link to my previous question


Solution

  • I will start with general problems not directly related to the question:

    • except: pass is generally a bad idea, specially when things go wrong because it will hide potentially useful messages. It is allowed by the language but should never exist in real code
    • in client.py you start a receiving thread per message, while you only need one for the whole client. You should start the thread outside the loop:

      threading.Thread(target=echo_data,args=(s,)).start()
      while True:
         msg = input("Enter your message : ")
         s.send(msg.encode())
      

    Now for the questions:

    • the message doesn't get sent unless the other client hits enter

      It can be caused by an IDE. Specifically, IDLE is known to behave poorly with multi-threaded scripts. If you correctly use one single receiving thread and starts the script from the command line (python client.py) it should work correctly

    • the messages get recived in this format: b'hi'Enter your message

      sock.recv(sz) returns a byte string. You need to decode it to convert it to a Python 3 unicode string:

       data = sock.recv(512)
       print(data.decode())
      

    But that is not all. It is fine for tests, but you should at least allow clients to disconnect from the server and when they do, remove them from client_sockets. And it is common not to send back a message to the sender. So you could improve the server.py loop:

    while True:
        try:
            data = conn.recv(512)
            for x in client_sockets:
                if x != conn:         # do not echo to sender
                    x.send(data)
        except Exception as e:        # problem in connection: exit the loop
            print(e)
            break
    # clear the connection
    conn.close()
    client_sockets.remove(conn)