0

Pythonでマルチpingコードを実装しようとしていますが、問題は、次のコードでは、定義した両方のソケットが同じ宛先から同じパケットを受信して​​いるように見えることです! なぜそれが起こるのですか?うーん

- - - - - - - - - - アップデート - - - - - - - - - -

受信したパケットは常に「最も速い」パケット [つまり、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

- - - - - - - - - - アップデート - - - - - - - - - -

CODE --> [フォーマットの問題を避けるために、投稿の最後にダウンロード可能なバージョンがあります]

#!/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

これが raw ソケットの仕組みです。

TCP/IP Raw Sockets (Windows)から(強調は鉱山):

受信したデータグラムは、次の条件を満たすすべてのソケットにコピーされます。 SOCK_RAW

  • ソケットが作成されたときにprotocolパラメータで指定された プロトコル番号は、受信したデータグラムの IP ヘッダーのプロトコル番号と一致する必要があります。

  • ソケットにローカル IP アドレスが定義されている場合は、受信したデータグラムの IP ヘッダーで指定されている宛先アドレスに対応している必要があります。アプリケーションは、関数を呼び出してローカル IP アドレスを指定できますbind。ソケットにローカル IP アドレスが指定されていない場合、受信したデータグラムの IP ヘッダーの宛先 IP アドレスに関係なく、データグラムはソケットにコピーされます。

  • ソケットに外部アドレスが定義されている場合は、受信したデータグラムの IP ヘッダーで指定されている送信元アドレスに対応する必要があります。connect アプリケーションは、またはWSAConnect関数を呼び出して外部 IP アドレスを指定できます。ソケットに外部 IP アドレスが指定されていない場合、受信したデータグラムの IP ヘッダーのソース IP アドレスに関係なく、データグラムはソケットにコピーされます。

SOCK_RAW一部のタイプのソケットは、多くの予期しないデータグラムを受信する可能性があることを理解することが重要です。たとえば、PING プログラムは、SOCK_RAWICMP エコー要求を送信し、応答を受信するタイプのソケットを作成する場合があります。アプリケーションが ICMP エコー応答を予期している間、他のすべての ICMP メッセージ (ICMP HOST_UNREACHABLE など) もこのアプリケーションに配信される可能性があります。さらに、SOCK_RAWコンピュータで同時に複数のソケットが開いている場合、開いているすべてのソケットに同じデータグラムが配信される可能性があります。アプリケーションには、対象のデータグラムを認識し、その他すべてを無視するメカニズムが必要です。PING プログラムの場合、このようなメカニズムには、ICMP ヘッダー内の一意の識別子 (アプリケーションのプロセス ID など) について、受信した IP ヘッダーを検査することが含まれる場合があります。

connectそのページは、ソケットを呼び出すと、接続されたアドレスからのみデータグラムを受信するようになることを暗示しているようですがconnect、生のソケットを呼び出すと、(OS X 上で) [Errno 56] Socket is already connected.

そのため、返信を手動で整理する必要があるようです。

于 2013-02-15T18:09:02.880 に答える