网络基础知识


以下内容概念全部来自wiki,代码来自WebRTC

私有网络

IPV4的私有IP定义在RFC 1918

RFC1918 规定区块名 IP地址区段 IP数量 分类网络 说明 最大CIDR区块(子网掩码) 主机端位长
24位区块 10.0.0.0 – 10.255.255.255 16,777,216 单个A类网络 10.0.0.0/8 (255.0.0.0) 24位
20位区块 172.16.0.0 – 172.31.255.255 1,048,576 16个连续B类网络 172.16.0.0/12 (255.240.0.0) 20位
16位区块 192.168.0.0 – 192.168.255.255 65,536 256个连续C类网络 192.168.0.0/16 (255.255.0.0) 16位

IPV6的私有IP定义在RFC 4193,地址块fc00 :: / 7已保留。这些地址称为唯一本地地址(Unique Local Addresses,ULA)。 它们被定义为单播地址,并在路由前缀中包含一个40位的随机数,以防止在两个私有网络互连时发生冲突。 尽管在本地使用,但唯一本地地址的IPv6地址范围是全局的。

前缀 Global ID (随机) Subnet ID Number of addresses in subnet
48 bits 16 bits 64bits
fd00::/8 fd xx:xxxx:xxxx yyyy 18,446,744,073,709,551,616

下面给出WebRTC中是如何判断私有地址的代码

bool IPIsPrivate(const IPAddress& ip) {
  switch (ip.family()) {
    case AF_INET: {
      return IsPrivateV4(ip.v4AddressAsHostOrderInteger());
    }
    case AF_INET6: {
      return IPIsLinkLocal(ip) || IPIsLoopback(ip);
    }
  }
  return false;
}
uint32_t IPAddress::v4AddressAsHostOrderInteger() const {
  if (family_ == AF_INET) {
    return NetworkToHost32(u_.ip4.s_addr);
  } else {
    return 0;
  }
}
inline uint32_t NetworkToHost32(uint32_t n) {
  return be32toh(n);
}
bool IsPrivateV4(uint32_t ip_in_host_order) {
  return ((ip_in_host_order >> 24) == 127) ||
      ((ip_in_host_order >> 24) == 10) ||
      ((ip_in_host_order >> 20) == ((172 << 4) | 1)) ||
      ((ip_in_host_order >> 16) == ((192 << 8) | 168)) ||
      ((ip_in_host_order >> 16) == ((169 << 8) | 254));
}
bool IPIsLinkLocal(const IPAddress& ip) {
  // Can't use the helper because the prefix is 10 bits.
  in6_addr addr = ip.ipv6_address();
  return addr.s6_addr[0] == 0xFE && addr.s6_addr[1] == 0x80;
}
bool IPIsLoopback(const IPAddress& ip) {
  switch (ip.family()) {
    case AF_INET: {
      return (ip.v4AddressAsHostOrderInteger() >> 24) == 127;
    }
    case AF_INET6: {
      return ip == IPAddress(in6addr_loopback);
    }
  }
  return false;
}

如何判断IPV6是不是ULA

bool IPIsULA(const IPAddress& ip) {
  // Can't use the helper because the prefix is 7 bits.
  in6_addr addr = ip.ipv6_address();
  return (addr.s6_addr[0] & 0xFE) == 0xFC;
}

分类网络

为了和已存在的IP地址空间及IP数据报兼容,对IP地址的定义在1981年的RFC 791进行了修改。修改后的IP地址共有三种网络地址长度不同的单播地址。如下表所示:

Class 前缀位 网络地址位数 剩余的位数 网络数 每个网络的主机数 开始地址 结束地址 对应的CIDR修饰 默认子网掩码
A类地址 0 8 24 128 16,777,214 0.0.0.0 127.255.255.255 /8 255.0.0.0
B类地址 10 16 16 16,384 65,534 128.0.0.0 191.255.255.255 /16 255.255.0.0
C类地址 110 24 8 2,097,152 254 192.0.0.0 223.255.255.255 /24 255.255.255.0
D类地址(群播) 1110 未定义 未定义 未定义 未定义 224.0.0.0 239.255.255.255 /4 未定义
E类地址(保留) 1111 未定义 未定义 未定义 未定义 240.0.0.0 255.255.255.255 /4 未定义

