【问题标题】:Python, Listening on two sockets at the same timePython,同时监听两个套接字
【发布时间】:2015-07-15 22:32:01
【问题描述】:

我正在创建一个 python 服务器,它应该处理来自端口 9090 上的远程主机的连接以及仅来自端口 9091 上的本地主机(发送数据的 GUI)的连接。 我认为问题出在 socket.listen(backlog) 中,因为这会阻止一切。 那么有没有办法让socket1.listen(backlog)和socket2.listen(backlog)同时运行呢? 另外,制作一个用于从 GUI(用 Java 编写)接收数据的套接字是不是一个好主意,最重要的是安全?

【问题讨论】:

  • 为什么不使用线程?第一个套接字不会阻塞另一个套接字。
  • 您的问题有些含糊,但是有可能,您需要另一个线程,最好是每个套接字都有一个线程。尝试将套接字功能包装在一个新类中,从 Thread 继承该类,然后为每个端口创建一个新类实例
  • 您是在使用现有的 Python Web 服务器还是自己编写?我不会使用套接字来亲自接收 gui 数据。我认为您正在寻找interprocess communications,例如 D-Bus、XML-RPC 等。
  • 是的,在一个套接字上从 python 发送数据并在 Java 中接收它是安全的。它也被认为是进程间通信的一个不错的选择,这就是你在这里解释的原因
  • 您可以通过在侦听套接字上设置socket.setblocking(0) 来使listen / accept 不阻塞。然后您可以使用select 等待事件。有一些细节需要处理,但应该相当容易。当然,您也可以使用为您完成此类工作的库。一种选择是扭曲的:twistedmatrix.com/trac。请参阅他们的回显服务器示例 (twistedmatrix.com/trac/#echoserver) 以开始使用。

标签: python multithreading sockets


【解决方案1】:

这可以在 C 中完成:Listen to multiple ports from one server

和 C++:Create multiple listening sockets

所以你应该能够在 Python 中做到这一点。

唯一会使这不安全的事情是,如果您的服务器执行危险活动以响应 GUI,但您可能可以添加代码来检查和防止黑客入侵。

【讨论】:

  • 有没有可能有人在127.0.0.1中欺骗了他的ip地址,冒充GUI向Python Server发送数据?
  • 不,这是一个特殊的loopback 地址,只能在同一台计算机上使用。
【解决方案2】:

我正在使用python2.7.3,正如我在评论中所述,我无法重现listen 阻塞问题。 备注:

  • 为了简单起见,我只使用IPv4 套接字
  • 我在代码中包含了Python 的提示符,以表明它没有阻塞
  • 关于最终的安全问题:监听 9091 的套接字 (s_local) 只接受来自 localhost 的连接(来自其他主机的连接会失败)

这是来自 CentOS5 机器的 sn-p:

>>> import socket
>>> s_global = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s_global.bind(("", 9090))
>>> backlog = 0xFF
>>> s_global.listen(backlog)
>>> s_local = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s_local.bind(("127.0.0.1", 9091))
>>> s_local.listen(backlog)
>>>

这是同一台机器上的一些输出:

netstat -an | grep 909
tcp        0      0 0.0.0.0:9090                0.0.0.0:*                   LISTEN      
tcp        0      0 127.0.0.1:9091              0.0.0.0:*                   LISTEN      

正如所见,两个 server 套接字都在监听连接。

现在,如果我的环境出现问题(一些阅读显示默认情况下套接字 默认阻塞),在创建每个套接字之后,您可以添加(如在一条评论中指定)以下行:

  • 对于 s_global

    s_global.setblocking(0)
    
  • 对于 s_local

    s_local.setblocking(0)
    

现在为了接收来自两个套接字的传入连接,您应该使用 Pythonselect module(用于异步 IO)不需要其他线程;您的服务器应用程序应该不断地监听连接,例如:

TIMEOUT = 1 #seconds
terminate = False # variable to be modified externally to end the loop
listening_sockets = [s_global, s_local]
while (not terminate):
    notified_socket_list = select.select(listening_sockets, [], [], TIMEOUT)[0]
    for notified_socket in notified_socket_list:
        incoming_socket, addr = notified_socket.accept()
        handle(incoming_socket, addr)

注意:值得一提:epoll(select.epoll) 在性能方面优于 select(select.select),但我还没有尝试过。

现在,关于连接处理功能(handle):如果您的服务器仅用于教育目的,您可以保持原样,但如果您的服务器设计为同时接受大量每个客户端之间的连接和大量数据交换,您应该在单独的线程中处理每个连接(这是一个一般建议,不适用于Python,因为它的GIL,有时线程只会减慢速度)/process 就像在 SocketServer.py - part of Python's standard library 中所做的那样。

@Edit1 - 回复第一条评论:

很简单,你只需要稍微修改一下innerfor循环:

    for notified_socket in notified_socket_list:
        incoming_socket, addr = notified_socket.accept()
        if notified_socket == s_global:
            handle_global(incoming_socket, addr)
        else:
            handle_local(incoming_socket, addr)

或者您可以使用我原来的解决方案并区分 handle 中的连接:addr 参数包含套接字连接到的(本地)端口;您可以对其进行测试,并根据其值(9090 或 9091)以不同方式处理连接。

【讨论】:

    猜你喜欢
    • 2013-03-29
    • 2016-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-25
    • 2011-02-15
    • 2017-06-09
    • 1970-01-01
    相关资源
    最近更新 更多