使用socket+Python实现ping

import os
import socket
import struct
import select
import time# 计算校验和,用于确保数据的完整性
def checksum(source_string):sum = 0count = 0max_count = len(source_string)# 处理成对的字节while count < max_count - 1:val = source_string[count + 1] * 256 + source_string[count]sum = sum + valsum = sum & 0xffffffff  # 保持sum为32位count = count + 2# 处理最后一个字节(如果长度为奇数)if max_count % 2:sum = sum + source_string[-1]sum = sum & 0xffffffff# 折叠高16位和低16位sum = (sum >> 16) + (sum & 0xffff)sum = sum + (sum >> 16)# 取反得到最终的校验和answer = ~sumanswer = answer & 0xffff# 最终调整顺序(大端或小端)answer = answer >> 8 | (answer << 8 & 0xff00)return answer# 创建 ICMP echo 请求包
def create_packet(id):# 头部类型为8(ICMP echo请求),代码为0,校验和为0,id为传入的id,序列号为1header = struct.pack('bbHHh', 8, 0, 0, id, 1)data = 256 * b'Q'  # 数据部分my_checksum = checksum(header + data)  # 计算校验和# 重新打包头部,包含正确的校验和header = struct.pack('bbHHh', 8, 0, socket.htons(my_checksum), id, 1)return header + data# 执行 ping 操作
def ping(dest_addr, timeout=1, count=4):icmp = socket.getprotobyname("icmp")  # 获取 ICMP 协议的编号# 创建原始套接字my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)my_ID = os.getpid() & 0xFFFF  # 生成一个唯一的IDsent_count = 0  # 发送的ping包数量received_count = 0  # 收到的ping包数量while sent_count < count:sent_count += 1packet = create_packet(my_ID)my_socket.sendto(packet, (dest_addr, 1))  # 发送 ICMP echo 请求while True:started_select = time.time()# 监听套接字是否有数据可读what_ready = select.select([my_socket], [], [], timeout)how_long_in_select = (time.time() - started_select)if what_ready[0] == []:  # 超时print("请求超时。")breaktime_received = time.time()received_packet, addr = my_socket.recvfrom(1024)  # 接收数据包icmp_header = received_packet[20:28]  # 提取 ICMP 头部type, code, checksum, packet_ID, sequence = struct.unpack("bbHHh", icmp_header)if packet_ID == my_ID:  # 确认是发自我们的请求bytes_In_double = struct.calcsize("d")time_sent = struct.unpack("d", received_packet[28:28 + bytes_In_double])[0]print("来自 {} 的回复: 字节={} 时间={:.2f}ms".format(addr[0], len(received_packet), (time_received - time_sent) * 1000))received_count += 1breaktime_left = timeout - how_long_in_selectif time_left <= 0:print("请求超时。")breakmy_socket.close()return received_count# 使用示例
if __name__ == '__main__':dest = input("输入要 ping 的主机: ")print("正在用 Python ping {}:".format(dest))ping(dest)

# 处理成对的字节
    while count < max_count-1:
        val = source_string[count + 1]*256 + source_string[count]
        sum = sum + val
        sum = sum & 0xffffffff
        count = count + 2

    1.以成对的字节进行遍历,将源字符串(source_string)中的成对字节合并成16位的整数,并将这些整数累加到 sum 变量中。源字符串是要发送的数据包的内容。

    2.val = source_string[count + 1]*256 + source_string[count] 将源字符串中的当前字节(source_string[count])和下一个字节(source_string[count + 1])结合起来形成一个16位的整数。由于在大多数计算机中,整数是以小端序存储的,所以 count + 1 位置的字节是高字节,需要乘以256(即左移8位)以放在结果整数的高位,然后加上 count 位置的字节作为低位。

    3.sum = sum + val 这里将刚刚计算出的16位整数 val 加到累加器 sum 上。这个累加器最终会包含所有16位整数的和。

    4.print(source_string)的结果为source_string:  b'\x08\x00\x00\x00\xe4H\x01\x00QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ'

