【问题标题】:simple server with multiple clients具有多个客户端的简单服务器
【发布时间】:2011-11-02 00:25:56
【问题描述】:

我正在尝试实现具有多个客户端的简单服务器。它应该从必要的套接字接收数据,处理然后将数据发送给其他客户端。我使用 Python 标准库中的 select 模块。 这是服务器:

class ProcessingServer:
    def __init__(self, bindaddress="localhost", portname=50001, maxqueue=5):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind((bindaddress, portname))
        self.socket.listen(maxqueue)
        self.inputsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.data = ""

    def connect_to_output(self, address, port):
        self.inputsocket.connect((address, port))

    def start(self):
        rsocks = []
        wsocks = []
        rsocks.append(self.socket)
        wsocks.append(self.inputsocket)
        self.socket.accept()

        while True:
            try:
                reads, writes, errs = select.select(rsocks, wsocks, [])
            except:
                return
            for sock in reads:
                if sock == self.socket:
                    client, address  = sock.accept()
                    rsocks.append(client)
                else:
                    self.socket.send(self.data)
                    rsocks.remove(sock) 
            for sock in writes:
               if sock == self.inputsocket:
                    self.data = sock.recv(512)
                    wsocks.remove(sock)
                    print repr(self.data)

这是一个简单的客户端:

import socket
mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysocket.connect(("localhost", 50001))
while True:
    data = mysocket.recv(512)
    print repr(data)
mysocket.close()

接收部分服务器工作正常,但服务器不产生任何输出。 我对网络编程一点经验都没有,感觉好像漏掉了什么。

【问题讨论】:

  • 谢谢,但我想让事情尽可能简单))
  • Twisted 比使用套接字要简单得多。您甚至不必担心线程。 Twisted 为您所有爵士乐。

标签: python sockets networking


【解决方案1】:

您的脚本中有几处看起来很奇怪。

select 模块的标准用法如下:你有一个套接字来监听连接,每个与客户端的连接都有一个套接字。

一开始,只有这个套接字被添加到你的潜在读者列表中,而你的潜在作者列表是空的。

调用 select.select(potential_readers, potential_writers, potential_errors) 将返回 3 个列表: - 准备阅读的插座 - 准备写入的套接字 - 错误的套接字

在准备读取的套接字列表中,如果套接字是监听连接的那个,它必须接受它并将新的套接字置于潜在的读取、潜在的写入和潜在的错误中。

如果套接字是另一个套接字,则有数据要从该套接字读取。你应该调用 sock.recv(length)

如果要发送数据,应从 select.select 返回的 wlist 中发送。

errlist 不经常使用。


现在,对于您的问题的解决方案,您描述协议的方式(如果我理解良好的话),它可能看起来像这样:

import socket, select

sock_producer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_producer.bind(('localhost', 5000))
sock_producer.listen(5)
producers = []    

clients = []
sock_consumer_listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Note: different port to differentiate the clients who receive data from the one who sends messages
sock_consumer_listener.bind(('localhost', 5001))

rlist = [sock_producer, sock_listener]
wlist = []
errlist = []

out_buffer = []

while True:
    r, w, err = select.select(rlist, wlist, errlist)
    for sock in r:
        if sock == sock_producer:
            prod, addr = sock.accept()
            producers.append(prod)
            rlist.append(prod)
         elif sock == sock_consumer_listener:
            cons, addr = sock.accept()
            clients.append(cons)
            wlist.append(cons)
         else:
            out_buffer.append(sock.recv(1024))

     out_string = ''.join(out_buffer)
     out_buffer = []

     for sock in w:
         if sock in clients:
             sock.send(out_string)

我还没有测试过这段代码,所以可能会有一些错误,但这与我的做法很接近。

【讨论】:

  • +1 for "不同的端口来区分接收数据的客户端和发送消息的客户端" 是的。有一天,我过了几个小时才明白这一点。它在文档和教程中没有很好地强调
  • @Martin:我不明白。我有一个套接字 - 数据源。源套接字是客户端套接字,我一启动服务器就尝试连接到它。 (在我的示例中为输入套接字)。另一个套接字正在侦听传入连接的套接字。但是您的示例中有两个“服务器”套接字 - sock_producer、sock_listener。你能再解释一下吗?
  • @qutron:数据源是一个socket。就我而言,源套接字是生产者。我听而不是在某个地方连接,但它应该很容易适应(连接而不是听)并删除块if sock == sock_producer: ...
  • @Martin:嗯,现在一切都很好。像魅力一样工作。谢谢!
【解决方案2】:

是的...改用zeromq

server.py

import zmq
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://127.0.0.1:50001")

while True:
    msg = socket.recv()
    print "Got", msg
    socket.send(msg)

client.py

import zmq
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect("tcp://127.0.0.1:50001")

for i in range(100):
    msg = "msg %s" % i
    socket.send(msg)
    print "Sending", msg
    msg_in = socket.recv()

【讨论】:

  • 将 zeromq 放到公共互联网上是否安全?截至今年早些时候,情况并非如此,因为恶意方可能会通过发送格式错误的数据来轻微地导致您的听众崩溃。
  • 不确定您指的是什么?关联?我想它会比尝试手动做低级套接字的东西更稳定。如果你想要更健壮的东西,扭曲可能是要走的路。
  • 例如,lists.zeromq.org/pipermail/zeromq-dev/2010-September/…>。那里提到的两个问题似乎已经解决(遗憾的是链接已损坏,所以这并不完全明显)。我只用了一两分钟的谷歌搜索就找到了那个帖子。进一步搜索可能会发现其他类似问题。
  • 很难推断 zeromq 在稳定性方面的地位。在我的使用中,我没有遇到任何问题,但我没有用它构建面向互联网的服务。在 IRC 频道或邮件列表中提出您的特定用例可能会很好。它没有内置的安全性,因此它本身并不适合面向互联网的服务。如果您正在构建它,那么使用 twisted 会是一个更好的主意。
  • 这个 zmq 示例是否适用于基本上在一个且仅一个客户端 ip+port 上运行的客户端脚本。这意味着如果您有一个带有 10 个节点 (PC) 通过一个 IP 地址连接的卫星办公室,每个节点都需要它自己的端口?在同一台机器上的不同窗口中运行这个脚本不会误导吗?服务器如何知道也发送数据的控制台窗口?
猜你喜欢
  • 2013-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
相关资源
最近更新 更多