Delphi中IP地址字符串例程?

4
我正在寻找一种在Delphi中验证和操作IP地址的方法。一些它应该能够做到的事情是:
  • 验证字符串是否为有效的IP地址
  • 验证字符串是否为有效的子网掩码
  • 验证IP地址是否在给定的子网内
  • 一些类型(记录或字符串等),用于存储IP地址
  • 基本的IP地址类型转换,例如StringArray[0..3] of Byte
  • 任何其他可以使IP操作更容易的IP地址例程
基本原因是我想看看这些东西是否已经存在,然后再决定是否需要重新开发它们。

1
只需要选择一个库。Synapse、ICS、Indy等。 - Warren P
1
Indy有一些类方法和全局函数可以用于这种情况。因此,即使您没有使用它们的组件进行连接,请查看该库,特别是IdGlobal单元。 - Marcus Adams
@DavidHeffernan 我并没有说我不想重新造轮子,那将是一次很好的经历。在我尝试自己制作之前,我宁愿有一些好的参考资料告诉我如何做。WinSock看起来有一些工具,但它也有许多我不需要的东西,而且它还需要一些较低级别的编码。我会尝试使用Synapse。 - Jerry Dodge
库就是这样。你只使用你需要的部分。你也不会使用 Synapse 的全部。顺便说一下,Delphi 自带 Indy。 - David Heffernan
3
参见:IPv6地址的算术运算 - NGLN
显示剩余5条评论
2个回答

10
我曾经写过一个IPv4和IPv6转换单元,包括两种类型的IP地址的自定义变体类型。这个答案展示了它的一些功能示例。最初,它是设计用来在一些滑块控件上以比例显示各种类型的值1)。当时的要求是默认现有库不足,但我同意这里的评论,你可能会受益于Indy(10!)或类似的东西。

使用该单元从一些代码片段回答您的问题列表:

  • Q4: Storage type for IP types:

      const
        IPv4BitSize = SizeOf(Byte) * 4 * 8;
        IPv6BitSize = SizeOf(Word) * 8 * 8;
    
      type
        T4 = 0..3;
        T8 = 0..7;
        TIPv4ByteArray = array[T4] of Byte;
        TIPv6WordArray = array[T8] of Word;
    
        TIPv4 = packed record
          case Integer of
            0: (D, C, B, A: Byte);
            1: (Groups: TIPv4ByteArray);
            2: (Value: Cardinal);
        end;
    
        TIPv6 = packed record
          case Integer of
            0: (H, G, F, E, D, C, B, A: Word);
            1: (Groups: TIPv6WordArray);
        end;
    
  • Q5: Conversion of IP address strings to these record or array types:

      function StrToIPv4(const S: String): TIPv4;
      var
        SIP: String;
        Start: Integer;
        I: T4;
        Index: Integer;
        Count: Integer;
        SGroup: String;
        G: Integer;
      begin
        SIP := S + '.';
        Start := 1;
        for I := High(T4) downto Low(T4) do
        begin
          Index := PosEx('.', SIP, Start);
          if Index = 0 then
            IPv4ErrorFmt(SInvalidIPv4Value, S);
          Count := Index - Start + 1;
          SGroup := Copy(SIP, Start, Count - 1);
          if TryStrToInt(SGroup, G) and (G >= Low(Word)) and (G <= High(Word)) then
              Result.Groups[I] := G
            else
              Result.Groups[I] := 0;
          Inc(Start, Count);
        end;
      end;
    
      function StrToIPv6(const S: String): TIPv6;
      { Valid examples for S:
        2001:0db8:85a3:0000:0000:8a2e:0370:7334
        2001:db8:85a3:0:0:8a2e:370:7334
        2001:db8:85a3::8a2e:370:7334
        ::8a2e:370:7334
        2001:db8:85a3::
        ::1
        ::
        ::ffff:c000:280
        ::ffff:192.0.2.128 }
      var
        ZeroPos: Integer;
        DotPos: Integer;
        SIP: String;
        Start: Integer;
        Index: Integer;
        Count: Integer;
        SGroup: String;
        G: Integer;
    
        procedure NormalNotation;
        var
          I: T8;
        begin
          SIP := S + ':';
          Start := 1;
          for I := High(T8) downto Low(T8) do
          begin
            Index := PosEx(':', SIP, Start);
            if Index = 0 then
              IPv6ErrorFmt(SInvalidIPv6Value, S);
            Count := Index - Start + 1;
            SGroup := '$' + Copy(SIP, Start, Count - 1);
            if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then
              IPv6ErrorFmt(SInvalidIPv6Value, S);
            Result.Groups[I] := G;
            Inc(Start, Count);
          end;
        end;
    
        procedure CompressedNotation;
        var
          I: T8;
          A: array of Word;
        begin
          SIP := S + ':';
          Start := 1;
          I := High(T8);
          while Start < ZeroPos do
          begin
            Index := PosEx(':', SIP, Start);
            if Index = 0 then
              IPv6ErrorFmt(SInvalidIPv6Value, S);
            Count := Index - Start + 1;
            SGroup := '$' + Copy(SIP, Start, Count - 1);
            if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then
              IPv6ErrorFmt(SInvalidIPv6Value, S);
            Result.Groups[I] := G;
            Inc(Start, Count);
            Dec(I);
          end;
          FillChar(Result.H, (I + 1) * SizeOf(Word), 0);
          if ZeroPos < (Length(S) - 1) then
          begin
            SetLength(A, I + 1);
            Start := ZeroPos + 2;
            repeat
              Index := PosEx(':', SIP, Start);
              if Index > 0 then
              begin
                Count := Index - Start + 1;
                SGroup := '$' + Copy(SIP, Start, Count - 1);
                if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then
                  IPv6ErrorFmt(SInvalidIPv6Value, S);
                A[I] := G;
                Inc(Start, Count);
                Dec(I);
              end;
            until Index = 0;
            Inc(I);
            Count := Length(A) - I;
            Move(A[I], Result.H, Count * SizeOf(Word));
          end;
        end;
    
        procedure DottedQuadNotation;
        var
          I: T4;
        begin
          if UpperCase(Copy(S, ZeroPos + 2, 4)) <> 'FFFF' then
            IPv6ErrorFmt(SInvalidIPv6Value, S);
          FillChar(Result.E, 5 * SizeOf(Word), 0);
          Result.F := $FFFF;
          SIP := S + '.';
          Start := ZeroPos + 7;
          for I := Low(T4) to High(T4) do
          begin
            Index := PosEx('.', SIP, Start);
            if Index = 0 then
              IPv6ErrorFmt(SInvalidIPv6Value, S);
            Count := Index - Start + 1;
            SGroup := Copy(SIP, Start, Count - 1);
            if not TryStrToInt(SGroup, G) or (G > High(Byte)) or (G < 0) then
              IPv6ErrorFmt(SInvalidIPv6Value, S);
            case I of
              0: Result.G := G shl 8;
              1: Inc(Result.G, G);
              2: Result.H := G shl 8;
              3: Inc(Result.H, G);
            end;
            Inc(Start, Count);
          end;
        end;
    
      begin
        ZeroPos := Pos('::', S);
        if ZeroPos = 0 then
          NormalNotation
        else
        begin
          DotPos := Pos('.', S);
          if DotPos = 0 then
            CompressedNotation
          else
            DottedQuadNotation;
        end;
      end;
    