由于字符串长度为264,循环将运行132次(假设字符串有偶数个字节)。第一次循环会处理索引为0和1的字节(\x08 和 \x00),第二次循环处理索引为2和3的字节(\x00 和 \x00),以此类推,直到处理完所有的字节对。(一个字节是8位)

# 最终调整顺序

answer = answer >> 8 | (answer << 8 & 0xff00)

    answer >> 8:这将 answer 的所有位向右移动8位。这个操作会将原始 answer 的高8位移动到低8位的位置。
    answer << 8:这将 answer 的所有位向左移动8位。这个操作会将原始 answer 的低8位移动到高8位的位置。
    answer << 8 & 0xff00:& 0xff00 是一个掩码操作,它将确保左移操作后只保留高8位的值,低8位将被清零。0xff00 是一个16位的整数,其中高8位是1,低8位是0。
    answer >> 8 | (answer << 8 & 0xff00):| 是按位或操作,它将上述两个操作的结果组合起来。右移的结果会在新值的低8位,左移并掩码后的结果会在新值的高8位。 

以 4660 为例操作:
    右移8位 (answer >> 8):将 00010010 01100100 右移8位变为 00000000 00010010,在十进制中这是 18。
    左移8位并应用掩码 (answer << 8 & 0xff00):首先,将 00010010 01100100 左移8位变为 01100100 00000000,这个操作后在十进制中为 25856。然后应用掩码 0xff00(在二进制中为 11111111 00000000),结果仍为 01100100 00000000(25856)。
    合并两个结果:使用按位或操作将 00000000 00010010(18)和 01100100 00000000(25856)合并,得到 01100100 00010010,这在十进制中为 25618。

header = struct.pack('bbHHh', 8, 0, 0, id, 1) 

    struct.pack() 是 Python struct 模块中的一个函数,它的作用是将给定的值打包成特定格式的二进制数据。在网络编程和二进制文件处理中经常使用这个函数,因为它能够根据指定的格式将Python数据类型转换为字节字符串,这些字节字符串可以被发送到网络或写入文件。

socket.htons(my_checksum) 

    socket.htons() 函数在 Python 中用于将一个16位的正短整数从主机字节序转换为网络字节序。在网络编程中经常需要这样做,因为不同的计算机系统有不同的整数存储方式,即字节序问题。 

my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)

    调用 socket.socket(socket.AF_INET, socket.SOCK_RAW) 的作用是创建一个原始套接字(raw socket),用于低级网络通信,其中可以发送和接收特定网络协议的数据包。 
    socket.AF_INET: 这个参数指定地址族为 IPv4。它是用来确定套接字使用的网络层协议,AF_INET 表示使用 IPv4 地址。
    socket.SOCK_RAW: 这个参数指定套接字类型为“原始套接字”。原始套接字允许你访问底层协议,如 ICMP、TCP、UDP 等。使用原始套接字,你可以构造自己的数据包头部,进行更为灵活的网络操作。
    icmp: 这是 socket.getprotobyname("icmp") 返回的 ICMP 协议编号。它告诉套接字使用 ICMP 协议。ICMP 通常用于发送控制消息,如 ping 请求和响应。

my_ID = os.getpid() & 0xFFFF 

    os.getpid() 函数用于获取当前进程的进程 ID(PID)。在 Python 中,这个函数返回一个整数,代表当前运行的进程的唯一标识符。 
    在网络编程,特别是在构建 ICMP Echo 请求(如 ping 命令)时,通常需要一个标识符来区分不同的 Echo 请求。使用当前进程的 PID 作为标识符是一个常见的做法,因为它能够为每个不同的进程提供一个唯一的标识。然而,由于 PID 的大小可能超出了 ICMP Echo 请求头中标识符字段所能容纳的范围(通常为 16 位),所以通过与 0xFFFF 进行按位与运算,可以确保得到的标识符适合在该字段中使用,即将 PID 限制在 0 到 65535 (即 0xFFFF)的范围内。这样做既保留了一定程度的唯一性,又符合协议的要求。(这个my_ID就是传入create_packet()函数中header = struct.pack('bbHHh', 8, 0, 0, id, 1) 所用的id参数。)

