<TCP/IP网络编程> Chap.3 地址族与数据序列

分配给套接字的IP地址与端口号

只需通过IP地址的第一个字节即可判断网络地址占用的字节数:
A类地址的首字节范围:0~127
B类地址的首字节范围:128~191
C类地址的首字节范围:192~223

端口号是在统一操作系统内为区分不同套接字而设置的,因此无法将1个端口号要分配给不同套接字(但TCP与UDP之间可以重复)。可分配的端口号范围是065535,其中01023是知名端口号。

地址信息的表示

struct sockaddr_in
{
    sa_family_t       sin_family;     // 地址族
    uint16_t          sin_port;       // 16位TCP/UDP端口号,以网络字节序保存
    struct in_addr    sin_addr;       // 32位IP地址,以网络字节序保存
    char              sin_zero[8];    // 不使用,填充0
}
struct in_addr
{
    in_addr_t         s_addr;         // 32位IPv4地址
}


网络字节序与地址变换

CPU向内存保存数据的方式有两种(以4字节整型数值1为例):

  • 大端序:高位字节存放到位地址(00000000 00000000 00000000 00000001)。
  • 小端序:高位字节存放到位地址(00000001 00000000 00000000 00000000)。

目前主流的Intel系列CPU以小端方式保存数据。网络字节序是通过网络传输数据时约定的统一方式,是大端序。

传输时是从低位地址开始传输的。

帮助转换字节序的函数:

  • unsigned short htons(unsigned short);
  • unsigned short ntohs(unsigned short);
  • unsigned long htonl(unsigned long);
  • unsigned long ntohl(unsigned short);

前两者用于端口号转换,后两者用于IP地址转换。

# gcc endian_conv.c -o conv
# ./conv  
Host ordered port: 0x1234 
Network ordered port: 0x3412 
Host ordered port: 0x12345678 
Network ordered port: 0x78563412 

这是在小端序CPU中运行的结果。如果在大端序CPU中运行,网络和主机序的字节应是一样的。

网络地址的初始化与分配

以下函数不仅完成从字符串到十进制IP地址的转换,同时也进行了网络字节序转换。

#include <arpa/inet.h>
in_addr_t inet_addr(const char *string);

该函数的调用:

# gcc inet_addr.c -o addr
# ./addr 
Network ordered integer addr: 0x4030201 
Error occured!

inet_aton函数与inet_addr函数类似,只是它利用了in_addr结构,更加方便编程。

#include <arpa/inet.h>
int inet_aton(const char *string, struct in_addr *addr);    // 成功返回1,失败返回0

inet_aton函数的调用过程:

# gcc inet_aton.c -o aton
# ./aton
Network ordered interger addr: 0x4f7ce87f

当然还有从十进制IP地址转换回字符串的函数:

#include <arpa/inet.h>
char * inet_ntoa(in_addr addr);    // 成功返回字符串,失败返回NULL

注意该函数在自己的域申请了内存地址来存放返回的结果,在下次调用这个函数之前应将返回的结果保存到别的变量中,以免再次调用时被覆盖。测试一下:

# gcc inet_ntoa.c -o ntoa
# ./ntoa 
Dotted-Decimal notiation1: 1.2.3.4 
Dotted-Decimal notiation2: 1.1.1.1 
Dotted-Decimal notiation3: 1.2.3.4

如果服务器不特意绑定自己的某一个地址,而想接收从每个网卡发给指定port的数据,就可以使用INADDR_ANY,它代表本机地址0.0.0.0。使用时只需addr.sin_addr.s_addr=htonl(INADDR_ANY);即可。

