0

我正在尝试在 python 中实现多 ping 代码,但问题是在下面的代码中,我定义的两个套接字似乎都从 SAME DESTINATION 接收了 SAME PACKET!为什么会这样?哦

- - - - - - - - - - 更新 - - - - - - - - - -

我注意到接收到的数据包始终是“最快”的数据包[即具有较小 RTT 的数据包]!例如:

dests_list = ['173.194.41.70','213.92.16.101'] -> DEST: 173.194.41.70
dests_list = ['213.92.16.101','173.194.41.70'] -> DEST: 173.194.41.70
dests_list = ['213.92.16.191','95.141.47.7'] -> DEST: 213.92.16.191

和:

RTT_213.92.16.191=20ms  RTT_173.194.41.70=8ms  RTT_95.141.47.7=28ms

- - - - - - - - - - 更新 - - - - - - - - - -

代码 --> [在帖子的末尾有一个可下载的版本以避免格式问题]

#!/usr/bin/python
# -*- coding: utf-8 -*-

from exceptions import Exception
import random
import select
import socket
import struct
import sys
import time

ICMP_ECHO_REQUEST = 8  # Seems to be the same on Solaris.
PACKET_SIZE = 32
IS_WIN = False
my_socket_list = []
dests_list = ['173.194.41.70','213.92.16.101']
N_DESTS = 2
seq_num = 1

#----------------------------------------------------------------------------------------#
#----> CHECKSUM: calculate checksum
#----------------------------------------------------------------------------------------#

def checksum(source_string):
    """
    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
    """

    sum = 0
    countTo = len(source_string) / 2 * 2
    count = 0
    while count < countTo:
        thisVal = ord(source_string[count + 1]) * 256 + ord(source_string[count])
        sum = sum + thisVal
        sum = sum & 0xffffffff  # Necessary?
        count = count + 2

    if countTo < len(source_string):
        sum = sum + ord(source_string[len(source_string) - 1])
        sum = sum & 0xffffffff  # Necessary?

    sum = (sum >> 16) + (sum & 65535)
    sum = sum + (sum >> 16)
    answer = ~sum
    answer = answer & 65535

    # Swap bytes. Bugger me if I know why.

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

    return answer


#----------------------------------------------------------------------------------------#
#----> RECEIVE PINGS: polling on all open sockets
#----------------------------------------------------------------------------------------#

def receive_pings():
    """
    Receive the ping from the socket.
    """

    whatReady = select.select(my_socket_list, [], [], 1000)

        if whatReady[0] != []:
        print whatReady[0]
        for skt in whatReady[0]:

            # time evaluation 
            if IS_WIN:
                timeReceived = time.clock()
            else:
                timeReceived = time.time()

            # get datas
            (recPacket, addr) = skt.recvfrom(PACKET_SIZE + 64)
            icmpHeader = recPacket[20:28]
            (type, code, checksum, packetID, sequence) = struct.unpack('bbHHh', icmpHeader)
            print addr
            if type == 0:
            bytesInDouble = struct.calcsize('d')
            timeSent = struct.unpack('d', recPacket[28:28 + bytesInDouble])[0]
            print "DEST: %s - RTT: %s"%(addr[0],str(timeReceived-timeSent))
           elif type == 3:
            codes = {
                        0: 'Net Unreachable',
                    1: 'Host Unreachable',
                    2: 'Protocol Unreachable',
                3: 'Port Unreachable',
                }
            raise Exception(codes[code])
            break


#----------------------------------------------------------------------------------------#
#----> SEND PING
#----------------------------------------------------------------------------------------#

def send_one_ping(my_socket, dest_addr, ID):

    # Header is type (8bit), code (8bit), checksum (16bit), id (16bit), sequence (16bit)

    my_checksum = 0

    # Make a dummy heder with a 0 checksum.

    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
    bytesInDouble = struct.calcsize('d')
    data = (PACKET_SIZE - len(header) - bytesInDouble) * 'x'

    if IS_WIN:
        start = time.clock()
    else:
        start = time.time()

    data = struct.pack('d', start) + data

    # Calculate the checksum on the data and the dummy header.

    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.

    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1)
    packet = header + data
    print my_socket 
    while packet:
        sent = my_socket.sendto(packet, (dest_addr, 1))  # Don't know about the 1
        packet = packet[sent:]      
        print "PING SENT TO %s"%dest_addr 