my_socket.sendto(packet, (dest_addr, 1)) 

   调用 my_socket.sendto(packet, (dest_addr, 1)) 的作用是通过之前创建的原始套接字 my_socket 发送一个数据包 packet 到指定的目标地址 dest_addr 上的端口号 1。

what_ready = select.select([my_socket], [], [], timeout)

if what_ready[0] == []:  # Timeout

                print("请求超时")

                break

(以上代码可以省去)

    调用 select.select([my_socket], [], [], timeout) 的作用是使用 select 模块来监视套接字的状态,检查套接字 my_socket 是否有数据可读,是否可以无阻塞地进行读操作。这是一种多路复用输入/输出的方式,用于在多个通信渠道上等待事件发生,从而避免程序在单个通信渠道上阻塞。

    在 select.select() 调用中,参数有以下含义:
    第一个参数 [my_socket] 是一个套接字列表,select 将监视这个列表中的套接字以查看它们是否变得可读(即是否有数据到达套接字,可以进行读操作)。
    第二个参数 [] 是一个空列表,用于指定需要检查是否可写的套接字列表。在这个调用中,我们不关心套接字是否可写,所以传入一个空列表。
    第三个参数 [] 同样是一个空列表,用于指定需要检查是否有错误的套接字列表。同样,在这个调用中,我们不关心套接字是否有错误,所以传入一个空列表。
    第四个参数 timeout 是一个超时值,指定 select 等待的最长时间(以秒为单位)。如果指定了超时时间,即使没有套接字变成可读,select 也会在超时后返回。如果 timeout 是 None,select 将会无限期地等待直到至少有一个套接字变得可读。

    例如,当你发送了一个 ICMP Echo 请求后,你可能会使用 select.select() 来等待一个响应,而不会阻塞程序的运行。如果 select 在超时时间内检测到 my_socket 有数据可读,它会返回一个包含 my_socket 的列表,这意味着你可以从套接字读取数据而不会阻塞。如果在超时时间内没有数据可读,select 将返回一个空列表,并且你可以决定是否重新发送请求、继续等待或进行其他操作。

 

received_packet, addr = my_socket.recvfrom(1024)

    调用 received_packet, addr = my_socket.recvfrom(1024) 的作用是接收通过网络传输到达指定的原始套接字 my_socket 的数据,并且读取最多 1024 字节的数据。

    在这个函数调用中:
    received_packet 是接收到的数据内容。这个数据包含了发送方发送的原始数据,可能包括IP头部、ICMP头部以及随后的数据部分。
    addr 是一个包含发送方地址信息的元组,通常形式为 (IP地址, 端口号)。在接收到的是 ICMP 消息的情况下,端口号通常是不相关的,因为 ICMP 是网络层的协议,不使用传输层的端口号。
my_socket 是之前创建的原始套接字,用于在网络上发送和接收低级别的协议数据包。
    1024 是指定的缓冲区大小,以字节为单位。它告诉 recvfrom 方法在单次调用中最多可以接收多少字节的数据。

    因此,当调用 my_socket.recvfrom(1024) 时,它会阻塞当前线程,直到有数据到达套接字或者套接字关闭。一旦接收到数据,它会停止阻塞,并将数据和发送方的地址赋值给 received_packet 和 addr 变量。这个操作通常用于网络通信中的数据接收,例如,在实现ping程序时,用来接收ICMP Echo响应。

icmp_header = received_packet[20:28]

    提取接收到的数据包中的 ICMP 协议头部分,ICMP 协议头通常包含类型、代码和校验和等信息,长度为 8 字节。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/6772.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5.5代码

