Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multiping function #20

Open
wants to merge 53 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
37a50fc
We cannot connect() using RAW_SOCKET, let's use a writer;
May 11, 2017
f995b14
Typo: icmp type is unsigned
May 11, 2017
864d961
Support ipv6
May 11, 2017
4865c85
Handle timeout in verbose_ping
May 11, 2017
7567fc7
getprotobyname only once, that will reduce the # of open files
May 15, 2017
929e112
When using raw socket, all received packet will be read on all socket…
May 15, 2017
9375d2a
Send packets synchronously (I dunno how to do it properly)
May 16, 2017
1ca0443
fix: use sendto asynchronously
Aug 9, 2017
8fd1be5
s/next/continue/: this ain't perl
Nov 9, 2017
3287e62
Merge pull request #2 from JackSlateur/master
anton-belousov Nov 9, 2017
f48ec95
Fix issue #5: Does not work on Windows
asantoni Apr 16, 2018
8c239df
Added install_requires
May 8, 2018
921b898
Merge pull request #7 from eddebc/master
anton-belousov May 8, 2018
df19b7f
Merge pull request #6 from asantoni/windows
anton-belousov Jun 15, 2018
61cbfc0
Update README.rst
anton-belousov Jun 15, 2018
66ae422
setup.py version updated.
anton-belousov Jun 15, 2018
d56694c
setup.py fixed for updated setuptools
anton-belousov Jun 15, 2018
eae0158
Moved from print to standard logging
Jul 20, 2018
4c00805
Update __init__.py
hergla Aug 20, 2018
73a48af
Handle exceptions in the sending callback
Apr 23, 2019
7699b61
Merge pull request #8 from wise0wl/master
anton-belousov Jul 4, 2020
b129aa9
PR-8 logging
anton-belousov Jul 4, 2020
ea46185
Merge pull request #15 from stellarbit/pr-8-logging
anton-belousov Jul 4, 2020
0c4cf0b
Testing code
anton-belousov Jul 4, 2020
a93e0a1
Merge pull request #12 from nARN/master
anton-belousov Jul 4, 2020
6f5f03b
Debug output for host resolution
anton-belousov Jul 4, 2020
e84ae67
IPv4 fix
anton-belousov Jul 4, 2020
245b58f
Tests fix
anton-belousov Jul 4, 2020
32f3592
Merge pull request #16 from stellarbit/pr-12-exceptions-in-callback
anton-belousov Jul 4, 2020
22f3c1f
Merge pull request #9 from hergla/master
anton-belousov Jul 4, 2020
defc647
Contributors list updated.
anton-belousov Jul 4, 2020
29ec094
Windows fix for Python 3.8
anton-belousov Jul 4, 2020
1b886cb
Release updates.
anton-belousov Jul 4, 2020
642e709
issue 17 fix attempt
anton-belousov Jul 5, 2020
a8cfecf
Tests improved
anton-belousov Jul 5, 2020
a8b94fe
Update __init__.py
Crypto-Spartan Jul 5, 2020
ed0b89b
Update __init__.py
Crypto-Spartan Jul 5, 2020
61dc2b0
Merge pull request #18 from stellarbit/issue-17-fix
anton-belousov Jul 5, 2020
557e02c
Tests adjusted, added contributors.
anton-belousov Jul 5, 2020
c6aa7be
Merge pull request #19 from Crypto-Spartan/master
anton-belousov Jul 5, 2020
0ad3a94
New distribution changes.
anton-belousov Jul 5, 2020
a65c5fb
Docs updated.
anton-belousov Jul 5, 2020
c6da4cd
Merge pull request #1 from stellarbit/master
Crypto-Spartan Jul 6, 2020
f66cbd6
Added description for multiping, changed examples to asyncio.run() in…
Crypto-Spartan Jul 18, 2020
5110a1c
add leading underscore to internal functions
Crypto-Spartan Jul 18, 2020
d44c476
return of ping() now in milliseconds
Crypto-Spartan Jul 18, 2020
3cb8aa7
add multiping functions, fix delay in verbose_ping()
Crypto-Spartan Jul 18, 2020
2537be2
update setup.py to 0.4.0
Crypto-Spartan Jul 18, 2020
e0b220d
add comments to functions
Crypto-Spartan Jul 18, 2020
9f008fd
change version slightly because of modifying previous commits
Crypto-Spartan Oct 30, 2020
fa4e153
Merge branch 'master' into master
Crypto-Spartan Oct 30, 2020
36a09fa
updated setup.py for 0.4.1
Crypto-Spartan Oct 30, 2020
865ccfd
Merge remote-tracking branch 'origin/master'
Crypto-Spartan Oct 30, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 30 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Or use the latest version from the master (if you are brave enough)::
Using aioping
-------------