路由形式

路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动。路由发生在OSI网络参考模型中的第三层即网络层。

单播

单播(原文:unicast)是指数据包在计算机网络的传输中,目的地址为单一目标的一种传输方式。它是现今网络应用最为广泛,通常所使用的网络协议或服务大多采用单播传输,例如一切基于TCP的协议。
每次只有两个实体相互通信,发送端和接收端都是唯一确定的。

广播

广播(英语:broadcast)是指将信息数据包发往指定网络范围内的所有设备。其发送范围称为“广播域”。
并非所有的计算机网络都支持广播,例如X.25网络和帧中继都不支持广播,而且也没有在“整个互联网范围中”的广播。IPv6亦不支持广播,广播的相应功能由多播代替。
通常来说,广播都是限制在局域网范围内,比如以太网或令牌环网络。因为广播在广域网中可能造成比在局域网中大的多的影响。
广播地址:

  • 以太网和IPv4网都用全1的地址表示广播,分别是ff:ff:ff:ff:ff:ff255.255.255.255
  • 令牌环网络使用IEEE 802.2控制域中的一个特殊值来表示广播。

多播

在计算机网络中,多播(英语:multicast,台湾又译作多点发送、多点广播或群播,中国大陆又译作组播)是一种群组通信,它把信息同时传递给一组目的计算机。多播可以是一对多或多对多布置。不应将其与物理层的点到多点通信混淆。
群组通信可由应用层多播实现,也可由网络级多播协助实现,后者能让一个源地址用一次传输将数据发给群组。数据到达包含该组成员的网络区域时,由路由器、交换机、基站子系统等网络组件自动完成复制分发。网络级多播可能通过数据链路层的一对多地址交换实现,如以太网多播地址、异步传输模式(ATM)、P2MP及Infiniband多播,也可能通过网络层由IP多播实现。在IP多播中,多播发生在IP路由层面,路由器创建一个最佳路径将数据发往多播目的地址。
多播通常应用于IP网络上的流媒体传输,如IPTV、多点视频会议等。

TCP/IP模型

通常人们认为OSI模型的最上面三层(应用层、表示层和会话层)在TCP/IP组中是一个应用层。由于TCP/IP有一个相对较弱的会话层,由TCP和RTP下的打开和关闭连接组成,并且在TCP和UDP下的各种应用提供不同的端口号,这些功能能够被单个的应用程序(或者那些应用程序所使用的库)增加。与此相似的是,IP是按照将它下面的网络当作一个黑盒子的思想设计的,这样在讨论TCP/IP的时候就可以把它当作一个独立的层。

层数 层名 说明
4 应用层 例如HTTP、FTP、DNS(如BGP和RIP这样的路由协议,尽管由于各种各样的原因它们分别运行在TCP和UDP上,仍然可以将它们看作网络层的一部分)
3 传输层 例如TCP、UDP、RTP、SCTP(如OSPF这样的路由协议,尽管运行在IP上也可以看作是网络层的一部分)
2 网络互联层 对于TCP/IP来说这是因特网协议(IP)(如ICMP和IGMP这样的必须协议尽管运行在IP上,也仍然可以看作是网络互连层的一部分;ARP不运行在IP上)
1 网络访问(链接)层 例如以太网、Wi-Fi、MPLS等。

UDP

在TCP/IP模型中,UDP为网络层以上和应用层以下提供了一个简单的接口。UDP只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以UDP有时候也被认为是不可靠的数据报协议)。UDP在IP数据报的头部仅仅加入了复用和数据校验字段。
UDP适用于不需要或在程序中执行错误检查和纠正的应用,它避免了协议栈中此类处理的开销。对时间有较高要求的应用程序通常使用UDP,因为丢弃数据包比等待或重传导致延迟更可取。

