Python TCP Server sending message to all clients
<p>I have been trying to make a TCP python server over my LAN, but I've constantly run into issues with this project. My question is: Is it possible to send a message (over TCP) to multiple clients from 1 server? (I.e. client-1 sends a message "Hello world" and it shows the message on all other clients [clients-2, clients-3]). Heres my code for the server so far:</p>

<pre><code>import socket, time, sys
import threading

TCP_IP = input("Host IP: ")
TCP_PORT = int(input("Host Port: "))
BUFFER_SIZE = 1024

def createNewThread(function):
    threading.Thread(target=function).start()

def Listening():
    try:
        while True:
            s.listen(1)

            conn,addr = s.accept()
            threading.Thread(target=Listening).start()
            print("User joined with IP %s" % (addr[0]))
            while 1:
                data = conn.recv(BUFFER_SIZE)
                if not data: break
                conn.send(addr[0].encode("utf-8") + b': ' + data)
            conn.close()
    except ConnectionResetError as e:
        print("Connection was closed: ", e)

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((TCP_IP,TCP_PORT))
    print("-----Server started-----")
    Listening()
except socket.error as e:
    print("Socket error occured. More info: ", e)
</code></pre>

<p>And heres my code for client:</p>

<pre><code>import socket, sys, time

TCP_IP = input("Connect to Local IP: ")
TCP_PORT = int(input("Connect to Local Port: "))
BUFFER_SIZE = 1024
running = True

while running == True:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print("Connecting...")
        s.connect((TCP_IP,TCP_PORT))
        print("Connected!")
        while True:
            MESSAGE = input("Message: ")
            if MESSAGE == "exit":
                s.close()
                raise SystemExit
            s.send(MESSAGE.encode('ascii'))
            data = s.recv(BUFFER_SIZE)
            print(data.decode("utf-8"))

        running = False
        time.sleep(20)
    except:
        print(sys.exc_info()[0])
        time.sleep(1)
</code></pre>

<p>Thanks in advance to any answers!</p>

<p><strong>Edit:</strong>
I want the ouput to look something like this:</p>

<pre><code>User3's IP: Message they sent
User1's IP: Message they sent
Message: What do you want to send?
</code></pre>

<p>Your code is... very weird. First thing is that you create a new thread on <code>accept</code> but you send listener to that thread instead of the client. Thus your threads never die and you have a memory and cpu leak. It's even worse: in your code the number of threads is equal to the number of <strong>all</strong> clients that have <strong>ever</strong> been connected to the server. This is bad.</p>

<p>Try this on the server side:</p>

<pre><code>def client(conn):
    while True:
        data = conn.recv(BUFFER_SIZE)
        if not data:
            break
        conn.send(data)  # simple ping

def listener():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((TCP_IP, TCP_PORT))
    s.listen(5)
    while True:
        conn, addr = s.accept()
        threading.Thread(target=client, args=(conn,)).start()

if __name__ == '__main__':
    listener()
</code></pre>

<p>The code is shorter, simpler and there are no memory/cpu leaks.</p>

<p>Now as for sending data to all clients. You have to keep track of them. You can achieve this by keeping a global dict of clients:</p>

<pre><code>CLIENTS = {}
</code></pre>

<p>Now in listener you do:</p>

<pre><code>def listener():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((TCP_IP,TCP_PORT))
    s.listen(5)
    while True:
        conn, addr = s.accept()
        # register client
        CLIENTS[conn.fileno()] = conn
        threading.Thread(target=client, args=(conn,)).start()
</code></pre>

<p>and in client:</p>

<pre><code>def client(conn):
    while True:
        data = conn.recv(BUFFER_SIZE)
        if not data:
            break
        # broadcast
        for client in CLIENTS.values():
            client.send(data)

    # the connection is closed: unregister
    del CLIENTS[conn.fileno()]
</code></pre>

<p>There's one little problem with that code (well actually there are several, e.g. error handling). What happens if some clients unregisters while we loop through <code>CLIENTS</code> dictionary? Python will throw an exception. Simple solution is to lock the dictionary on inserts, deletions and iterations.</p>

<p>Also there's a race condition if some other socket reuses previous <code>fileno()</code>. In that case you may want to generate ids for sockets manually (preferably by wrapping <code>socket</code> object with a custom class).</p>

<p>Note that it is possible to use <code>set</code> instead of <code>dict</code>. However you will eventually need a dict since at some point you would want to send msg to a specific client (identified by some id).</p>

python sockets tcp