5

我想编写一个相当简单的客户端-服务器网络应用程序。我只使用纯 IPv4 网络,但如果我的代码能够适应未来,那就太好了。我可能会使用 TcpListener/TcpClient,因为对 WCF 的初步调查显示它的设置过于复杂且难以理解。

对于客户端,.NET 是否提供了一种工具来自动解码包含 IPv4 或 IPv6 地址(其中 IPv4 地址包括端口号)的字符串?如果它可以解析域名,则可以加分。

对于服务器端,我听说 IPv6 不使用端口号,那么侦听端口的等效项是什么,是否有一种标准方法可以将 IPv4 端口号字符串与 IPv6 等效项区分开来?没关系,IPv6 服务与 IPv4 服务一样具有 16 位端口号。

4

4 回答 4

6

的 System.Net.IPAddress

IPAddress.Parse( "fe80::21c:42ff:fe00:8%vnic0" );
IPAddress.Parse( "127.0.0.1" );

并测试 IPv4 或 v6

if( IPAddress.Parse(...).AddressFamily == AddressFamily.InterNetwork )
  // IPv4 address
于 2011-03-11T18:53:58.403 回答
2

System.Net.IPAddress 可用于解析表示有效 IPv4 和 IPv6 地址的字符串。System.Net.Dns 可用于解析网络上的主机名和地址。

对彼此而言,

using System.Net;
于 2011-03-11T18:55:24.977 回答
2

@Qwertie 说:

由于 IPv6 地址也包含冒号,我想知道解码包含端口号的 IPv6 地址或 IPv4 地址的正确方法是什么。

正如其他人所指出的,端口号不是 IP 地址(IPv4 或 IPv6)的一部分。IP 地址是原子的无符号整数(IPv4 是 32 位,IPv6 是 128 位)。当以字符形式(例如 URI)表示时,它们可以与端口号(和其他信息)结合使用。在 URI 中,主机和端口是 [可以是] URI权限部分的一部分。

URI 可以使用System.Uri. URI的Authority一部分由以下属性组成:Host, Port(以及,可选且已弃用的 UserInfo 子组件,由用户名和密码组成)。该属性HostNameType会告诉您您拥有什么样的主机名,枚举值UriHostNameType:Dns、Ipv4、Ipv6 或其他。

您还可以使用RFC 3986附录 B 中定义的正则表达式将 URI 解析为其组成部分:

附录 B. 使用正则表达式解析 URI 引用
由于“第一场比赛获胜”算法与“贪婪”算法相同 POSIX正则表达式使用的消歧方法,它是 使用正则表达式解析 URI 引用的潜在五个组成部分。
以下行是用于分解 a 的正则表达式 对其组件的格式良好的 URI 引用。
^(([^:/?#]+):)?(//([^/?#] ))?([^?#] )(\?([^#] ))?(#(.))? 12 3 4 5 6 7 8 9
上面第二行中的数字只是为了便于阅读; 它们指示每个子表达式的参考点(即,每个 双括号)。我们指的是子表达式匹配的值 <n> 为 $<n>。例如,将上述表达式与
http://www.ics.uci.edu/pub/ietf/uri/#Related
匹配会 导致以下子表达式匹配:
$1 = http: $2 = http $3 = //www.ics.uci.edu $4 = www.ics.uci.edu $5 = /pub/ietf/uri/ $6 = 7 美元 = $8 = #相关 9 美元 = 相关
其中 <undefined> 表示组件不存在,原样 上例中查询组件的情况。因此,我们 可以将五个分量的值确定为
方案 = $2 权限 = 4 美元 路径 = $5 查询 = $7 片段 = 9 美元
反过来,我们可以重新创建一个 URI 引用 它的组件使用第 5.3 节的算法。

请注意,这个正则表达式似乎有点不正确。根据RFC 3986 及其前身 RFC 2396的勘误表:

勘误编号:1933
状态:已验证 类型:技术 报告人:跳过 Geel 报告日期:2009-10-25 验证人姓名:Peter Saint-Andre 验证日期:2010-11-11
附录 B 部分说:
^(([^:/?#]+):)?(//([^/?#] ))?([^?#] )(\ ?([^#] ))?(#(. ))?
它应该说:
/^(([^:\/?#]+):)?(\/\/([^\/?#] ))?([^?#] )(\?([^ #] ))?(#(. ))?/
注:
正则表达式由斜杠 ("/") 分隔。里面有一个斜线 前面应该有一个反斜杠(“\”)。
Peter:这也适用于 RFC 3986。

于 2011-03-11T20:16:19.760 回答
0

正如 Paul 所提到的,没有端口号的普通 IP 地址可以通过IPAddress.Parse(). 但是,如果有端口号和/或主机名(12.34.56.78:90 或 www.example.com:5555),则需要使用不同的方法。如果你想使用 TcpClient 连接,这个函数会这样做:

public static TcpClient Connect(string ipAndPort, int defaultPort)
{
    if (ipAndPort.Contains("/"))
        throw new FormatException("Input should only be an IP address and port");

    // Uri requires a prefix such as "http://", "ftp://" or even "foo://".
    // Oddly, Uri accepts the prefix "//" UNLESS there is a port number.
    Uri uri = new Uri("tcp://" + ipAndPort);

    string ipOrDomain = uri.Host;
    int port = uri.Port != -1 ? uri.Port : defaultPort;
    return new TcpClient(ipOrDomain, port);
}

defaultPort如果输入字符串没有,该参数指定要使用的端口。例如:

using (NetworkStream s = Connect("google.com", 80).GetStream())
{
    byte[] line = Encoding.UTF8.GetBytes("GET / HTTP/1.0\r\n\r\n");
    s.Write(line, 0, line.Length);

    int b;
    while ((b = s.ReadByte()) != -1)
        Console.Write((char)b);
}

要在不连接地址的情况下解码地址(例如,验证它是否有效,或者因为您通过需要 IP 地址的 API 进行连接),此方法将这样做(并可选择执行 DNS 查找):

public static IPAddress Resolve(string ipAndPort, ref int port, bool resolveDns)
{
    if (ipAndPort.Contains("/"))
        throw new FormatException("Input address should only contain an IP address and port");

    Uri uri = new Uri("tcp://" + ipAndPort);

    if (uri.Port != -1)
        port = uri.Port;
    if (uri.HostNameType == UriHostNameType.IPv4 || uri.HostNameType == UriHostNameType.IPv6)
        return IPAddress.Parse(uri.Host);
    else if (resolveDns)
        return Dns.GetHostAddresses(uri.Host)[0];
    else
        return null;
}

奇怪的是,Dns.GetHostAddresses可以返回多个地址。我问了一下,显然可以简单地使用第一个地址。

如果存在语法错误或解析域名 (FormatExceptionSocketException) 的问题,将引发异常。如果用户指定了域名但是resolveDns==false,这个方法返回null

于 2011-03-11T22:14:33.357 回答