目录 1.内存空间 1.内存空间 真的要吐了&#xff0c;人都麻了题还没看完&#xff0c;看样子就是要想办法提取出来想要的东西2022第十三届蓝桥杯决赛C/C大学A组-C题内存空间_蓝桥杯a组c语言题目-CSDN博客 这个是一个非常清晰的代码&#xff0c;好几个帖子都管这个题叫大模拟题…

开源版本管理系统的搭建二:SVN部署及使用

作者&#xff1a;私语茶馆 1. Visual SVN Server部署 SVN Server部署包括&#xff1a; 创建版本仓库创建用户 这些部署是通过VisualSVN Server Manager实现的&#xff0c;如下图&#xff1a; VisualSVN Server Manager&#xff08;安装后自带&#xff09; 1.1.SVN 初始化配…

【QEMU系统分析之实例篇(十五)】

系列文章目录 第十五章 QEMU系统仿真的机器创建分析实例 文章目录 系列文章目录第十五章 QEMU系统仿真的机器创建分析实例 前言一、QEMU是什么&#xff1f;二、QEMU系统仿真的机器创建分析实例1.系统仿真的命令行参数2.应用旧的机器设置项qemu_apply_legacy_machine_options()…

ROS2学习——Docker环境下安装于使用(1)

目录 一、简要 二、ROS2和ROS1区别 三、环境搭建与安装 &#xff08;2&#xff09;拉取ubuntu22.04镜像 &#xff08;2&#xff09;安装ROS2 1. 基本设置 2.设置源 3.安装ROS2功能包 4.测试 四、相关指令学习 1.小海龟测试 2.ros2 node等指令 3.rqt 一、简要 随着R…

Git常用(持续更新)

常用场景&#xff1a; 初始化&#xff1a; git config --global user.name "codelabs" git config --global user.email mycodelabs.com git init git remote add origin https://github.com/username/repository.git git pull origin master 提交&#xff1a; gi…

踏春正当时!VELO Prevail Ride带你探索多元骑行潮流体验~

嘿&#xff0c;朋友&#xff01;踏春正当时嘞&#xff01;在这个追求个性化与多元化的新时代&#xff0c;骑行爱好者们也开始寻找能适应各种骑行场景的理想坐垫。从悠闲自在的日常通勤&#xff0c;到热血沸腾的公路竞速&#xff0c;再到勇攀高峰的山地探险&#xff0c;维乐VELO…

HNU-人工智能-实验3-贝叶斯分类器

人工智能-实验3 计科210x 甘晴void 【感悟】本实验值得自己完成一遍 文章目录 人工智能-实验3一、实验目的二、实验平台三、实验内容3.0 基础知识3.1 条件概率&#xff08;选择题&#xff09;3.2 贝叶斯公式&#xff08;选择题&#xff09;3.3 朴素贝叶斯分类算法流程3.3.1 算…

多线程系列(三) -synchronized 关键字使用详解

一、简介 在之前的线程系列文章中&#xff0c;我们介绍了线程创建的几种方式以及常用的方法介绍。 今天我们接着聊聊多线程线程安全的问题&#xff0c;以及解决办法。 实际上&#xff0c;在多线程环境中&#xff0c;难免会出现多个线程对一个对象的实例变量进行同时访问和操…

3-qt综合实例-贪吃蛇的游戏程序

引言&#xff1a; 如题&#xff0c;本次实践课程主要讲解贪吃蛇游戏程序。 qt贪吃蛇项目内容&#xff1a; 一、功能需求 二、界面设计 各组件使用&#xff1a; 对象名 类 说明 Widget QWidge 主窗体 btnRank QPushButton 排行榜-按钮 groupBox QGroupBox 难…

【数据结构与算法】力扣 347. 前 K 个高频元素

题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 示例 1: 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2]示例 2: 输入: nums [1], k 1 输出: [1]提示&#xff1a; 1 < nums.length < …

