2

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

halfer
  • 19,824
  • 17
  • 99
  • 186
Hass786123
  • 666
  • 2
  • 7
  • 16
  • @han solo How would i implement the `select` module in my code? – Hass786123 Feb 17 '19 at 18:15
  • Check this. This has everything you need. And loose the threading. https://pymotw.com/2/select/ :) – han solo Feb 17 '19 at 18:22
  • @hansolo I have read through it but I'm still not too sure on how to implement it into my code so could you show me how to please – Hass786123 Feb 17 '19 at 18:35
  • @hansolo He's trying to use a thread for each client. With that design, blocking a client's thread waiting for data from that client is fine. Similarly, in his client, he has one thread whose purpose is to block waiting for data from the server. Again, fine to block that thread since its only purpose is to wait for data on that connection. – David Schwartz Feb 17 '19 at 18:52
  • I don't know python well enough to help you. But I don't think there's anything majorly wrong with your program. You need to change the input from line mode to a more raw mode and you need to convert your received data from raw bytes to a printable string. Other than that, it seems okay to me. Your server will block if any client refuses to read data from it, but I don't think you care much about resistance to attacks at this point. – David Schwartz Feb 17 '19 at 19:33
  • @DavidSchwartz Yeah, you are right – han solo Feb 18 '19 at 09:29
  • @system123456 Are you doing this for learning purpose ? Otherwise, i suggest you take a look at the `twisted` module :) – han solo Feb 18 '19 at 09:34
  • @hansolo yes i'm doing this for learning purpose so that why I don't want to use the twisted module – Hass786123 Feb 18 '19 at 16:18

1 Answers1

1

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)
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252