【TCP/IP网络编程】:03地址族与数据序列

上一篇文章介绍了套接字的建立过程,这篇文章主要讨论分配给套接字的IP地址和端口号的相关知识。编程

IP地址和端口号浏览器

IP(Internet Protocol,网络协议)地址是收发网络数据而分配给计算机的值,端口号则并不是赋予计算机的值,而是为了区分计算机程序所建立的不一样套接字而分配给套接字的编号。服务器

网络地址网络

IP地址分为以下两类,其中,IPv6是为了应对2010年先后IP地址耗尽的问题而提出的新标准。不过,目前广泛使用的主要仍是IPv4,IPv6的普及可能还须要一段时间。socket

  IPv4(Internet Protocol version 4)  4字节地址族函数

  IPv6(Internet Protocol version 6)  16字节地址族oop

IPv4标准的4字节IP地址由网络地址和主机地址组成,其中网络地址主要分为A、B、C、D等类型,结构以下:ui

 IPv4地址族spa

IP地址为何要分为网络地址和主机地址?网络地址是为了区分不一样网络而设置的一部分IP地址,试想,若是4字节的IP地址全都是主机地址,那么计算机间的通讯寻路将会变得异常低效。而网络地址的加入将通讯寻路过程分为了两个部分,首先锁定有效的网络地址,再查找主机地址,有效提升了通讯效率。例如,某主机向203.211.172.103和203.211.217.202传输数据,其中,203.211.172和203.211.217为网络地址。从图中能够看出,网络地址的节点是由路由器或交换机组成的,接收数据的路由器再根据数据中的主机地址向目标主机传输数据。操作系统

上面提到的网络地址是203.211.172和203.211.217,那么如何区分它们的类型?咱们只须要关注IP地址的第一个字节的数值便可:

  A类地址首字节范围:0~127  0000 0000 ~ 01111 11111

  B类地址首字节范围:128~191   1000 0000 ~ 10111 11111

  C类地址首字节范围:192~223   1100 0000 ~ 11011 11111

另外,根据上面不一样类型网络地址所占的字节数,可以知道A类地址是最宝贵的,由于它拥有的IP地址资源最为丰富。一个A类地址至关于256个B类地址,一样的,一个B类地址至关于256个C类地址。

端口号

IP地址用于区分计算机,只要有IP地址就可以向目标主机传输数据,但仅此并不能肯定具体程序。好比,此时计算机中正运行着聊天程序和浏览器程序,那么该计算机如何决定将接收到的网络数据发给哪一个程序呢?

计算机中通常都配有NIC(Network Interface Card,网络接口卡)数据传输设备,经过NIC向计算机内部传输数据时须要用到IP地址。而操做系统负责将内部数据具体分配给套接字,这时就须要用到端口号来做为区分。整个数据的分配过程以下:

端口号就是同一操做系统中为了区分不一样套接字而设置的,所以没法将一个端口号分配给不一样的套接字(因为TCP套接字和UDP套接字不会共用端口号,能够重复)。端口号由16位组成,范围0~65535,但0~1023是特定程序使用的知名端口号,因此应当使用此范围以外的值。

地址信息的表示

以前的文章中有提到sockaddr_in和sockaddr,下面给出相关的结构体定义

//IPv4地址结构
struct sockaddr_in
{
    sa_family_t    sin_family;       //地址族
    uint16_t     sin_port;        //16位端口号,网络序保存
    struct in_addr sin_addr;         //32位IP地址,网络序保存
    char        sin_zero[8];     //保留
}

//通用地址结构
struct sockaddr
{
    sa_family_t    sin_family;     //地址族
    char           sa_data[14];    //地址信息
}

struct in_addr
{
   in_addr_t    s_addr;    //32位IPv4地址
}

以上结构体定义中涉及到的数据类型如uint16_t、in_addr_t等,可参考POSIX(Portable Operating System Interface,可移植操做系统接口)。之因此有这些定义,是考虑到移植性的结果,如uint16_t,不管是在32位系统中仍是在64位系统中其对应数据类型均是16位无符号整形。POSIX中的一些数据类型定义以下:

