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---\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上下载.
依旧不知道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 X7.0,Swift2.1,iOS9.0模拟器
c不能使用,用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,添加超时处理,防止程序一直处于阻塞状态.