There are 2 ways to use the library.
There are 3 ways to use the library.

First one is interactive, which sends results to standard Python logger.
Please make sure you are running this code under root, as only
Expand All @@ -30,10 +30,9 @@ root is allowed to send ICMP packets:
import logging

logging.basicConfig(level=logging.INFO) # or logging.DEBUG
loop = asyncio.get_event_loop()
loop.run_until_complete(aioping.verbose_ping("google.com"))
asyncio.run(aioping.verbose_ping("google.com"))

Alternatively, you can call a ping function, which returns a
Secondly, you can call a ping function, which returns a
ping delay in milliseconds or throws an exception in case of
error:

Expand All @@ -44,14 +43,31 @@ error:

async def do_ping(host):
try:
delay = await aioping.ping(host) * 1000
delay = await aioping.ping(host)
print("Ping response in %s ms" % delay)

except TimeoutError:
print("Timed out")

loop = asyncio.get_event_loop()
loop.run_until_complete(do_ping("google.com"))
asyncio.run(do_ping("google.com"))

The last way is to call a multiping function, which returns a
list of tuples. The tuples are formatted as (dest_addr, delay) with
delay measured in milliseconds. In the event of a timeout, the tuple
will be returned as (dest_addr, 'TimeoutError'). Lowering the timeout
will result in a faster return. NOTE: This function is limited to 255
pings at one time due to the limitation of select().

.. code:: python

import asyncio
import aioping

async def do_multiping():
results = await aioping.multiping(['8.8.8.8','1.1.1.1','google.com'])
print(results)

asyncio.run(do_multiping())

Methods
-------
Expand All @@ -70,6 +86,13 @@ Methods
- ``count`` - count of packets to send (default: ``3``)
- ``family`` - family of resolved address - ``socket.AddressFamily.AF_INET`` for IPv4, ``socket.AddressFamily.AF_INET6``
for IPv6 or ``None`` if it doesn't matter (default: ``None``)

``multiping(dest_addr, timeout=5, family=None)``

- ``dest_addr`` - destination address, IPv4, IPv6 or hostname
- ``timeout`` - timeout in seconds (default: ``5``)
- ``family`` - family of resolved address - ``socket.AddressFamily.AF_INET`` for IPv4, ``socket.AddressFamily.AF_INET6``
for IPv6 or ``None`` if it doesn't matter (default: ``None``)

Credits
-------
Expand Down
61 changes: 51 additions & 10 deletions aioping/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
proto_icmp6 = socket.getprotobyname("ipv6-icmp")


def checksum(buffer):
def _checksum(buffer):
"""
I'm not too confident that this is right but testing seems
to suggest that it gives the same answers as in_cksum in ping.c
Expand Down Expand Up @@ -136,7 +136,7 @@ def checksum(buffer):
return answer


async def receive_one_ping(my_socket, id_, timeout):
async def _receive_one_ping(my_socket, id_, timeout):
"""
receive the ping from the socket.
:param my_socket:
Expand Down Expand Up @@ -170,7 +170,7 @@ async def receive_one_ping(my_socket, id_, timeout):
data = rec_packet[offset + 8:offset + 8 + struct.calcsize("d")]
time_sent = struct.unpack("d", data)[0]

return time_received - time_sent
return (time_received - time_sent) * 1000

except asyncio.TimeoutError:
asyncio.get_event_loop().remove_writer(my_socket)
Expand All @@ -180,7 +180,7 @@ async def receive_one_ping(my_socket, id_, timeout):
raise TimeoutError("Ping timeout")


