zoukankan      html  css  js  c++  java
  • Swift和C混合Socket编程实现简单的ping命令&主机发现

    Swift和C混合Socket编程实现简单的ping命令

    这个是用Mac下的Network Utility工具实现ping命令,用Wireshark抓取的ICMP数据包:

    发送ICMP数据包内容
    接受ICMP数据包内容
     

    一.icmp结构

    要真正了解ping命令实现原理,就要了解ping命令所使用到的TCP/IP协议。
    ICMP(Internet Control Message,网际控制报文协议)是为网关和目标主机而提供的一种差错控制机制,使它们在遇到差错时能把错误报告给报文源发方。ICMP协议是IP层的 一个协议,但是由于差错报告在发送给报文源发方时可能也要经过若干子网,因此牵涉到路由选择等问题,所以ICMP报文需通过IP协议来发送。
    ICMP数据报的数据发送前需要两级封装:首先添加ICMP报头形成ICMP报文,再添加IP报头形成IP数据报

    各种ICMP报文的前32bits都是三个长度固定的字段:type类型字段(8位)、code代码字段(8位)、checksum校验和字段(16位)

    8bits类型和8bits代码字段:一起决定了ICMP报文的类型。常见的有:
      
      类型8、代码0:回射请求。
      
      类型0、代码0:回射应答。
      
      类型11、代码0:超时。
      
      16bits校验和字段:包括数据在内的整个ICMP数据包的校验和,其计算方法和IP头部校验和的计算方法是一样的。

    对于ICMP回射请求和应答报文来说,接下来是16bits标识符字段:用于标识本ICMP进程。
      
    最后是16bits序列号字段:用于判断回射应答数据报。

    ICMP报文包含在IP数据报中,属于IP的一个用户,IP头部就在ICMP报文的前面

    一个ICMP报文包括IP头部(20字节)、ICMP头部(8字节)和ICMP报文数据部分

     

    ICMP报文格式,在Mac(Unix)下结构包含在ip_icmp.h中:
    引入头文件#include //icmp数据包结构

    struct icmp {
        u_char    icmp_type;        /* type of message, see below */
        u_char    icmp_code;        /* type sub code */
        u_short    icmp_cksum;        /* ones complement cksum of struct */
        union {
            u_char ih_pptr;            /* ICMP_PARAMPROB */
            struct in_addr ih_gwaddr;    /* ICMP_REDIRECT */
            struct ih_idseq {
                n_short    icd_id;
                n_short    icd_seq;
            } ih_idseq;
            int ih_void;
    
            /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
            struct ih_pmtu {
                n_short ipm_void;
                n_short ipm_nextmtu;
            } ih_pmtu;
    
            struct ih_rtradv {
                u_char irt_num_addrs;
                u_char irt_wpa;
                u_int16_t irt_lifetime;
            } ih_rtradv;
        } icmp_hun;
    #define    icmp_pptr    icmp_hun.ih_pptr
    #define    icmp_gwaddr    icmp_hun.ih_gwaddr
    #define    icmp_id        icmp_hun.ih_idseq.icd_id
    #define    icmp_seq    icmp_hun.ih_idseq.icd_seq
    #define    icmp_void    icmp_hun.ih_void
    #define    icmp_pmvoid    icmp_hun.ih_pmtu.ipm_void
    #define    icmp_nextmtu    icmp_hun.ih_pmtu.ipm_nextmtu
    #define    icmp_num_addrs    icmp_hun.ih_rtradv.irt_num_addrs
    #define    icmp_wpa    icmp_hun.ih_rtradv.irt_wpa
    #define    icmp_lifetime    icmp_hun.ih_rtradv.irt_lifetime
        union {
            struct id_ts {
                n_time its_otime;
                n_time its_rtime;
                n_time its_ttime;
            } id_ts;
            struct id_ip  {
                struct ip idi_ip;
                /* options and then 64 bits of data */
            } id_ip;
            struct icmp_ra_addr id_radv;
            u_int32_t id_mask;
            char    id_data[1];
        } icmp_dun;
    #define    icmp_otime    icmp_dun.id_ts.its_otime
    #define    icmp_rtime    icmp_dun.id_ts.its_rtime
    #define    icmp_ttime    icmp_dun.id_ts.its_ttime
    #define    icmp_ip        icmp_dun.id_ip.idi_ip
    #define    icmp_radv    icmp_dun.id_radv
    #define    icmp_mask    icmp_dun.id_mask
    #define    icmp_data    icmp_dun.id_data
    };
    IP报文格式,在Mac(Unix)下结构包含在ip.h中:
    引入头文件#include //ip数据包结构
    struct ip {
    #ifdef _IP_VHL
        u_char    ip_vhl;            /* version << 4 | header length >> 2 */
    #else
    #if BYTE_ORDER == LITTLE_ENDIAN
        u_int    ip_hl:4,        /* header length */
            ip_v:4;            /* version */
    #endif
    #if BYTE_ORDER == BIG_ENDIAN
        u_int    ip_v:4,            /* version */
            ip_hl:4;        /* header length */
    #endif
    #endif /* not _IP_VHL */
        u_char    ip_tos;            /* type of service */
        u_short    ip_len;            /* total length */
        u_short    ip_id;            /* identification */
        u_short    ip_off;            /* fragment offset field */
    #define    IP_RF 0x8000            /* reserved fragment flag */
    #define    IP_DF 0x4000            /* dont fragment flag */
    #define    IP_MF 0x2000            /* more fragments flag */
    #define    IP_OFFMASK 0x1fff        /* mask for fragmenting bits */
        u_char    ip_ttl;            /* time to live */
        u_char    ip_p;            /* protocol */
        u_short    ip_sum;            /* checksum */
        struct    in_addr ip_src,ip_dst;    /* source and dest address */
    };

    二.具体实现代码

    //xSocketPing.c


    #include "xSocketPing.h" //statistics void statistics(char* back){ double percent = ((double)sendPacketNumber - (double)recvPacketNumber) / (double)sendPacketNumber * 100; sprintf(back, "---%s ping statistics--- %d packets trasmitted, %d packet received, %0.1f%% packet loss",inet_ntoa(dstAddr.sin_addr),sendPacketNumber,recvPacketNumber,percent); } //check sum unsigned short checkSum(unsigned short *buffer, int size){ unsigned long checkSum = 0; while (size > 1) { checkSum += *buffer++; size -= sizeof(unsigned short);//unsigned short is 2 bytes = 16 bits } //if size is odd number if (size == 1){ checkSum += *(unsigned short *)buffer; } checkSum = (checkSum >> 16) + (checkSum & 0xFFFF); checkSum += (checkSum >> 16); return ~checkSum; } //calculate time difference double timeSubtract(struct timeval *recvTimeStamp, struct timeval *sendTimeStamp){ //calculate seconds long timevalSec = recvTimeStamp->tv_sec - sendTimeStamp->tv_sec; //calculate microsends long timevalUsec = recvTimeStamp->tv_usec - sendTimeStamp->tv_usec; //if microsends less then zero if (timevalUsec < 0) { timevalSec -= 1; timevalUsec = - timevalUsec; } return (timevalSec * 1000.0 + timevalUsec) / 1000.0; } //fill icmp packet and return size of packet int fillPacket(int packetSequence){ int packetSize = 0; struct icmp *icmpHeader = (struct icmp *)sendBuffer; icmpHeader->icmp_type = ICMP_ECHO; icmpHeader->icmp_code = 0; icmpHeader->icmp_cksum = 0; icmpHeader->icmp_id = pid; icmpHeader->icmp_seq = packetSequence; packetSize = dataSize + 8; tvSend = (struct timeval *)icmpHeader->icmp_data; gettimeofday(tvSend, NULL);//get current of time icmpHeader->icmp_cksum = checkSum((unsigned short *)icmpHeader, packetSize); return packetSize; } //send icmp packet to dstAddr int sendPacket(int packetSequence){ int packSize = 0; packSize = fillPacket(packetSequence); if ((sendto(socketfd, sendBuffer, packSize, 0, (struct sockaddr *)&dstAddr, sizeof(dstAddr))) < 0) { printf("Send icmp packet Error "); sendPacketNumber--; recvPacketNumber--; return -1; } return 0; } //setting ip address void settingIP(){ //initialize bzero(&dstAddr,sizeof(dstAddr)); dstAddr.sin_family = AF_INET; dstAddr.sin_addr.s_addr = inet_addr(ipAddr); } //get current process id void getPid(){ pid = getpid(); } //create socket int createSocket(){ //原始套接字SOCK_RAW需要使用root权限,所以改用SOCK_DGRAM if ((socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) < 0) { printf("Create Socket Error "); return -1; } return 0; } //setting socket void settingSocket(int timeout){ int size = 50 * 1024; //setting timeout seconds or you can set it by microseconds struct timeval timeOut; timeOut.tv_sec = timeout; //扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答 setsockopt(socketfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeOut, sizeof(timeOut)); } //destory socket void destorySocket(){ close(socketfd); } //unpacket void unPacket(char* packetBuffer,char* back, long size){ struct ip *ipHeader = NULL; struct icmp *icmpHeader = NULL; double rtt;//往返时间 int ipHeaderLength;//ip header length ipHeader = (struct ip *)packetBuffer; ipHeaderLength = ipHeader->ip_hl<<2;//求ip报头长度,即ip报头的长度标志乘4 icmpHeader = (struct icmp *)(packetBuffer + ipHeaderLength);//越过IP头,point to ICMP header size -= ipHeaderLength; if (size < 8){ back = "Unpacket Error:packet size minmum 8 bytes "; } if ((icmpHeader->icmp_type == ICMP_ECHOREPLY) && (icmpHeader->icmp_id == pid)) { tvSend = (struct timeval *)icmpHeader->icmp_data; gettimeofday(&tvRecv, NULL); //以毫秒为单位计算rtt rtt = timeSubtract(&tvRecv, tvSend); sprintf(back,"%ld bytes from %s: icmp_seq=%u ttl=%d time=%.1f ms ",size,inet_ntoa(recvAddr.sin_addr),icmpHeader->icmp_seq,ipHeader->ip_ttl,rtt); }else{ back = "Unpacket Error "; } } //receive packet void receivePacket(char* back){ //claculate packet size int packetSize = sizeof(recvAddr); long size; if ((size = recvfrom(socketfd, recvBuffer, sizeof(recvBuffer), 0, (struct sockaddr *)&recvAddr, (socklen_t *)&packetSize)) < 0) { sprintf(back,"Receive timeout "); recvPacketNumber--; }else{ gettimeofday(&tvRecv, NULL); //char temp[100] = {0}; unPacket(recvBuffer, back, size); //printf("%s ",temp); } } void ping(char *ipAddress, int number, int timeout){ int packetnumber = 0; ipAddr = ipAddress; sendPacketNumber = number; recvPacketNumber = number; settingIP(); getPid(); if (createSocket() != -1){ settingSocket(timeout); printf("PING %s: %d bytes of data. ",ipAddress,dataSize); while(packetnumber < number){ if (sendPacket(packetnumber) != -1){ char back[100] = {0}; receivePacket(back); printf("%s",back); } sleep(1); packetnumber++; } char back[100] = {0}; statistics(back); printf("%s ",back); destorySocket(); } }


    //xSocketPing.h

    #ifndef xSocketPing_h
    #define xSocketPing_h #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h>//基本系统数据类型 #include <sys/socket.h> #include <netinet/in.h> //get current of time #include <sys/time.h> #include <arpa/inet.h>//inet_ntoa将一个IP转换成一个互联网标准点分格式的字符串 #include <unistd.h>//close(int) #include <netdb.h>//定义了与网络有关的结构、变量类型、宏、函数等 //ip packet structure #include <netinet/ip.h> //icmp packet structure #include <netinet/ip_icmp.h> //time to live int ttl = 64; //icmp data size ,icmp header 8bytes,data size 56bytes,the maximum of packet size 64bytes int dataSize = 56; //packet number int sendPacketNumber; int recvPacketNumber; //ip address char * ipAddr; //send packet of time struct timeval *tvSend; //receive packet of time struct timeval tvRecv; //Socket address, internet style. //the destination address struct sockaddr_in dstAddr; //the receive address struct sockaddr_in recvAddr; //send icmp buffer char sendBuffer[1024] = {0}; //receive icmp replay buffer char recvBuffer[1024] = {0}; //the current process of id int pid; //socket int socketfd = 0; void statistics(char* back); unsigned short checkSum(unsigned short *buffer, int size); double timeSubtract(struct timeval *recvTimeStamp, struct timeval *sendTimeStamp); int fillPacket(int packetSequence); int sendPacket(int packetSequence); void settingIP(); void getPid(); int createSocket(); void settingSocket(int timeout); void destorySocket(); void unPacket(char* packetBuffer,char* back, long size); void receivePacket(char* back); void ping(char *ipAddress, int number, int timeout); #endif /* xSocketPing_h */


    //xSocketPing.swift
    
    import Foundation
    
    public class xSocketPing{
        
        private var ipAddress:String
        //default packet number 3 or you can setting it by yourself
        private var packetNumber:Int = 3
        private var timeout:Int = 1
        //refresh UI
        weak var delegate:refreshTextDelegate?
    
        init(ipAddress:String, delegate:refreshTextDelegate){
            self.ipAddress = ipAddress
            self.delegate = delegate
        }
        convenience init(ipAddress:String, packetNumber:Int, delegate:refreshTextDelegate){
            self.init(ipAddress: ipAddress,delegate: delegate)
            self.packetNumber = packetNumber
        }
        convenience init(ipAddress:String, packetNumber:Int, timeout:Int, delegate:refreshTextDelegate){
            self.init(ipAddress: ipAddress,packetNumber: packetNumber,delegate: delegate)
            self.timeout = timeout
        }
        
        public func xPing(){
            let tempIpAddress:UnsafeMutablePointer = UnsafeMutablePointer<Int8>((ipAddress as NSString).UTF8String)
            ipAddr = tempIpAddress
            sendPacketNumber = Int32(packetNumber)
            recvPacketNumber = Int32(packetNumber)
            
            settingIP()
            getPid()
            if createSocket() != -1 {
                settingSocket(Int32(timeout))
                let message:String = "PING (ipAddress): 56 bytes of data.
    "
                refresh(message, speed: 0.0)
                //将String转换为UnsafeMutablePointer<CChar>,相当于char tempmessage[100]
                let tempmessage:UnsafeMutablePointer = UnsafeMutablePointer<CChar>.alloc(100)
                var packetsequence:Int = 0
                var speed:Float = 0.0
                
                while packetsequence < packetNumber {
                    if sendPacket(Int32(packetsequence)) != -1 {
                        receivePacket(tempmessage)
                        //Calculate percentage
                        speed = (Float(packetNumber) - (Float(packetNumber) - Float(packetsequence))) / Float(packetNumber)
                        //将UnsafeMutablePointer<CChar>转换为String
                        refresh(String.fromCString(tempmessage)!, speed: speed)
                    }
                    sleep(1);
                    packetsequence++
                }
                statistics(tempmessage)
                refresh(String.fromCString(tempmessage)!, speed: 1)
                destorySocket()
            }
        }
        
        func refresh(text:String, speed:Float){
            delegate?.refresh(text, speed: speed)
        }
        deinit{
            print("xSocketPing destory")
        }
    }

    //ViewController.swift
    
    import UIKit
    
    protocol refreshTextDelegate:NSObjectProtocol{
        func refresh(text:String, speed:Float)
    }
    
    class ViewController: UIViewController,refreshTextDelegate {
    
        @IBOutlet weak var xprogress: UIProgressView!
        @IBOutlet weak var xip: UITextField!
        @IBOutlet weak var xnumber: UITextField!
        @IBOutlet weak var xtext: UITextView!
        
        var number:Int = 3
        var ip:String = "192.168.1.1"
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            xtext.text = ""
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
        
        @IBAction func xNumberChange(sender: UIStepper) {
            xnumber.text = "(Int(sender.value))"
            self.number = Int(sender.value)
        }
        
        @IBAction func beginPing(sender: UIButton) {
            sender.enabled = false
            xip.enabled = false
            xnumber.enabled = false
            
            xprogress.progress = 0
            xtext.text = ""
            
            let ip = xip.text
            if ip == ""{
                let alert = UIAlertView(title: "Error", message: "No IP Address", delegate: self, cancelButtonTitle: "OK")
                alert.show()
            }else{
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
                    let ping:xSocketPing = xSocketPing(ipAddress: "(ip!)", packetNumber: self.number, delegate: self)
                    ping.xPing()
                })
            }
            xnumber.enabled = true
            xip.enabled = true
            sender.enabled = true
        }
        
        
        func refresh(text:String, speed:Float) {
            dispatch_async(dispatch_get_main_queue(), {
                //更新进度条
                self.xprogress.progress = speed
                //更新UITextView,追加内容并滚动到最下面
                self.xtext.text.appendContentsOf(text)
                self.xtext.scrollRangeToVisible(NSMakeRange(self.xtext.text.characters.count, 0))
            })
        }
        override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
            self.view.endEditing(true)
        }
    }

    运行结果图:


     

     

    使用Swift进行主机发现和MAC地址解析

    贴出的都是核心代码,代码都有注释,全部代码可以在Github上下载.



    主机发现基本原理:根据Stackoverflow上面的解释,应该是读取arp缓存表来获取MAC地址.(依旧不知道Fing是弄的,根据抓包来看是发送arp数据包来实现主机扫描的,但是又不能使用原始套接字,又不能获取本机MAC地址,没办法,只能继续研究下去了.如果有哪位大神知道,希望告知,谢谢.)
    至于IP地址是使用getifaddrs函数来获取本机的所有网卡的信息:具体看Get all Ethernet information in Swift  (返回的信息中包含了IP地址,子网掩码,广播地址.)
    然后获取WiFi的SSID和BSSID:Get WIFI SSID and BSSID     
    获取网卡信息和WiFi的信息我放到了一个类中方便调用.
    class GetInterfaceInformation{
        
        /**
        get ethernet information about name,address,netmask,broadcast
        
        - returns: return Dictionary contain Ethernet name,ip address,netmask,broadcast
        */
        static func getInterfaceInformation() -> [String:[String]] {
            var information = [String:[String]]()
            
            var ifaddr:UnsafeMutablePointer<ifaddrs> = nil
            //retrieve the current interface -- return 0 on success
            if getifaddrs(&ifaddr) == 0 {
                var interface = ifaddr
                //loop through linked list of interface
                while interface != nil {
                    if interface.memory.ifa_addr.memory.sa_family == UInt8(AF_INET) {//ipv4
                        let interfaceName = String.fromCString(interface.memory.ifa_name)
                        let interfaceAddress = String.fromCString(inet_ntoa(UnsafeMutablePointer<sockaddr_in>(interface.memory.ifa_addr).memory.sin_addr))
                        let interfaceNetmask = String.fromCString(inet_ntoa(UnsafeMutablePointer<sockaddr_in>(interface.memory.ifa_netmask).memory.sin_addr))
                        //ifa_dstaddr /* P2P interface destination */
                        //The ifa_dstaddr field references the destination address on a P2P inter-face, interface,
                        //face, if one exists, otherwise it contains the broadcast address.
                        let interfaceBroadcast = String.fromCString(inet_ntoa(UnsafeMutablePointer<sockaddr_in>(interface.memory.ifa_dstaddr).memory.sin_addr))
                        
                        if let name = interfaceName {
                            information[name] = [interfaceAddress!,interfaceNetmask!,interfaceBroadcast!]
                        }
                    }
                    interface = interface.memory.ifa_next
                }
                freeifaddrs(ifaddr)
            }
            return information
        }
        
        /**
        get WI-FI information
        
        - returns: return Dictionary contain ssid and bssid
        */
        static func getWIFIInformation() -> [String:String] {
            var informationDictionary = [String:String]()
            if #available(iOS 9.0, *) {
                let information = NEHotspotHelper.supportedNetworkInterfaces()
                informationDictionary["SSID"] = information[0].SSID!
                informationDictionary["BSSID"] = information[0].BSSID!
                return informationDictionary
            } else {
                // Fallback on earlier versions
                let informationArray:NSArray? = CNCopySupportedInterfaces()
                if let information = informationArray {
                    let dict:NSDictionary? = CNCopyCurrentNetworkInfo(information[0] as! CFStringRef)
                    if let temp = dict {
                        informationDictionary["SSID"] = String(temp["SSID"]!)
                        informationDictionary["BSSID"] = String(temp["BSSID"]!)
                        return informationDictionary
                    }
                }
            }
            return informationDictionary
        }
    }
    之后根据IP地址和子网掩码,计算出IP地址池
    /// Calculate Network Information about all the possible IP address.....
    public class Network{
        
        private var mask:in_addr_t
        private var ip:in_addr_t
        /**
        if ip or netmask is error,return nil
        
        - parameter ipAddress: ip
        - parameter netmask:   mask
        
        - returns: return nil if error
        */
        init?(ipAddress:String, netmask:String){
            ip = inet_addr(ipAddress)
            mask = inet_addr(netmask)
            
            guard ip != INADDR_NONE else { return nil }
            guard mask != INADDR_NONE else { return nil }
        }
        
        /// calculate host number in lan(contain 0 and 255)
        public var HostNumber:Int { return Int(~mask.bigEndian) + 1 }
        
        /// calculate first ip address like 192.168.1.0
        public var firstIPAddress:String { return String.fromCString(inet_ntoa(in_addr(s_addr: ip & mask)))! }
        
        /// calculate all host ip address like 192.168.1.1~192.168.1.254
        public var allHostIP:[String] {
            var temp = [String]()
            for i in 0..<HostNumber - 2 {
                let tempip = CFSwapInt32(CFSwapInt32(inet_addr(firstIPAddress)) + UInt32(i + 1))
                temp.append(String.fromCString(inet_ntoa(in_addr(s_addr: tempip)))!)
            }
            return temp
        }
    }

    之后循环IP地址,获取到MAC地址就保存对应的条目,并用于之后的显示.

    读取缓存表是参考的stackoverflow上面的代码.

    //
    //  GetMAC.h
    //  NetManager
    //
    //  Created by XWJACK on 3/12/16.
    //  Copyright © 2016 XWJACK. All rights reserved.
    //
    
    #ifndef GetMAC_h
    #define GetMAC_h
    
    #include <stdio.h>
    #include <stdlib.h>
    //GetMAC
    #include <sys/sysctl.h>
    
    #include <netinet/if_ether.h>
    #include <net/if_arp.h>
    #include <net/route.h>
    #include <net/if_dl.h>
    #include <sys/socket.h>
    
    #include <err.h>
    #include <errno.h>
    
    int ctoMACAddress(in_addr_t addr, char *back);
    
    #endif /* GetMAC_h */
    Header

    头文件的设置:如果发现找不到对应的头文件,需要将Mac OS中的头文件包含到

    如果只是真机调试,就复制到对应的目录.

    例如:route.h文件需要从Mac OS对应目录复制到:

    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/net/route.h

    #include "GetMAC.h"
    
    /**
     *  This function was finded in stackoverflow.
     *  How do I query the ARP table on iPhone?:http://stackoverflow.com/questions/2258172/how-do-i-query-the-arp-table-on-iphone
     *  Getting ARP table on iPhone/iPad:http://stackoverflow.com/questions/10395041/getting-arp-table-on-iphone-ipad
     *  I samplified this code and fixed some memory leak.
     *
     *
     *  @param addr IP Address
     *  @param back MAC Address
     *
     *  @return if error return -1 ,else return 0
     */
    int ctoMACAddress(in_addr_t addr, char *back) {
        char *buf,*next;
        size_t needed;
        
        struct rt_msghdr *rtm;
        struct sockaddr_inarp *sin;
        struct sockaddr_dl *sdl;
        
        int mib[6] = {
            CTL_NET,//Top-level identifiers  network
            PF_ROUTE,//Protocol families, same as address families for now.
            0,
            AF_INET,//internetwork: UDP, TCP
            NET_RT_FLAGS,//PF_ROUTE - Routing table
            RTF_LLINFO//generated by link layer
        };
        
        if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
            printf("route-sysctl-estimate");
            return -1;
        }
        if ((buf = (char *)malloc(needed)) == NULL) {
            printf("malloc");
            return -1;
        }
        if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
            printf("retrieval of routing table");
            free(buf);
            return -1;
        }
        
        for (next = buf; next < buf + needed; next += rtm->rtm_msglen) {
            
            rtm = (struct rt_msghdr *)next;
            sin = (struct sockaddr_inarp *)(rtm + 1);
            sdl = (struct sockaddr_dl *)(sin + 1);
            
            if (addr != sin->sin_addr.s_addr || sdl->sdl_alen < 6) { continue; }
            
            u_char *cp = (u_char*)LLADDR(sdl);
            
            //ret = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]];
            sprintf(back, "%02X:%02X:%02X:%02X:%02X:%02X", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
            break;
        }
        if (next >= buf + needed) {
            free(buf);
            return -1;
        }
        
        free(buf);
        return 0;
    }

    至于MAC地址和主机名的获取是扩展String方法实现的,这样很简单:

    // MARK: - It's very convenience to change IP to MACAddress or HostName
    extension String {
        public var toMACAddress:String? {
            let back:UnsafeMutablePointer = UnsafeMutablePointer<Int8>((self as NSString).UTF8String)
            if ctoMACAddress(inet_addr(self), back) == -1 { return nil }
            return String.fromCString(back)
        }
        public var toHostName:String? {
            let back:UnsafeMutablePointer = UnsafeMutablePointer<Int8>((self as NSString).UTF8String)
            var ip = in_addr()
            guard inet_aton(back, &ip) == 1 else { return nil }
            let hp = gethostbyaddr(&ip, UInt32(sizeofValue(ip)), AF_INET)
            if hp != nil { return String.fromCString(hp.memory.h_name) }
            return nil
        }
    }

    具体的扫描类:进程和线程的概念还是没有熟悉....还需要多看.那个败笔等我学会了就会更新.

    /// Globe varible to store all interface information: ip,mac,name,picture sort by ip.
    var list = [String:[String]]()
    
    public class Scanner {
        public static func refresh() {
            
            /// get local interface ip,netmask,broadcast
            let allInterfaceInformation = GetInterfaceInformation.getInterfaceInformation()
            
            if allInterfaceInformation["en0"] == nil {
                let alert = UIAlertView(title: "No Network", message: "You have not connected to Wi-Fi", delegate: nil, cancelButtonTitle: "OK")
                alert.show()
                return
            }
            let wifi = GetInterfaceInformation.getWIFIInformation()
            
            /// Calculate all ip address
            if let network = Network(ipAddress: allInterfaceInformation["en0"]![0], netmask: allInterfaceInformation["en0"]![1]) {
                let allhostip = network.allHostIP
                
                //let group = dispatch_group_create()
                dispatch_sync(dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL), {
                    for i in allhostip {
                        dispatch_async(dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT),  {
                            let ping = xSocketPing(ipAddress: i, packetNumber: 1, timeout: 10, delegate: nil)
                            ping.xPing()
                        })
                    }
                //dispatch_group_notify(group, dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL), {
                    NSThread.sleepForTimeInterval(5)//这是个败笔
                    
                    for i in allhostip {
                        //if ip is myslef, jump.
                        if i == allInterfaceInformation["en0"]![0] { list[i] = ["xx:xx:xx:xx:xx:xx", UIDevice.currentDevice().name, "Local.png"]; continue }
                        
                        if let MACAddress = i.toMACAddress {
                            //if mac is route
                            
                            if wifi["BSSID"]! == MACAddress {
                                list[i] = [MACAddress, wifi["SSID"]!, "Wifi.png"]
                                continue
                            }
                            //others
                            if let hostname = i.toHostName { list[i] = [MACAddress, hostname, "User.png"]; continue }
                            list[i] = [MACAddress, "unknow", "User.png"]
                        }
                        //print(list)
                    }
                })
                //print(allhostip)
            }else {
                let alert = UIAlertView(title: "Error", message: "Calculate Error", delegate: nil, cancelButtonTitle: "OK")
                alert.show()
                return
            }
            print(list)
        }
    }

    运行环境OS X10.10.5,Xcode7.0,Swift2.1,iOS9.0模拟器

    如果任然有疑惑可以评论.新浪博客已经不更新了..但是博客园还是会继续更新下去.
    如果发现任何错误也可以联系我,谢谢


    GitHub项目地址:NetManager


    本程序还有很多不足之处,后续会持续更新,并更新GitHub项目,需要改进的,请在博客上留言
    抓包的图片是不更新的,所以会和运行结果图不一样.

    更新于2016-3-13
    新增主机发现功能,mac地址解析功能,本机mac不能使用,用xx:xx:xx:xx:xx:xx代替,获得WiFi的SSID和BSSID.
    程序依旧有许多不足的地方,如果有大神,希望帮我改改.
    - 没有运行循环,用的是超时.
    - 有时候会出现连续发送icmp失败的情况.
    - 再次刷新的时候会出现bogon变成unknow的情况.
    - 主机发现是根据stackoverflow上面的代码改编的.应该是读取arp缓存表,但是前提是发送icmp来刷新缓存表.
    - 发送icmp数据包是多线程异步并列的形式发送.线程这里没有处理好,当学会了之后再更新.
    - 那个进度条还没有添加,给人感觉是卡死的,因为设置的是同步的.


    更新于2016-1-13
    修复bug,优化部分显示,更好,更安全的处理指针类型.


    更新于2016-1-12
    新增功能:优化显示,将返回的结果显示到模拟器中.发送数据包在额外的线程中进行,不会阻塞主线程.由于程序使用的是Swift和C语言混合编程,用到了指针,交互的时候容易crash.后续会继续修复bug.


    更新2016-1-7
    新增功能:优化显示,将返回结果集中到主函数中,方便调用,修复部分bug,添加超时处理,防止程序一直处于阻塞状态.

  • 相关阅读:
    MIT自然语言处理第三讲:概率语言模型(第四部分)
    MIT自然语言处理第二讲:单词计数(第三部分)
    MIT自然语言处理第五讲:最大熵和对数线性模型(第二部分)
    MIT自然语言处理第二讲:单词计数(第四部分)
    MIT自然语言处理第三讲:概率语言模型(第一部分)
    MIT自然语言处理第二讲:单词计数(第二部分)
    MIT自然语言处理第五讲:最大熵和对数线性模型(第一部分)
    MIT自然语言处理第四讲:标注(第二部分)
    MIT自然语言处理第二讲:单词计数(第一部分)
    MIT自然语言处理第一讲:简介和概述(第二部分)
  • 原文地址:https://www.cnblogs.com/xwjack1554239786/p/5131787.html
Copyright © 2011-2022 走看看