python数据分析——数据分析的统计推断

数据分析的统计推断 前言一、提出问题二、统计归纳方法三、统计推断四、统计推断步骤4.1.点估计4.2.区间估计4.2.1. 总体方差已知4.2.2总体方差未知 4.3. 假设检验4.4. 假设检验的假设4.5.显著性水平 五、检验统计量六、检验方法七、拒绝域八、假设检验步骤九、重要假设检验方法…

伺服电机初识

目录 一、伺服电机的介绍二、伺服电机的基本原理三、伺服电机的技术特点四、伺服电机的分类五、实际产品介绍1、基本技术规格&#xff1a;2、MD42电机硬件接口3、通讯协议介绍3.1 通讯控制速度运行3.2 通讯控制位置运行3.3 通讯控制转矩运行 4、状态灯与报警信息 一、伺服电机的…

金融行业AI大模型百项应用案例综述【大模型系列】

逐浪金融大模型的玩家&#xff0c;除了BAT、华为等高科技巨头&#xff0c;试图以技术优势充当产业链的“卖铲人”&#xff0c;更多的还是金融和类金融企业&#xff0c;包括银行、保险、互金、券商等&#xff0c;既不想被喧宾夺主&#xff0c;又不想肥水外流&#xff0c;都在押注…

确保WEB应用安全:深入分析与有效防范

目录 前言 一. 概述 二. WEB应用体系结构脆弱性分析 1.前端用户界面 2.后端服务器 3.数据库 4.风险和后果 5.缓解措施 三. SQL注入攻击及防范 1.使用参数化查询或预定义语句 2.输入验证和转义 3.最小化数据库权限 4.使用存储过程和预定义语句 5.保持软件更新 6.实…

【Linux系统化学习】网络套接字(编写简单的TCP服务端和客户端)

目录 TCP服务端 创建套接字 解决绑定失败问题 填充网络信息 绑定 设置监听状态 接受请求 收取和反馈消息 完整的服务端封装代码 TCP客户端 创建套接字 填充网络信息 发起连接 发送和收取消息 客户端完整代码 一些补充 TCP服务端 初始化服务端 创建套接字 和U…

【JAVA项目】基于个人需求和地域特色的【外卖推荐系统】

技术简介&#xff1a;采用B/S架构、ssm 框架、Java技术、MySQL等技术实现。 系统简介&#xff1a;统权限按管理员&#xff0c;商家和用户这三类涉及用户划分。(a) 管理员&#xff1b;管理员使用本系统涉到的功能主要有&#xff1a;首页&#xff0c;个人中心&#xff0c;用户管理…

C++ | Leetcode C++题解之第61题旋转链表

题目&#xff1a; 题解&#xff1a; class Solution { public:ListNode* rotateRight(ListNode* head, int k) {if (k 0 || head nullptr || head->next nullptr) {return head;}int n 1;ListNode* iter head;while (iter->next ! nullptr) {iter iter->next;n…

ctfshow web入门 sql注入 web201--web208

web201 先扫描先 python .\sqlmap.py -u "http://4863661d-2371-4812-ae62-128fadbdc0a4.challenge.ctf.show/api/?id" --user-agentsqlmap 加头 python .\sqlmap.py -u "http://4863661d-2371-4812-ae62-128fadbdc0a4.challenge.ctf.show/api/?id" --u…

每日算法之从前序与中序遍历序列构造二叉树

题目描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: &#xfffc; 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: […

央视影音 视频下载 2

浏览器猫抓插件&#xff0c;拿到视频地址&#xff0c;这个地址的播放不正常&#xff0c;花屏。https://dh5.cntv.qcloudcdn.com/asp/h5e/hls/2000/0303000a/3/default/6edd15a0ebb3467993bec51a95be0e22/2000.m3u8 改一下地址&#xff0c;把代码中的h5e去掉。网址改为https://…