由于UDP缺乏可靠性且属于无连接协议,所以应用程序通常必须容许一些丢失、错误或重复的数据包。某些应用程序(如TFTP)可能会根据需要在应用程序层中添加基本的可靠性机制。
一些应用程序不太需要可靠性机制,甚至可能因为引入可靠性机制而降低性能,所以它们使用UDP这种缺乏可靠性的协议。流媒体,实时多人游戏和IP语音(VoIP)是经常使用UDP的应用程序。 在这些特定应用中,丢包通常不是重大问题。如果应用程序需要高度可靠性,则可以使用诸如TCP之类的协议。
例如,在VoIP中延迟和抖动是主要问题。如果使用TCP,那么任何数据包丢失或错误都将导致抖动,因为TCP在请求及重传丢失数据时不向应用程序提供后续数据。如果使用UDP,那么应用程序则需要提供必要的握手,例如实时确认已收到的消息。
由于UDP缺乏拥塞控制,所以需要基于网络的机制来减少因失控和高速UDP流量负荷而导致的拥塞崩溃效应。换句话说,因为UDP发送端无法检测拥塞,所以像使用包队列和丢弃技术的路由器之类的网络基础设备会被用于降低UDP过大流量。数据拥塞控制协议(DCCP)设计成通过在诸如流媒体类型的高速率UDP流中增加主机拥塞控制,来减小这个潜在的问题。

TCP

在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。
应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,然后TCP把数据流分割成适当长度的报文段(通常受该计算机连接的网络的数据链路层的最大传输单元(MTU)的限制)。之后TCP把结果包传给IP层,由它来透过网络将包传送给接收端实体的TCP层。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认信息(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失并进行重传。TCP用一个校验和函数来检验数据是否有错误,在发送和接收时都要计算校验和。

其他概念

名词 说明
MTU 最大传输单元是指资料连结层上面所能通过的最大数据包大小(以字节为单位)。最大传输单元这个参数通常与通信接口有关(网络卡、串口等)
NTP 网络时间协议,NTP使用64比特的时间戳,其中32位表示秒,32位表示秒的小数,NTP以1900年1月1日作为开始时间,因此第一次翻转将在2036年2月7日发生
RTT 来回通信延迟(Round-trip delay time)
FEC 前向纠错(forward error correction),是一种在单向通信系统中控制传输错误的技术,通过连同数据发送额外的信息进行错误恢复,以降低比特误码率
CRC 循环冗余校验(Cyclic redundancy check),一种数据完整性检测算法

WebRTC中CRC32算法的实现代码如下:

template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N];

#define arraysize(array) (sizeof(ArraySizeHelper(array)))

static const uint32_t kCrc32Polynomial = 0xEDB88320;
static uint32_t kCrc32Table[256] = {0};

static void EnsureCrc32TableInited() {
  if (kCrc32Table[arraysize(kCrc32Table) - 1])
    return; // already inited
  for (uint32_t i = 0; i < arraysize(kCrc32Table); ++i) {
    uint32_t c = i;
    for (size_t j = 0; j < 8; ++j) {
      if (c & 1) {
        c = kCrc32Polynomial ^ (c >> 1);
      } else {
        c >>= 1;
      }
    }
    kCrc32Table[i] = c;
  }
}

uint32_t UpdateCrc32(uint32_t start, const void* buf, size_t len) {
  EnsureCrc32TableInited();

  uint32_t c = start ^ 0xFFFFFFFF;
  const uint8_t* u = static_cast<const uint8_t*>(buf);
  for (size_t i = 0; i < len; ++i) {
    c = kCrc32Table[(c ^ u[i]) & 0xFF] ^ (c >> 8);
  }
  return c ^ 0xFFFFFFFF;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章