从线程 ping 多个 ip 时的 Python ICMP ping 实现?

发布于 2024-12-27 17:32:07 字数 2130 浏览 0 评论 0原文

我一直在使用 jedie 的 python ping 实现< /a> 在 Windows 上。我可能是错的,但是当从不同的线程 ping 两台计算机(A 和 B)时,ping 将返回它收到的第一个 ping,无论来源如何。

由于这可能是 jedie 的 fork 的问题,我恢复到以前的版本。 (这是我下面要探讨的版本)

我在 receive_one_ping 中添加了一行代码:(第 134 行或类似行)

recPacket, addr = my_socket.recvfrom(1024) # Existing line
print "dest: {}, recv addr: {}.".format(dest_addr, addr) # New line

这使我们能够看到我们正在接收的 ping 的地址。 (应该与目标 IP 相同,对吧?)

测试:

ping1() ping 已知的离线 IP (1.2.3.4),
ping2() ping 一个已知的在线 IP(192.168.1.1 - 我的路由器)

>>> from ping import do_one

>>> def ping1():
    print "Offline:", do_one("1.2.3.4",1)

>>> ping1()
Offline: None

>>> def ping2():
    print "Online:", do_one("192.168.1.1",1)

>>> ping2()
Online: dest: 192.168.1.1, recv addr: ('192.168.1.1', 0).
0.000403682590942

现在,如果我们一起执行它们:(为了简单起见,使用计时器)

>>> from threading import Timer
>>> t1 = Timer(1, ping1)
>>> t2 = Timer(1, ping2)
>>> t1.start(); t2.start()
>>> Offline:Online: dest: 192.168.1.1, recv addr: ('192.168.1.1', 0).dest: 1.2.3.4, recv addr: ('192.168.1.1', 0).

0.0004508952953870.000423517514093

它有点损坏(由于打印不能很好地与线程一起工作),所以这里有点更清楚:

>>> Online: dest: 192.168.1.1, recv addr: ('192.168.1.1', 0).
Offline:dest: 1.2.3.4, recv addr: ('192.168.1.1', 0). # this is the issue - I assume dest should be the same as recv address?

0.000450895295387
0.000423517514093

我的问题:

  1. 任何人都可以重现这个吗?

  2. ping 应该像这样吗?我想不会。

  3. 现有的 Python ICMP ping 是否不会出现此行为?
    或者,您能想到一个简单的修复方法 - 即轮询 receive_one_ping 直到我们的目的地与我们的接收地址匹配?

编辑:我在 python-ping github 上创建了一个问题页面

I've been using jedie's python ping implementation on Windows. I could be wrong, but when pinging two computers (A and B) from separate threads, ping will return the first ping it receives, regardless of source.

Since it could be an issue with jedie's fork, I reverted to the previous version. (This is the version I'm going to explore below)

I added in a line of code in receive_one_ping: (Line 134 or similar)

recPacket, addr = my_socket.recvfrom(1024) # Existing line
print "dest: {}, recv addr: {}.".format(dest_addr, addr) # New line

This allows us to see the address of the ping we're receiving. (Should be same as the destination IP, right?)

Testing:

ping1() pings a known offline IP (1.2.3.4),
ping2() pings a known online IP (192.168.1.1 - my router)

>>> from ping import do_one

>>> def ping1():
    print "Offline:", do_one("1.2.3.4",1)

>>> ping1()
Offline: None

>>> def ping2():
    print "Online:", do_one("192.168.1.1",1)

>>> ping2()
Online: dest: 192.168.1.1, recv addr: ('192.168.1.1', 0).
0.000403682590942

Now if we do them together: (Using Timer for simplicity)

>>> from threading import Timer
>>> t1 = Timer(1, ping1)
>>> t2 = Timer(1, ping2)
>>> t1.start(); t2.start()
>>> Offline:Online: dest: 192.168.1.1, recv addr: ('192.168.1.1', 0).dest: 1.2.3.4, recv addr: ('192.168.1.1', 0).

0.0004508952953870.000423517514093

It's a little mangled (due to print not working nicely with threading), so here it is a bit clearer:

>>> Online: dest: 192.168.1.1, recv addr: ('192.168.1.1', 0).
Offline:dest: 1.2.3.4, recv addr: ('192.168.1.1', 0). # this is the issue - I assume dest should be the same as recv address?

0.000450895295387
0.000423517514093

My questions:

  1. Can anyone recreate this?

  2. Should ping be behaving like this? I assume not.

  3. Is there an existing ICMP ping for python that will not have this behaviour?
    Alternatively, can you think of an easy fix - ie polling receive_one_ping until our destination matches our receive address?

Edit: I've created an issue on the python-ping github page

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

清风不识月 2025-01-03 17:32:07

这是由于 ICMP 的性质而发生的。 ICMP 没有端口的概念,因此所有 ICMP 消息都会被所有 侦听器接收。

消除歧义的常用方法是在 ICMP ECHO REQUEST 有效负载中设置唯一标识符,并在响应中查找它。此代码似乎可以做到这一点,但它使用当前进程 ID 来组成 ID。由于这是多线程代码,因此它们将共享一个进程 ID,并且当前进程中的所有侦听器都会认为所有 ECHO REPLY 都是它们自己发送的!

您需要更改 do_one() 中的 ID 变量,使其在每个线程中都是唯一的。您需要在 do_one() 中更改这一行:

my_ID = os.getpid() & 0xFFFF

可能这将作为替代方案,但理想情况下您应该使用真正的 16 位哈希函数:

# add to module header
try:
    from thread import get_ident
except ImportError:
    try:
        from _thread import get_ident
    except ImportError:
        def get_ident():
            return 0

# now in do_one() body:
my_ID = (get_ident() ^ os.getpid()) & 0xFFFF

我不'我不知道这个模块是否有任何其他线程问题,但粗略检查似乎没问题。

使用 jedie 实现,您可以对 Ping() own_id 构造函数参数进行类似的更改。您可以传入一个您知道是唯一的 id(如上)并自己管理 Ping() 对象,也可以在构造函数中更改此行 (110):

self.own_id = os.getpid() & 0xFFFF

另请参阅 此问答和答案评论线程了解更多信息。

This is happening because of the nature of ICMP. ICMP has no concept of ports, so all ICMP messages are received by all listeners.

The usual way to disambiguate is to set a unique identifier in the ICMP ECHO REQUEST payload, and look for it in the response. This code appears to do that, but it uses the current process id to compose the ID. Since this is multithreaded code, they will share a process id and all listeners in the current process will think all ECHO REPLYs are ones they themselves sent!

You need to change the ID variable in do_one() so that it is per-thread unique. You will need to change this line in do_one():

my_ID = os.getpid() & 0xFFFF

Possibly this will work as an alternative, but ideally you should use a real 16-bit hashing function:

# add to module header
try:
    from thread import get_ident
except ImportError:
    try:
        from _thread import get_ident
    except ImportError:
        def get_ident():
            return 0

# now in do_one() body:
my_ID = (get_ident() ^ os.getpid()) & 0xFFFF

I don't know if this module has any other thread issues, but it seems to be ok from a cursory examination.

Using the jedie implementation, you would make a similar change for the Ping() own_id constructor argument. You can either pass in an id you know to be unique (like above) and manage Ping() objects yourself, or you can change this line (110) in the constructor:

self.own_id = os.getpid() & 0xFFFF

Also see this question and answer and answer comment thread for more info.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文