习题

  1. IP地址族IPv4和IPv6有何区别?在何种背景下诞生了IPv6?
    主要区别在于表示IP地址所用的字节数。为了应对IP地址耗尽问题而提出了IPv6。
  2. 通过IPv4网络ID、主机ID及路由器的关系说明向公司局域网中的计算机传输数据的过程。
    先查看网络地址(网络ID),将数据传到构成该网络的路由器后,再查看主机地址(主机ID)并将数据传给目标主机。
  3. 套接字地址分为IP地址和端口号。为什么需要IP地址和端口号?或者说,通过IP可以区分哪些对象?通过端口号可以区分哪些对象?
    IP地址区分计算机,端口号区分应用。
  4. 请说明IP地址的分类方法,并据此说出下面这些IP地址的分类。
    按第一个字节来分类。
    a. 214.121.212.102 (C)
    b. 120.101.122.89 (A)
    c. 129.78.102.211 (B)
  5. 计算机通过路由器或交换机连接到互联网。请说出路由器和交换机的作用。
    路由器根据网络地址传递数据,交换机根据主机地址传递数据。
  6. 什么是知名端口?其范围是多少?知名端口中具有代表性的HTTP和FTP端口号各是多少?
    分配给特定应用程序的端口。1~1023。HTTP的默认端口号是80。默认情况下FTP协议使用TCP端口中的20和21这两个端口,其中20用于传输数据,21用于传输控制信息。
  7. 向套接字分配地址的bind函数原型是int bind(int sockfd, struct sockaddr *myaddr, socklen_t addr_len);而调用时则用bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));此处serv_addr与函数原型不同,传入的是sockaddr_in结构体变量,请说明原因。
    首先,struct sockaddr是一个统一的结构,可保存各种协议的地址与端口号等信息,所以bind函数必须使用这个通用的结构。其次,struct sockaddr除了第一个参数协议族以外,后面需要填充的地址与端口号(还有填充0的部分)是一个14字节的char型数组,由于对字节的操作比较麻烦,可以先将它们保存到sockaddr_in结构体中,再在传递时强制转换。
  8. 请解释大端序、小端序、网络字节序,并说明为何需要网络字节序。
    大端序指的是数据在内存中从把高位字节放到低位地址。小端序指的是数据在内存中从把低位字节放到低位地址。网络字节序是一种在网络中传输数据的约定,它是大端序。它的作用就是统一数据传输的标准。
  9. 大端序计算机希望把4字节整型数据12传递到小端序计算机。请说出数据传输过程中发生的字节序变换过程。
    4字节整型12在大端序计算机中保存为0x0c000000,在传输前转换成网络字节序,仍是0x0c000000。小端序计算机接收到后转换成小端序进行保存,即0x0000000c。
  10. 怎样表示回送地址?其含义是什么?如果向回送地址传输数据将发生什么情况?
    127.x.x.x(IPv4)或::1(IPv6)。计算机自身IP地址。数据被发送给本机的服务。


我的问题

  1. 多次调用inet_ntoa函数的结果为什么会被覆盖?
    字符串是在其内部静态分配的,后面的每次调用都会覆盖上一次的值。
char * 
inet_ntoa (struct in_addr in) 
{ 
  __libc_once_define (static, once); 
  char *buffer; 
  unsigned char *bytes; 
 
  /* If we have not yet initialized the buffer do it now.  */ 
  __libc_once (once, init); 
 
  if (static_buf != NULL) 
    buffer = static_buf; 
  else 
    { 
      /* We don't use the static buffer and so we have a key.  Use it
     to get the thread-specific buffer.  */ 
      buffer = __libc_getspecific (key); 
      if (buffer == NULL) 
    { 
      /* No buffer allocated so far.  */ 
      buffer = malloc (18); 
      if (buffer == NULL) 
        /* No more memory available.  We use the static buffer.  */ 
        buffer = local_buf; 
      else 
        __libc_setspecific (key, buffer); 
    } 
    } 
 
  bytes = (unsigned char *) ∈ 
  __snprintf (buffer, 18, "%d.%d.%d.%d", 
          bytes[0], bytes[1], bytes[2], bytes[3]); 
 
  return buffer; 
} 
  1. 地址127.0.0.1和0.0.0.0的区别?
    IPv4中,0.0.0.0地址被用于表示一个无效的,未知的或者不可用的目标。在服务器中,0.0.0.0指的是本机上的所有IPv4地址,如果一个主机有两个IP地址,192.168.1.1和10.1.2.1,并且该主机上的一个服务监听的地址是0.0.0.0,那么通过两个ip地址都能够访问该服务。在路由中,0.0.0.0表示的是默认路由,即当路由表中没有找到完全匹配的路由的时候所对应的路由。
    用途总结:
    a. 当一台主机还没有被分配一个IP地址的时候,用于表示主机本身(DHCP分配IP地址的时候)。
    b. 用作默认路由,表示”任意IPV4主机”。
    c. 用来表示目标机器不可用。
    d. 用作服务端,表示本机上的任意IPV4地址。
    127.0.0.1属于{127,}集合中的一个,而所有网络号为127的地址都被称之为回环地址,所以回环地址!=127.0.0.1,它们是包含关系,即回环地址包含127.0.0.1。
    回环地址:所有发往该类地址的数据包都应该被loop back。
    用途总结:
    a. 回环测试,通过使用ping 127.0.0.1测试某台机器上的网络设备,操作系统或者TCP/IP实现是否工作正常。
    b. DDos攻击防御:网站收到DDos攻击之后,将域名A记录到127.0.0.1,即让攻击者自己攻击自己(?)。
    c. 大部分Web容器测试的时候绑定的本机地址。
  2. 发送给127.0.0.1的数据是怎样被传递的?
    数据包不会经过网卡,而是在TCP/IP栈的环回驱动程序中直接被放到IP输入函数。


附录

Github
127.0.0.1和0.0.0.0地址的区别
网络篇-“Ping 127.0.0.1”工作原理你知道吗?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352