以前的文章中有提到sockaddr_in和sockaddr的区别的问题,结合上面的结构体定义可知,sockaddr_in是IPv4地址信息专用的结构体;而sockaddr则是兼容除IPv4地址信息以外的结构体(不过从地址信息字节大小14个字节来看,彷佛并不兼容IPv6)。

网络字节序与地址转换

字节序,简单来讲就是一个数据的高低字节在内存中的存储及解析方式。字节序分为以下两种方方式:

  大端序(Big Endian):高位字节存放在低位地址

  小端序(Little Endian):低位字节存放在高位地址

单字节数据的大小端存储方式并无区别,多字节数据的大小端存储方式则大相径庭。如一个4字节整型值0x12345678的高字节数据0x12,大端存储时在内存的低地址端,而小端存储则正好相反:

 

大端序存储和小端序存储

为何计算机通讯须要字节序转换?若是发生数据交换的两台计算机有不一样的字节序存储方式会发生什么?

 不一样字节序的计算机发生数据交换时引起的错误

正如上图所发生的那样,大端序系统的数据传输到小端序系统,原来的数据0x1234被错误地解析为0x3412。这显然不是咱们所指望获得的结果,所以当须要在不一样的计算机间传输数据时,咱们要时刻铭记字节序的转换。而考虑到程序的可移植性,即便咱们已知通讯双方主机拥有相同字节序,咱们仍需作字节序转换的操做。进一步,当有数据须要保存而且有可能在其余主机上恢复时,咱们也须要考虑字节序转换的操做。

实际上,在网络编程中,咱们只须要考虑向sockaddr_in结构体变量填充数据时的字节序转换,而真正传输数据的字节序转换是底层机制自动完成的。

网络地址的初始化

sockaddr_in中保存的地址信息成员为32位整型值,而咱们熟悉的是点分十进制(Dotted Decimal Notation)这种字符串表示方法,所以须要作相应的装换。下面介绍的一些函数将帮助咱们完成这些装换

#include <arpa/inet.h>

in_addr_t inet_addr(const char *string);
    -> 成功时返回32位大端序整型值,失败时返回INADDR_NONE
#include <arpa/inet.h>

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

char *inet_ntoa(struct in_addr addr);
    -> 成功时返回转换的字符串首地址,失败时返回-1

函数inet_addr和inet_aton在功能上彻底相同,不过函数inet_aton能够直接返回in_addr结构体,即直接传入sockaddr_in中地址成员sin_addr的地址便可保存地址值。函数inet_ntoa则刚好与inet_aton相反,完成网络地址从32位整型值到点分十进制的字符串的转换。不过函数inet_ntoa所返回的字符串只是存储在内部临时的内存空间中,一旦下次被调用,则以前的转换字符串将被覆盖。所以,对于函数inet_ntoa的返回值咱们应及时copy到本身的存储空间中。

网络地址初始化

struct sockaddr_in serv_addr;
char *serv_ip = "192.168.158.10";
char *serv_port = "8080";
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(serv_ip);
serv_addr.sin_port = htons(atoi(serv_port));

sockaddr_in地址在绑定到socket套接字时,须要先初始化并赋值。memset将sockaddr_in内存空间的全部值置0,主要是将sockaddr_in没有使用到的第4个保留参数sin_zero置0,不然可能会引发一些未知的错误(好比在与sockaddr结构体转换的过程引入的垃圾值所带来的影响)。

INADDR_ANY

以前的系列文章中提到过关于INADDR_ANY地址的思考,INADDR_ANY的做用是能够自动获取运行服务器端计算机的IP地址。若同一计算机中已分配多个IP地址(多宿主(Multi-homed)计算机,通常路由器属于这一类),则只要端口号一致,就能够从不一样IP地址接收数据。所以,服务器端会优先考虑这种方式。

建立服务器端套接字为什么须要配置IP地址?如上所述,同一计算机中能够分配多个IP地址,实际IP地址的个数与计算机中安装的NIC数量相等,所以,即便是服务器端也须要决定接收那个IP传来的数据。而若是服务器端只有一个NIC,则可直接使用INADDR_ANY便可。

关于127.0.0.1地址

127.0.0.1是回环地址(loopback address),指的是计算机自身的IP地址。若是服务器端和客户端都运行在同一台计算机中,则使用该地址替换计算机的实际IP地址仍可正常运行。

相关文章
相关标签/搜索