Swift和C混合Socket編程實現簡單的ping命令&主機發現

Swift和C混合Socket編程實現簡單的ping命令html

這個是用Mac下的Network Utility工具實現ping命令,用Wireshark抓取的ICMP數據包:
git

發送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位)github

8bits類型和8bits代碼字段:一塊兒決定了ICMP報文的類型。常見的有:
  
  類型八、代碼0:回射請求。
  
  類型0、代碼0:回射應答。
  
  類型十一、代碼0:超時。
  
  16bits校驗和字段:包括數據在內的整個ICMP數據包的校驗和,其計算方法和IP頭部校驗和的計算方法是同樣的。編程

對於ICMP回射請求和應答報文來講,接下來是16bits標識符字段:用於標識本ICMP進程。
  
最後是16bits序列號字段:用於判斷回射應答數據報。swift

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---\n%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\n"); 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\n"); 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\n"; } 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\n",size,inet_ntoa(recvAddr.sin_addr),icmpHeader->icmp_seq,ipHeader->ip_ttl,rtt); }else{ back = "Unpacket Error\n"; } } //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\n"); recvPacketNumber--; }else{ gettimeofday(&tvRecv, NULL); //char temp[100] = {0}; unPacket(recvBuffer, back, size); //printf("%s\n",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.\n",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\n",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.\n"
            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地址就保存對應的條目,並用於以後的顯示.app

讀取緩存表是參考的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,添加超時處理,防止程序一直處於阻塞狀態.

相關文章
相關標籤/搜索