我需要服务器向所有客户端发送消息 (Python,套接字)

11

这是我的服务器程序,它如何将从每个客户端接收到的数据发送给其他每个客户端?

import socket
import os
from threading import Thread
import thread

def listener(client, address):
    print "Accepted connection from: ", address

    while True:
        data = client.recv(1024)
        if not data:
            break
        else:
            print repr(data)
            client.send(data)

    client.close()

host = socket.gethostname()
port = 10016

s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host,port))
s.listen(3)
th = []

while True:
    print "Server is listening for connections..."
    client, address = s.accept()
    th.append(Thread(target=listener, args = (client,address)).start())

s.close()

请勿尝试将Python代码作为可运行的代码片段发布;这仅适用于JS / HTML / CSS。(这次我已经编辑过了。) - abarnert
1
请查看这个SO被接受的答案Python SocketServer:发送到多个客户端?,也许这正是您正在寻找的,特别是在一个小的实现示例中解决了@abarnert提出的所有建议点。 - Anzel
@abarnert,您能否详细解释一下为什么您反对使用asyncore?我认为它是向客户端发送多个消息的好选择,所以我想从您的角度了解更多信息。 - Anzel
@abarnert,谢谢你的指引,我会去找那篇文章并阅读更多相关文档。干杯 :) - Anzel
@Anzel:实际上,对我来说(但也许不是对Guido来说...),最好的论点是,如果你今天要从头编写一个新服务器,你真的应该使用3.4或更高版本。如果你有成千上万行现有的2.7代码,一定要使用2.7。如果你正在匆忙拼凑一个脏脏的脚本,并且机器上有2.7,请使用它。但对于全新而且重要的东西,为什么要自己给自己设置障碍呢? - abarnert
显示剩余4条评论
1个回答

13
如果你需要向所有客户发送消息,你需要以某种方式保存所有客户的信息。例如:
clients = set()
clients_lock = threading.Lock()

def listener(client, address):
    print "Accepted connection from: ", address
    with clients_lock:
        clients.add(client)
    try:    
        while True:
            data = client.recv(1024)
            if not data:
                break
            else:
                print repr(data)
                with clients_lock:
                    for c in clients:
                        c.sendall(data)
    finally:
        with clients_lock:
            clients.remove(client)
            client.close()

最好将其中的某些部分拆分成单独的函数,例如一个名为broadcast的函数,用于执行所有发送操作。

然而,这是最简单的方法,但存在问题:

  • 如果一个客户端连接速度较慢,其他人写入数据时可能会被拖累。而当他们在等待写入时,他们无法读取任何内容,因此可能会导致缓冲区溢出并断开所有人的连接。
  • 如果一个客户端发生错误,则正在向该客户端写入的线程可能会收到异常,这意味着您将断开错误的用户连接。

因此,更好的解决方案是为每个客户端提供一个队列,并配备一个专门服务于该队列的写入线程和读取线程。(您还可以以各种方式扩展此功能-例如对队列设置限制,使人们停止尝试与落后太远的人交流等)


作为Anzel指出的,除了使用每个客户端一个线程(或两个线程)的方式外,还有一种不同的服务器设计方式:使用一个reactor来多路复用所有客户端的事件。

Python 3.x内置了一些很好的库来实现这个功能,但是2.7只有笨重且过时的asyncore/asynchat和低级别的select

作为Anzel所说,Python SocketServer:向多个客户端发送有一个使用asyncore的答案,值得一读。但我实际上不会使用它。如果您想在Python 2.x中编写基于反应器的服务器,我要么使用更好的第三方框架,如Twisted,要么找到或编写一个非常简单的框架,直接在select上运行。

精准地解决OP将面临的问题。 - Anzel
谢谢您的详细回答! - samrap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接