Python ICMP原始套接字实现

0 投票
2 回答
3790 浏览
提问于 2025-04-17 08:18

我对Python还比较陌生,所以请多多包涵...

我正在通过原始套接字实现一个服务器和一个客户端。我有必要的权限。

我定义的服务器是这样的:

host = socket.gethostbyname(socket.gethostname())
address = (host, 22224)
sockSer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
sockSer.bind(address)
sockSer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
packet, addr = sockSer .recvfrom(4096)   # wait for packet from client

问题1) 为什么我不能简单地写:hosts = 'localhost'?如果我这样做,就无法写这一行:sockSer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)。然后服务器就收不到客户端的消息。只有在使用gethostbyname(socket.gethostname())时,我才能得到192.168.1.101,然后它才有效。

在另一个类中:客户端套接字:

host = socket.gethostbyname(socket.gethostname())
address = (host, 22224)
sockCli = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)

问题2) 我是否也需要写:sockCli.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)或者sockCli.connect(address)?似乎不使用connect命令也能正常工作。对于客户端套接字来说,这样可以吗?

现在,当我执行以下操作时,问题就出现了:

1) 从客户端发送一个数据包到服务器:

header=...
payload='a'
sockCli.sendto(header + payload, address)

2) 在服务器接收数据包并发送一些东西回客户端:

while(true):
    data, addr = sockSer.recvfrom(4096)
    header2=...
    payload2='b'
    sockSer.sendto(header2 + payload2, addr)

现在,我最重要的问题是:问题3) 服务器只向客户端发送了一个数据包,内容是'b'。结果是,我的客户端在循环中实际上收到了两个数据包:第一个数据包是客户端自己发送给服务器的,另一个数据包是客户端从服务器收到的。因此我的输出是'ab'而不是简单的'b'。为什么会这样???

注意:我没有写完整的代码,但我认为我的语法、解析、头部组成等都是正确的。我的代码中是否有明显的问题?如果有必要,我会上传完整的代码。

谢谢

2 个回答

0

问1:我可以成功绑定到本地地址(localhost)并用两个参数调用IOCTL。如果你的客户端也在同一台机器上,确保客户端发送的是“localhost”,否则你的服务器根本收不到数据包。如果你的客户端在另一台机器上,那你的服务器肯定收不到数据包。

问2:发送数据包时不需要用到IOCTL。直接用sendto()就可以了。

问3:你看到两个回复的原因是,内核也在处理回显请求,除了你自己写的用户空间代码之外。

虽然你可以用ICMP来传递任意消息,但正如其他人提到的,这并不是它的设计初衷。你可能会发现,在消息回复中你的数据部分被截断了。例如,当你发送回显请求时,回复通常会包含你发送的所有内容;但是,类型为3代码为3的回复可能不会包含你的数据,只会包含ICMP头的前8个字节。

0

我也遇到过这个问题。我的解决办法是在接收代码里加一个判断,比如说如果我发送的是Ping包,那我只想要ECHO回复(类型0,代码0),我这样写:

if type != 0:
     continue

你也可以这样写:

如果地址等于我的IP地址,就跳过这次循环。

看起来没有什么简单的解决办法。

撰写回答