在Python中,是否有一种方法可以通过ICMP来ping服务器,如果服务器响应则返回TRUE,如果没有响应则返回FALSE ?
当前回答
在linux上,可以创建ICMP数据报(不是原始)套接字,而不需要root(或setuid或CAP_NET_RAW): https://unix.stackexchange.com/a/592914。最后我得到了
$ id
uid=1000(raylu) gid=1000(raylu) [...]
$ sudo sysctl net.ipv4.ping_group_range='1000 1000'
import socket
import struct
import time
def main():
ping('192.168.1.10')
def ping(destination):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.getprotobyname('icmp'))
sock.settimeout(10.0)
start_time = time.time_ns() # python 3.7+ only
payload = struct.pack('L', start_time)
sock.sendto(encode(payload), (destination, 0))
while (time.time_ns() - start_time) // 1_000_000_000 < 10:
try:
data, source = sock.recvfrom(256)
except socket.timeout:
print('timed out')
return
message_type, message_code, check, identifier, sequence_number = struct.unpack('bbHHh', data[:8])
if source == (destination, 0) and message_type == ICMP.ECHO_REPLY and data[8:] == payload:
print((time.time_ns() - start_time) // 1_000_000, 'ms')
break
else:
print('got unexpected packet from %s:' % source[0], message_type, data[8:])
else:
print('timed out')
def encode(payload: bytes):
# calculate checksum with check set to 0
checksum = calc_checksum(icmp_header(ICMP.ECHO_REQUEST, 0, 0, 1, 1) + payload)
# craft the packet again with the checksum set
return icmp_header(ICMP.ECHO_REQUEST, 0, checksum, 1, 1) + payload
def icmp_header(message_type, message_code, check, identifier, sequence_number) -> bytes:
return struct.pack('bbHHh', message_type, message_code, check, identifier, sequence_number)
def calc_checksum(data: bytes) -> int:
'''RFC 1071'''
# code stolen from https://github.com/alessandromaggio/pythonping/blob/a59ce65a/pythonping/icmp.py#L8
'''
MIT License
Copyright (c) 2018 Alessandro Maggio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
subtotal = 0
for i in range(0, len(data)-1, 2):
subtotal += (data[i] << 8) + data[i+1]
if len(data) % 2:
subtotal += (data[len(data)-1] << 8)
while subtotal >> 16:
subtotal = (subtotal & 0xFFFF) + (subtotal >> 16)
check = ~subtotal
return ((check << 8) & 0xFF00) | ((check >> 8) & 0x00FF)
class ICMP:
ECHO_REPLY = 0
ECHO_REQUEST = 8
虽然这里其他答案建议的许多软件包也可以工作
其他回答
很多答案都忽略了一点(至少在Windows中),如果ping命令收到“目标主机不可达”的回复,它将返回0(表示成功)。
下面是我的代码,它检查b' ttl ='是否在响应中,因为它只在ping到达主机时才出现。注意:此代码的大部分是基于这里的其他答案。
import platform
import subprocess
def ping(ipAddr, timeout=100):
'''
Send a ping packet to the specified host, using the system ping command.
Accepts ipAddr as string for the ping destination.
Accepts timeout in ms for the ping timeout.
Returns True if ping succeeds otherwise Returns False.
Ping succeeds if it returns 0 and the output includes b'TTL='
'''
if platform.system().lower() == 'windows':
numFlag = '-n'
else:
numFlag = '-c'
completedPing = subprocess.run(['ping', numFlag, '1', '-w', str(timeout), ipAddr],
stdout=subprocess.PIPE, # Capture standard out
stderr=subprocess.STDOUT) # Capture standard error
# print(completedPing.stdout)
return (completedPing.returncode == 0) and (b'TTL=' in completedPing.stdout)
print(ping('google.com'))
注意:这将捕获输出而不是打印输出,因此如果您希望看到ping的输出,则需要打印completedPing。返回前的Stdout。
import subprocess
ping_response = subprocess.Popen(["/bin/ping", "-c1", "-w100", "192.168.0.1"], stdout=subprocess.PIPE).stdout.read()
在linux上,可以创建ICMP数据报(不是原始)套接字,而不需要root(或setuid或CAP_NET_RAW): https://unix.stackexchange.com/a/592914。最后我得到了
$ id
uid=1000(raylu) gid=1000(raylu) [...]
$ sudo sysctl net.ipv4.ping_group_range='1000 1000'
import socket
import struct
import time
def main():
ping('192.168.1.10')
def ping(destination):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.getprotobyname('icmp'))
sock.settimeout(10.0)
start_time = time.time_ns() # python 3.7+ only
payload = struct.pack('L', start_time)
sock.sendto(encode(payload), (destination, 0))
while (time.time_ns() - start_time) // 1_000_000_000 < 10:
try:
data, source = sock.recvfrom(256)
except socket.timeout:
print('timed out')
return
message_type, message_code, check, identifier, sequence_number = struct.unpack('bbHHh', data[:8])
if source == (destination, 0) and message_type == ICMP.ECHO_REPLY and data[8:] == payload:
print((time.time_ns() - start_time) // 1_000_000, 'ms')
break
else:
print('got unexpected packet from %s:' % source[0], message_type, data[8:])
else:
print('timed out')
def encode(payload: bytes):
# calculate checksum with check set to 0
checksum = calc_checksum(icmp_header(ICMP.ECHO_REQUEST, 0, 0, 1, 1) + payload)
# craft the packet again with the checksum set
return icmp_header(ICMP.ECHO_REQUEST, 0, checksum, 1, 1) + payload
def icmp_header(message_type, message_code, check, identifier, sequence_number) -> bytes:
return struct.pack('bbHHh', message_type, message_code, check, identifier, sequence_number)
def calc_checksum(data: bytes) -> int:
'''RFC 1071'''
# code stolen from https://github.com/alessandromaggio/pythonping/blob/a59ce65a/pythonping/icmp.py#L8
'''
MIT License
Copyright (c) 2018 Alessandro Maggio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
subtotal = 0
for i in range(0, len(data)-1, 2):
subtotal += (data[i] << 8) + data[i+1]
if len(data) % 2:
subtotal += (data[len(data)-1] << 8)
while subtotal >> 16:
subtotal = (subtotal & 0xFFFF) + (subtotal >> 16)
check = ~subtotal
return ((check << 8) & 0xFF00) | ((check >> 8) & 0x00FF)
class ICMP:
ECHO_REPLY = 0
ECHO_REQUEST = 8
虽然这里其他答案建议的许多软件包也可以工作
确保安装了pyping或安装它
#!/usr/bin/python
import pyping
response = pyping.ping('Your IP')
if response.ret_code == 0:
print("reachable")
else:
print("unreachable")
我借鉴了其他答案。尝试简化和最小化查询。
import platform, os
def ping(host):
result = os.popen(' '.join(("ping", ping.param, host))).read()
return 'ttl=' in result.lower()
ping.param = "-n 1" if platform.system().lower() == "windows" else "-c 1"
编辑:根据奥利维尔B的评论,忽略返回的大小写。
推荐文章
- 我如何在Django中过滤一个DateTimeField的日期?
- 在Python中用索引迭代列表
- -e,——editable选项在pip install中什么时候有用?
- 使用pip命令从requirements.txt升级python包
- Django更改默认的runserver端口
- 输入对象的datetime。Datetime没有Datetime属性
- numpy数组的Python内存使用情况
- NumPy或Pandas:保持数组类型为整数,同时具有NaN值
- 列表理解条件中的' elif '
- Python的“property”和“attribute”有什么区别?
- 在Python中格式化多行字典的正确方法是什么?
- 向异常添加信息?
- Sphinx autodoc不够自动
- 如何限制Django模型中数值字段的最大值?
- 访问对象内存地址