Swift 为 iOS 项目构建 IP 4 地址 class

Swift Building IP 4 address class for a iOS project

我试图为 IP 地址创建自己的 class。我想将其用于 iOS 项目。我试图弄清楚是否要将代码放在 0 到 255 之间。分隔符无法正常工作。我希望它保存一个像 192.168.0.1 这样的 IP 地址,如果我输入 400.168.0.1 它应该 return 一个错误或者说请输入一个真实的 IP 地址作为输出之一,这是错误的。我试图创建一个 class,当你输入一个 ip 地址时,它应该保存在 class 本身中。我想稍后制作一个 iOS 应用程序,这将帮助人们了解如何为 cisco 路由器和交换机分配 IP 地址。基本上想制作一个应用程序来帮助使用 CIDR ip 地址符号的人,比如我在网上找到的这个网页程序,但如果没有互联网连接我几乎无法使用。 http://jodies.de/ipcalc 如果我可以编写一个移动应用程序来执行此网站的操作,那将非常适合我在网络方面的工作。

import UIKit

class IP4 {
    var ipA: Int?
    var ipB: Int?
    var ipC: Int?
    var ipD: Int?

    func fullIP() -> Int {
        var parts: [Int] = []

        if let ipA = self.ipA {
            parts += [ipA]
        }

        if let ipB = self.ipB {
            parts += [ipB]
        }

        if let ipC = self.ipC {
            parts += [ipC]
        }

        if let ipD = self.ipD {
            parts += [ipD]
        }
        return parts.joined(separator: ".")
    }
}

let ipaddress = IP4()
ipaddress.ipA = 223
ipaddress.ipB = 9
ipaddress.ipC = 50
ipaddress.ipD = 60

这是一个示例,可以在Playground上进行测试。 (在 Xcode 9.4.1 上测试。)

import Foundation

struct IPv4: CustomStringConvertible, Equatable, Hashable {

    enum Errors: Error {
        case invalidFormat
        case octetOutOfRange
    }

    var ipA: UInt8
    var ipB: UInt8
    var ipC: UInt8
    var ipD: UInt8

    init() {
        ipA = 0
        ipB = 0
        ipC = 0
        ipD = 0
    }

    init(_ ipA: UInt8, _ ipB: UInt8, _ ipC: UInt8, _ ipD: UInt8) {
        self.ipA = ipA
        self.ipB = ipB
        self.ipC = ipC
        self.ipD = ipD
    }

    private static let parsingRegex = try! NSRegularExpression(pattern: "^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$")
    init(_ ipString: String) throws {
        guard let match = IPv4.parsingRegex.firstMatch(in: ipString, range: NSRange(0..<ipString.utf16.count)) else {
            throw Errors.invalidFormat
        }
        let strA = ipString[Range(match.range(at: 1), in: ipString)!]
        let strB = ipString[Range(match.range(at: 2), in: ipString)!]
        let strC = ipString[Range(match.range(at: 3), in: ipString)!]
        let strD = ipString[Range(match.range(at: 4), in: ipString)!]
        guard
            let ipA = UInt8(strA),
            let ipB = UInt8(strB),
            let ipC = UInt8(strC),
            let ipD = UInt8(strD)
        else {throw Errors.octetOutOfRange}
        self.ipA = ipA
        self.ipB = ipB
        self.ipC = ipC
        self.ipD = ipD
    }

    var description: String {
        return "\(ipA).\(ipB).\(ipC).\(ipD)"
    }

    var networkBytes: Data {
        return Data(bytes: [ipA, ipB, ipC, ipD])
    }

    var hostWord: UInt32 {
        return UInt32(ipA) << 24 + UInt32(ipB) << 16 + UInt32(ipC) << 8 + UInt32(ipD)
    }
}

let ip = IPv4(223, 9, 50, 60)
print(ip) //->223.9.50.60

do {
    let ip = try IPv4("400.168.0.1")
    print(ip)
} catch {
    print(error) //->octetOutOfRange
}
  • 更好地使用 struct 而不是 class。因为它的相等性应该由它的内容来判断,而不是它在堆中的地址。

  • IPv4 地址由 4 个八位字节组成。你最好使用UInt8,非可选。任何部分都不能为nil。

  • 没有可以容纳 3 位小数的数字类型。如果你想生成像 192.168.0.1 这样的符号,它需要是 String.

我准备了3种输出。仔细想想你想要哪一个。

同时查找我的代码的哪一部分正在实施 如果我输入 400.168.0.1 它应该 return 一个错误.


我向您展示了我的 struct 的一些扩展,这可能有助于实现链接站点中显示的类似功能。

要进行二进制表示:

extension UInt8 {
    var fixedBinaryDescription: String {
        let binStr = String(self, radix: 2)
        return String(repeating: "0", count: 8-binStr.count) + binStr
    }
}

extension IPv4 {
    var binaryDescription: String {
        return "\(ipA.fixedBinaryDescription).\(ipB.fixedBinaryDescription).\(ipC.fixedBinaryDescription).\(ipD.fixedBinaryDescription)"
    }
}
print(ip.binaryDescription) //->11011111.00001001.00110010.00111100

要使用遮罩:

extension IPv4 {
    init(maskOfLength len: Int) {
        let highBits: [UInt8] = [
            0b10000000,
            0b11000000,
            0b11100000,
            0b11110000,
            0b11111000,
            0b11111100,
            0b11111110,
            0b11111111,
        ]
        switch len {
        case 0:
            self = IPv4(0, 0, 0, 0)
        case 1...8:
            self = IPv4(highBits[len-1], 0, 0, 0)
        case 9...16:
            self = IPv4(0b11111111, highBits[len-9], 0, 0)
        case 17...24:
            self = IPv4(0b11111111, 0b11111111, highBits[len-17], 0)
        case 25...32:
            self = IPv4(0b11111111, 0b11111111, 0b11111111, highBits[len-25])
        default:
            fatalError()
        }
    }

    func masked(by mask: IPv4) -> IPv4 {
        return IPv4(self.ipA & mask.ipA, self.ipB & mask.ipB, self.ipC & mask.ipC, self.ipD & mask.ipD)
    }
}

let mask = IPv4(maskOfLength: 24)
print(mask.binaryDescription) //->11111111.11111111.11111111.00000000
print(ip.masked(by: mask).binaryDescription) //->11011111.00001001.00110010.00000000

获取 IP v4 地址 class 的扩展。

enum IPv4AddressClass {
    case a
    case b
    case c
    case d
    case e
}
extension IPv4 {
    var addressClass: IPv4AddressClass {
        if ipA & 0b1_0000000 == 0b0_0000000 {
            return .a
        } else if ipA & 0b11_000000 == 0b10_000000 {
            return .b
        } else if ipA & 0b111_00000 == 0b110_00000 {
            return .c
        } else if ipA & 0b1111_0000 == 0b1110_0000 {
            return .d
        } else {
            return .e
        }
    }
}
print(ip.addressClass) //->c