【问题标题】:Python TCP Server sending message to all clientsPython TCP Server 向所有客户端发送消息
【发布时间】:2016-10-12 09:00:12
【问题描述】:

我一直试图在我的局域网上创建一个 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?

【问题讨论】:

  • " 是否可以从一台服务器向多个客户端发送消息(通过 TCP)? - 是的。您面临的问题是什么?
  • @Prabhu sigh 然后呢?

标签: python sockets tcp


【解决方案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 标识)。

【讨论】:

  • 我知道我的代码很奇怪,当涉及到这类事情时,我不是很“有条理”。感谢您的帮助,现在就尝试一下!
  • 什么是 CPU 泄漏?
  • @Sam 和内存泄漏一样:你丢失了一个资源。在 cpu 的情况下,这意味着它做了无意义的工作,因此你失去了可能正在做一些有意义的事情的周期。
  • @freakish,我想我从来没有听说过它是“cpu 泄漏”,我称之为“低效算法”,泄漏意味着你最终会用完。你不会“用完”cpu 周期,你只是比你可能的要慢。
猜你喜欢
  • 2019-04-29
  • 2018-05-19
  • 2013-05-20
  • 1970-01-01
  • 2016-05-25
  • 1970-01-01
  • 1970-01-01
  • 2015-11-05
相关资源
最近更新 更多