Python TCP Server向所有客户端发送消息

时间:2016-10-12 09:00:12

标签: python sockets tcp

我一直在尝试通过我的局域网创建一个TCP python服务器,但我一直遇到这个项目的问题。我的问题是:是否可以从1台服务器向多个客户端发送消息(通过TCP)? (即客户端1发送消息" Hello world"它在所有其他客户端[clients-2,clients-3]上显示消息)。到目前为止,我的服务器代码是:

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)

继承我的客户代码:

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)

提前感谢任何答案!

修改 我希望输出看起来像这样:

User3's IP: Message they sent
User1's IP: Message they sent
Message: What do you want to send?

1 个答案:

答案 0 :(得分:1)

你的代码......非常奇怪。首先,您在accept上创建一个新线程,但是您将侦听器发送到该线程而不是客户端。因此,您的线程永远不会死亡,并且您有内存和CPU泄漏。更糟糕的是:在您的代码中,线程数等于所有客户端的数量已连接到服务器。这很糟糕。

在服务器端试试这个:

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()

代码更短,更简单,没有内存/ CPU泄漏。

现在将数据发送到所有客户端。你必须跟踪它们。你可以通过保持客户的全球命令来实现这一目标:

CLIENTS = {}

现在在听众中你做了:

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()

并在客户端:

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()]

该代码存在一个小问题(实际上有几个问题,例如错误处理)。如果某些客户在我们循环遍历CLIENTS字典时取消注册,会发生什么? Python将抛出异常。简单的解决方案是在插入,删除和迭代时锁定字典。

如果其他套接字重复使用之前的fileno(),则会出现竞争条件。在这种情况下,您可能希望手动为套接字生成ID(最好通过使用自定义类包装socket对象)。

请注意,可以使用set代替dict。但是,您最终需要一个dict,因为在某些时候您可能希望将msg发送到特定的客户端(由某个ID标识)。