def sendto_ready(packet, socket, future, dest):
def _sendto_ready(packet, socket, future, dest):
try:
socket.sendto(packet, dest)
except (BlockingIOError, InterruptedError):
Expand All @@ -193,7 +193,7 @@ def sendto_ready(packet, socket, future, dest):
future.set_result(None)


async def send_one_ping(my_socket, dest_addr, id_, timeout, family):
async def _send_one_ping(my_socket, dest_addr, id_, timeout, family):
"""
Send one ping to the given >dest_addr<.
:param my_socket:
Expand All @@ -215,7 +215,7 @@ async def send_one_ping(my_socket, dest_addr, id_, timeout, family):
data = struct.pack("d", default_timer()) + data.encode("ascii")

# Calculate the checksum on the data and the dummy header.
my_checksum = checksum(header + data)
my_checksum = _checksum(header + data)

# Now that we have the right checksum, we put that in. It's just easier
# to make up a new header than to stuff it into the dummy.
Expand All @@ -225,7 +225,7 @@ async def send_one_ping(my_socket, dest_addr, id_, timeout, family):
packet = header + data

future = asyncio.get_event_loop().create_future()
callback = functools.partial(sendto_ready, packet=packet, socket=my_socket, dest=dest_addr, future=future)
callback = functools.partial(_sendto_ready, packet=packet, socket=my_socket, dest=dest_addr, future=future)
asyncio.get_event_loop().add_writer(my_socket, callback)

await future
Expand Down Expand Up @@ -282,8 +282,8 @@ async def ping(dest_addr, timeout=10, family=None):

my_id = uuid.uuid4().int & 0xFFFF

await send_one_ping(my_socket, addr, my_id, timeout, family)
delay = await receive_one_ping(my_socket, my_id, timeout)
await _send_one_ping(my_socket, addr, my_id, timeout, family)
delay = await _receive_one_ping(my_socket, my_id, timeout)
my_socket.close()

return delay
Expand All @@ -310,5 +310,46 @@ async def verbose_ping(dest_addr, timeout=2, count=3, family=None):
break

if delay is not None:
delay *= 1000
logger.info("%s get ping in %0.4fms" % (dest_addr, delay))


async def _do_multiping(dest_addr, timeout=5, family=None):
"""
Execute the ping of a single address for multiping function
"""

try:
delay = await ping(dest_addr, timeout, family)
return (dest_addr, delay)

except TimeoutError:
return (dest_addr, 'TimeoutError')


async def _multiping_sem(dest_addr, sem, timeout=5, family=None):
"""
run the multiping with asyncio.Semaphore limit of 255
"""

async with sem:
return await _do_ping_sem(dest_addr, timeout, family)


async def multiping(dest_addr, timeout=5, family=None):
"""
Returns tuple (dest_addr, delay) for each ip address
or domain submitted in a list. Will return
(dest_addr, 'TimeoutError') if ping times out.
"""

# limit because of select()
if len(dest_addr) > 255:
sem = asyncio.Semaphore(255)
tasks = [_multiping_sem(x, sem, timeout, family) for x in dest_addr]

# no limit if pinging less than 255 addresses
else:
tasks = [_do_multiping(x, timeout, family) for x in dest_addr]

return await asyncio.gather(*tasks)

2 changes: 1 addition & 1 deletion aioping/tests/test_aioping.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from unittest import TestCase
import asyncio
from aioping import verbose_ping, ping
from aioping import verbose_ping, ping, multiping
import logging
import socket

Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
setup(
name="aioping",
packages=["aioping"],
version="0.3.1",
version="0.4.1",
install_requires=["async_timeout", "aiodns"],
description="Asyncio ping implementation",
author="Anton Belousov",
author_email="anton@stellarbit.com",
url="https://github.com/stellarbit/aioping",
download_url="https://github.com/stellarbit/aioping/tarball/0.3.1",
download_url="https://github.com/stellarbit/aioping/tarball/0.4.1",
keywords=["network", "icmp", "ping", "asyncio"],
classifiers=[
"Development Status :: 4 - Beta",
Expand Down