python学习之socket应用

环境说明

本文的代码运行环境:
windows 10,python 3.7(代码兼容python 2)

实现的目标:
1、实现nslookup 命令,查询域名对应的ip地址
2、实现ping命令,查询目标ip是否在线

代码实现

nslookup命令实现

import socket
import sys


def nslookup(domain):
    result = socket.getaddrinfo(domain,53,socket.AF_INET,socket.SOCK_DGRAM,17,0)
    #print(result)
    num=len(result)
    #print(num)
    for a in range(num):
        i = result[a][4][0]
        print(i)


if __name__ == '__main__':
    try:
        domain = sys.argv[1]
    except:
        print('Usage: %s www.baidu.com' % sys.argv[0])
        exit()
    nslookup(domain)

ping命令实现(icmp协议数据包模拟

# coding=utf-8
import os
import sys
import socket
import struct
import select

try:
    from time import perf_counter
except:
    from time import clock as perf_counter

ICMP_ECHO_REQUEST = 8

def receive_ping(my_socket, ID, timeout):
    """
    receive the ping from the socket
    """
    start_time = timeout
    while True:
        start_select = perf_counter()
        # select.select(rlist, wlist, xlist[, timeout])
        # wait until ready for read / write / exceptional condition
        # The return value is a triple of lists
        what_ready = select.select([my_socket], [], [], start_time)
        how_long = perf_counter() - start_select
        if what_ready[0] == []: #timeout
            return None
        time_received = perf_counter()
        # socket.recvfrom(bufsize[, flags])
        # The return value is a pair (string, address)
        rec_packet, addr = my_socket.recvfrom(1024)
        icmp_header = rec_packet[20:28]
        ip_type, code, checksum, packet_ID, sequence = struct.unpack("bbHHh", icmp_header)
        if ip_type != 8 and packet_ID == ID: # ip_type should be 0
            byte_in_double = struct.calcsize("d")
            time_sent = struct.unpack("d", rec_packet[28:28 + byte_in_double])[0]
            return time_received - time_sent
        start_time = start_time - how_long
        if start_time <= 0:
            return None


def get_checksum(source):
    """
    return the checksum of source
    the sum of 16-bit binary one's complement
    """
    #print(type(source)) # in py2, str; in py3, bytes
    checksum = 0
    count = int((len(source) // 2) * 2)
    i = 0
    while i < count:
        if sys.version.startswith('3.'):
            temp = source[i + 1] * 256 + source[i] # 256 = 2^8
        else:
            temp = ord(source[i + 1]) * 256 + ord(source[i]) # 256 = 2^8
        checksum += temp
        checksum = checksum & 0xffffffff # 4,294,967,296 (2^32)
        i = i + 2
    if i < len(source):
        if sys.version.startswith('3.'):
            checksum += source[len(source) - 1]
        else:
            checksum += ord(source[len(source) - 1])
        checksum = checksum & 0xffffffff
    # 32-bit to 16-bit
    checksum = (checksum >> 16) + (checksum & 0xffff)
    checksum = checksum + (checksum >> 16)
    answer = ~checksum
    answer = answer & 0xffff
    # why? ans[9:16 1:8]
    answer = answer >> 8 | (answer << 8 & 0xff00)
    return answer



def send_ping(my_socket, ip_addr, ID):
    """
    send ping to the given ip address
    """
    ip = socket.gethostbyname(ip_addr)
    # Header is type (8), code (8), checksum (16), id (16), sequence (16)
    my_checksum = 0
    # Make a dummy heder with a 0 checksum
    # b:signed char, h:short 2, H:unsigned short 2
    header = struct.pack('bbHHh', ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
    # struct.calcsize(fmt)
    # Return the size of the struct corresponding to the given format.
    byte_in_double = struct.calcsize("d") # C type: double, 8
    data = (192 - byte_in_double) * b"P" # any char is OK, any length is OK
    tt = perf_counter()
    data = struct.pack("d", tt) + data
    my_checksum = get_checksum(header + data)
    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1)
    packet = header + data
    my_socket.sendto(packet, (ip, 80)) # it seems that 0~65535 is OK (port?)


def ping_once(ip_addr, timeout):
    """
    return either delay (in second) or none on timeout.
    """
    icmp = socket.getprotobyname('icmp')
    try:
        my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
    except socket.error:
        raise
    # Return the current process id.
    # int: 0xFFFF = -1, unsigned int: 65535
    my_ID = os.getpid() & 0xFFFF
    send_ping(my_socket, ip_addr, my_ID)
    delay = receive_ping(my_socket, my_ID, timeout)
    my_socket.close()
    return delay


def icmp_ping(ip_addr, timeout = 2, count = 1):
    """
    send ping to ip_addr for count times with the given timeout
    """
    for i in range(count):
        print('ping ' + ip_addr)
        try:
            delay = ping_once(ip_addr, timeout)
        except socket.gaierror as e:
            print("failed. (socket error: '%s')" % e[1])
            break
        if delay == None:
            print('failed. (timeout within %s second.)' % timeout)
        else:
            print('get reply in %0.4f ms' % (delay * 1000))



if __name__ == '__main__':
    try:
        ip_addr = sys.argv[1]
    except:
        print('Usage: %s 1.2.3.4' % sys.argv[0])
        exit()
    icmp_ping(ip_addr)

参考文献:
ICMP协议Ping方法的Python实现解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值