#----------------------------------------------------------------------------------------#
#----> DO ONE: socket creation and ping sending
#----------------------------------------------------------------------------------------#

def send_pings():

    i = 1
    for skt in my_socket_list:
        print i
        send_one_ping(skt, dests_list[i-1], i)
        i+=1

    seq_num=+1


#----------------------------------------------------------------------------------------#
#----> SCHEDULE PING: open socket towards a specific destination
#----------------------------------------------------------------------------------------#

def sockets_opening(Ndests):
    try:

        if sys.platform[0:-2] == 'win':
                IS_WIN = True
        else:
                IS_WIN = False

        for i in range(0,Ndests):
            icmp = socket.getprotobyname('icmp')
            my_socket_list.append(socket.socket(socket.AF_INET, socket.SOCK_RAW,icmp))
            print "O2: %s"%my_socket_list[i]

    except socket.error, (errno, msg):
        if errno == 1:      # operation not permitted
            msg = msg + ' - Note that ICMP messages can only be sent from processes running as root.'
                raise socket.error(msg)
            raise   # raise the original error

def sockets_closing():
    try:
        for skt in my_socket_list:
            print "C2: %s"%skt      
            skt.close()
    except socket.error, (errno, msg):
        print "ERR:%d -> %s"%(errno,msg)        
        raise

#----------------------------------------------------------------------------------------#
#----> MAIN
#----------------------------------------------------------------------------------------#

if __name__ == '__main__':
    print
    sockets_opening(N_DESTS)
    send_pings()
    #time.sleep(3)
    receive_pings()
    sockets_closing()

输出-->

CREATED-SOCKETS:[<socket._socketobject object at 0xb7267df4>, <socket._socketobject object at 0xb7267e2c>]

PING SENT TO 173.194.41.70
PING SENT TO 213.92.16.101

READY-SOCKETS:[<socket._socketobject object at 0xb7267df4>, <socket._socketobject object at 0xb7267e2c>]
DEST: 173.194.41.70 - RTT: 0.00797414779663
DEST: 173.194.41.70 - RTT: 0.00811314582825

C2: <socket._socketobject object at 0xb7267df4>
C2: <socket._socketobject object at 0xb7267e2c>

代码链接:PingSKT.py

4

1 回答 1

0

这就是原始套接字的工作方式。

TCP/IP 原始套接字(Windows)(强调我的):

接收到的数据报被复制到所有 SOCK_RAW满足以下条件的套接字中:

  • 创建套接字时在协议参数中指定的协议号 应与接收到的数据报的 IP 头中的协议号相匹配。

  • 如果为套接字定义了本地 IP 地址,它应该对应于接收到的数据报的 IP 头中指定的目标地址。应用程序可以通过调用该bind函数来指定本地 IP 地址。如果没有为套接字指定本地 IP 地址,则将数据报复制到套接字中,而不管接收到的数据报的 IP 标头中的目标 IP 地址。

  • 如果为套接字定义了一个外部地址,它应该对应于接收到的数据报的 IP 头中指定的源地址。应用程序可以通过调用connect orWSAConnect函数来指定外部 IP 地址。如果没有为套接字指定外部 IP 地址,则将数据报复制到套接字中,而不管接收到的数据报的 IP 标头中的源 IP 地址如何。

重要的是要了解某些类型的套接字SOCK_RAW可能会收到许多意外的数据报。例如,一个 PING 程序可以创建一个类型的套接字SOCK_RAW来发送 ICMP 回显请求和接收响应。当应用程序期待 ICMP 回显响应时,所有其他 ICMP 消息(例如 ICMP HOST_UNREACHABLE)也可能会传递到此应用程序。此外,如果SOCK_RAW计算机上同时打开多个套接字,则可能会将相同的数据报传送到所有打开的套接字。应用程序必须有一种机制来识别感兴趣的数据报并忽略所有其他数据报。对于 PING 程序,这种机制可能包括检查接收到的 IP 标头中 ICMP 标头中的唯一标识符(例如,应用程序的进程 ID)。

该页面似乎暗示调用connect套接字将导致它仅从连接的地址接收数据报,但是调用connect原始套接字对我来说(在 OS X 上)使用[Errno 56] Socket is already connected.

所以看起来你必须手动整理回复。

于 2013-02-15T18:09:02.880 回答