针对 Q1 至 Q3,您需要自行推导一些例程,但这应该不会有任何问题。

1) 对于那些感兴趣的人,它是 这个滑块控件这个主题 作为本单元的启动。


谢谢!我还注意到你喜欢在方法内部实现嵌入方法,就像你为我的另一个问题组合的玻璃绘图一样...我有时也会使用它,但通常我会直接在单元或类中声明其他方法。 - Jerry Dodge
@Jerry 这些不是方法,因为它们不是类的成员。 - NGLN
1
@Jerry 当嵌套例程可以提高可读性时,我会将其嵌入到其他例程中,否则它们将导致一个大的函数或过程。当我需要在代码中添加注释来解释自己正在做什么,例如某些内容的开始和结束位置时,我会进行拆分。如果这些嵌套的例程在其他地方也被证明是有用的,那么它们就会演变成独立的代码甚至是单独的单元。 - NGLN
@NGLN,谢谢你,但是还有两个缺失的函数:IPv4ErrorFmt和IPv6ErrorFmt,也许可以抛出异常,这样对吗? - antonio
@antonio,它们已经被添加了(很久以前就添加了;-))。 - NGLN

4
我已经编写了您所需的所有功能,但恐怕我不能分享代码。
然而,Synapse库在synaip单元中包含了很多函数。例如:
function IsIP(const Value: string): Boolean;
function IsIP6(const Value: string): Boolean;
function IPToID(Host: string): Ansistring;
function StrToIp6(value: string): TIp6Bytes;
function Ip6ToStr(value: TIp6Bytes): string;
function StrToIp(value: string): integer;
function IpToStr(value: integer): string;
function ReverseIP(Value: AnsiString): AnsiString;
function ReverseIP6(Value: AnsiString): AnsiString;

几年前我尝试使用这些函数时,IPv6功能有些错误,特别是在处理压缩的IPv6地址时。
如果您想自行开发,以下是一些指导:
- 操作IPv4地址相当简单,因为它们仅由 32 位组成,可以存储在标准整数类型中。IPv6地址则更加困难,因为它们需要 128 位,没有任何本地类型具有那么多位。 - 在尝试操作IP地址之前,首先将其转换为整数(仅限IPv4),若要做到这一点,使用“.”作为分隔符拆分IP地址,检查每个单独的值是否只包含数字并且处于0到255之间,然后使用这些值生成最终的整数。 - 将IP地址转换为整数后,可以按照您的喜好操纵它。例如,给定一个IP地址和子网掩码,您可以找到IP地址所属的子网,例如:IPtoInt(IPAddress)AND NOT(IPToInt(SubnetMask))=子网的整数。现在,您可以将整数IP地址与整数子网进行比较,以查看IP是否位于子网内。 - 最后,将整数IP地址转换回字符串格式。
如果您有任何具体问题,我也可以尝试回答。

2
大多数相同的函数也在IdGlobal中。 - Marcus Adams
1
注意:Indy 9将该列表限制为IsValidIP和IsHostname。 - NGLN
1
IdGlobal中的IPv4ToUInt32()似乎有缺陷,它将"300.1.1.1"作为有效IP地址接受。 - Mehmet Fide

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接