思维导图:
1.子网掩码(重点!)
二级IP地址划分后,主机的基数还是比较大,所以引入了另外一个概念:子网掩码。利用子网掩码可以将主机号进行再次划分:
IP = 网络号 + 子网号 + 主机号
三级划分比较灵活,可以选择划分,也可以选择不划分,可以选择划分出2部分,4部分,8部分...2^n(n=0,1,2......)
1)子网掩码的概念
子网掩码:用于将一个大的IP网络中的主机号划分为若干小的子网络(常用)。
或者将若干个小网络组合成一个大的局域网(称之为超网技术)。
- 指明一个IP地址的哪些位表示的是主机所在的子网
- 指明哪些位表示的是主机的位掩码。
- 子网掩码不能单独使用,必须结合IP地址一起使用
子网掩码的格式:
- 与IP地址一样长的32位无符号整数,是由一串连续的1,后面跟着一串连续的0组成。
- 默认子网掩码的格式:
- 1的个数与IP地址中网络号的个数一致
- 0的个数与IP地址中主机号的个数一致。
2)默认子网掩码
A类IP地址的默认子网掩码:11111111 00000000 00000000 00000000 ===> 255.0.0.0
B类IP地址的默认子网掩码:11111111 11111111 00000000 00000000 ===> 255.255.0.0
C类IP地址的默认子网掩码:11111111 11111111 11111111 00000000 ===> 255.255.255.0
子网掩码是对主机号做再次划分,D类E类没有主机号,所以没有子网掩码。
C类IP地址的默认子网掩码:11111111 11111111 11111111 00000000 ===> 255.255.255.0 11111111 11111111 11111111 10000000 ===> 255.255.255.128
11111111 11111111 11111111 11000000 ===> 255.255.255.192
11111111 11111111 11111111 11100000 ===> 255.255.255.224
11111111 11111111 11111111 00000011 ===> 1不连续错误的!!!
3)格式用法:IP & 子网掩码=子网网段
i. 1个子网网段
192.168.125.229 & 255.255.255.0
11000000 10101000 01111101 11100101 ===> 192.168.125.229
11111111 11111111 11111111 00000000 ===> 255.255.255.0
---------------------------------------------------
11000000 10101000 01111101 00000000 ===> 192.168.125.0 子网网段
即192.168.125.229属于192.168.125.0该子网网段。
在00000000-11111111该范围内的主机号,&上255.255.255.0这个子网掩码,得到的结果均为192.168.125.0 子网网段。
即0~255这个范围内的主机号,均为192.168.125.0 子网网段。
所以没有划分出新的子网,所有主机号都在192.168.125.0 子网网段内。
该子网网段可以写作:192.168.125.0/24 (24代表该子网网段是通过有24个1的子网掩码得到)。 这个子网网段内的主机号是多少个 2^8个= 256个。
ii. 2个子网网段
192.168.125.229 & 255.255.255.128
11000000 10101000 01111101 1 1100101 ===> 192.168.125.229
111111111 111111111 111111111 1 0000000 ===> 255.255.255.128
----------------------------------------------------
11000000 10101000 01111101 1 0000000 ===> 192.168.125.128 子网网段
范围:如下范围的主机号[128, 255],& 255.255.255.128得到的结果均192.168.125.128/25 子网网段。
11000000 10101000 01111101 1 0000000 ===> 192.168.125.128
11000000 10101000 01111101 1 11111111===> 192.168.125.255
192.168.125.127 & 255.255.255.128
11000000 10101000 01111101 0 1111111 ===> 192.168.125.127
111111111 111111111 11111111 1 0000000 ===> 255.255.255.128
----------------------------------------------------
11000000 10101000 01111101 0 0000000 ===> 192.168.125.0 子网网段
范围:如下范围的主机号[0, 127],& 255.255.255.128得到的结果均为192.168.125.0/25 子网网段。
11000000 10101000 01111101 0 0000000 ===> 192.168.125.0
11000000 10101000 01111101 0 1111111 ===> 192.168.125.127
综上所述, 通过255.255.255.128可以将192.168.125.0该网络划分出2个子网 每个子网中有2^7个主机号。
子网网段个数 = 2^(子网掩码中多加的1的个数)
每个子网网段中主机号的个数 = 2^(剩余0的个数)
特殊的IP地址:
- 每个子网网段中,都有自己的子网网段地址。有效网络号+有效子网号+全是0的主机号 。 掐头
- 每个子网网段中,都有自己的子网广播地址。 去尾
总结:每个子网网段都需要掐头去尾
题型
- 任务:有如下B类IP地址,130.1.2.3,请问其默认子网掩码是什么?若想要划分出4个子网,请问子网掩码怎么设置,每个子网网段中主机号个数是多少个?每个子网网段中可用主机号个数是多少个?
- 默认子网掩码:255.255.0.0
- 子网掩码怎么设置: 4 = 2^2,多加2个1, 255.255.192.0
- 主机号个数是多少个:子网掩码中剩余14个0,所以每个子网网段中主机号是2^14个
- 每个子网网段中可用主机号个数是多少个: 2^14-2个
- 130.1.2.3该地址,若使用255.255.192.0子网掩码,请问该网络中所有可用主机号是多少个?
- 2^16-2*4(每个子网网段的头和尾)-1(网关)
- 192.168.0.0/25,请问该子网掩码是什么,共划分出了几个子网网段。请问该子网网段的网段地址和广播地址分别是什么?
- 255.255.255.128
- 两个子网网段
- 192.168.0.0/25
- 192.168.0.127
练习
某个公司有4 部门:行政 研发 售后 营销,每个部门20台电脑接入公司局域网交换机。
如果在192.168.1.0网段划分每个部分的子网,写出所有可用的子网掩码?子网的地址范围是什么
- 所以至少划分出4个子网网段:255.255.255. 1100 0000 --》 255.255.255.192 每个子网网段中,主机号的个数为:64个
- 划分出8个子网网段:255.255.255. 1110 0000 --》 255.255.255.224 每个子网网段中,主机号的个数为:32个
192.168.1.2~192.168.1.62 192.168.1.0/26
192.168.1.65~192.168.1.126 192.168.1.64/26
192.168.1.129~192.168.1.190 192.168.1.128/26
192.168.1.193~192.168.1.254 192.168.1.192/26
2.网关
网关是一个网络通向其他网络的IP地址
目前家用路由器一般使用192.168.1.1和192.168.0.1作为LAN接口的地址,这个两个也是最常用的网关地址。
【7】域名系统
由于使用IP地址来指定计算机不方便人们记忆,且输入时候容易出错,用字符标识网络种计算机名称方法。
这种命名方法就像每个人的名字,这就是域名(Domian Name)
域名服务器(Domain Name server):用来处理IP地址和域名之间的转换。
域名系统(Domain Name System,DNS):域名翻译成IP地址的软件
一个域名,可以绑定多个ip
域名结构
例如域名 www.baidu.com.cn 从右向左看
cn为高级域名,也叫一级域名,它通常分配给主干节点,取值为国家名,cn代表中国
com为网络名,属于二级域名,它通常表示组织或部门
中国互联网二级域名共40个,edu表示教育部门,com表示商业部门,gov表示政府,军队mil等等
baidu为机构名,在此为三级域名,表示百度
www:万维网world wide web,也叫环球信息网,是一种特殊的信息结构框架。
【8】端口号
为了区分一台主机收到的数据包交给哪个进程处理,使用端口号来区分。程序启动后将端口号和进程绑定在一起。
网络里面的通讯是由 IP地址+端口号 来决定
端口号存储在 2个字节 无符号整数中 (unsigned short int)。[1, 65535]
众所周知的端口号:
1~1023 端口我们编程时候不要使用,是那些”VIP“应用程序占了
TCP 21端口:FTP文件传输服务
TCP 23端口:TELNET终端仿真服务
TCP 25端口:SMTP简单邮件传输服务
TCP 110端口:POP3邮局协议版本3
TCP 80端口:HTTP超文本传输服务
TCP 443端口:HTTPS加密超文本传输服务
UDP 53端口:DNS域名解析服务
UDP 69端口:TFTP文件传输服务
TCP和UDP的端口号是相互独立的
可以使用的:1024~49151,就是我们平时编写服务器使用的端口号
临时端口号:49152~65535,这部分是客户端运行时候动态选择的
2. 跨主机传输
【1】字节序(重点!)
1. 字节序的概念
- 字节序是不同类型CPU主机,内存存储 多字节整数 序列的方式。
- char 字符串 float double均没有字节序的说法
- short int long long long有字节序的说法
- 小端字节序:低字节存储在低地址上,高字节存储在高地址上。
- 大端字节序:低字节存储在高地址上,高字节存储在低地址上。
ps:首地址都是低地址,数据的读取都是从低地址往高地址读取,经过大小端转换后得出结果。
练习
请简述字节序的概念,并用共用体(联合体)的方式,判断本机的字节序?
2. 本地字节序与网络字节序
本地字节数:主机字节序(Host Byte Order) HBO
网络字节序(Network Byte Order) NBO,网络字节序规定使用大端字节序。
在跨主机传输过程中,需要使用统一的字节序,即网络字节序,避免兼容性问题。
3.字节序转换函数
1)htons htonl 主机字节序-->网络字节序
头文件: #include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
参数: 指定要转换成网络字节序的整型:分别是32bit和16bit;
返回值: 成功,返回转换后网络字节序的整型
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, const char *argv[])
{
unsigned int a = 0x87654321;
printf("%#x\n", a); //0x87654321
printf("%#x\n", htonl(a)); //0x21436587
printf("%#x\n", htons(a)); //0x2143
return 0; }
2)ntohs ntohl 网络字节序---->主机字节序
头文件: #include <arpa/inet.h>
原型: uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
参数: uint32_t hostlong:32位网络字节序整型;
uint16_t hostshort:16位网络字节序整型;
返回值: 成功,返回转换成主机字节序的整型;
4. 结构体对齐
编译器会对结构体进行对齐,加速CPU取值周期,由于数据对齐也是与操作系统相关,不同的主机如果使用不同的对齐方式,会导致数据无法解析。
所以网络传输结构体的时候需要取消结构体对齐;
#include <stdio.h>
#pragma pack(1) //设置默认对齐系数 :()中的参数只能填2^n (n=0,1,2,3,4,5......)
typedef struct {
char a; //1
int b; //4
int d; //4
}_A;
#pragma pack() //重置默认对其系数,重新置为8
typedef struct
{
char a; //1
int b; //4
int d; //4
}__attribute__((packed)) B; //取消结构体对齐
typedef struct
{
char a; //1
//3
int b; //4
int d; //4
}_C;
int main(int argc, const char *argv[])
{
printf("%ld\n", sizeof(_A)); //9
printf("%ld\n", sizeof(_B)); //9
printf("%ld\n", sizeof(_C)); //12
return 0; }
5. 类型长度
int long int不同操作系统这两个数据类型所占的字节数可能是不一样的
解决方式:可以通过通用类型:uint8_t uint16_t uint32_t
因为涉及到跨平台,不同平台会有不同的字长
#include <stdint.h>
typedef struct
{
uint8_t a; //1
uint32_t b; //4
uint16_t d; //2
}_A;
【2】IP转换
由于IP地址本质上是一个4个字节的无符号整数,所以在跨主机传输中也有字节序的概念。
所以需要将IP地址转换成网络字节序。
"192.168.8.189" ---->本机字节序的整型 0xC0A808BD---->网络字节序0xBD08A8C0
"192.168.31.42"----> 0xC0A81F2A ---->0x2A1FA8C0
1. 点分十进制--->网络字节序
1)inet_aton
头文件:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
原型:
int inet_aton(const char *cp, struct in_addr *inp);
参数:
char *cp:源IP地址的点分十进制字符串,
struct in_addr *inp 存储转换成网络字节序的IP
typedef uint32_t int_addr_t;
struct in_addr {
in_addr_t s_addr;
};
返回值:
成功反回非0; 失败,返回0;
只能转换IPv4
例子:
#define IP "192.168.1.10" //0xC0A8010A
int main(int argc, const char *argv[])
{
struct in_addr inp; if(inet_aton(IP, &inp) == 0)
{
printf("转换失败\n");
return -1;
}
printf("%#X\n", inp.s_addr); //0X0A01A8C0 return 0; }
2)inet_pton
既可以转IPv4也能处理IPv6
头文件: #include <arpa/inet.h>
原型: int inet_pton(int af, const char *src, void *dst);
参数: int af:协议族
AF_INET IPV4
AF_INET6 IPV6
char *src:指定要转换成网络字节序的点分十进制字符串;
void* dst
typedef uint32_t in_addr_t;
struct in_addr
{ in_addr_t s_addr; };
af == AF_INETa;
struct in6_addr { }
返回值: 成功,返回1; 失败,返回0或者-1,更新errno;
#define IP "192.168.1.3" //0xC0A80103 --> 0x301A8C0 struct in_addr inp;
inet_pton(AF_INET, IP, &inp);
printf("%#X\n", inp.s_addr); //0x301A8C0
3)inet_addr 最常用
头文件: #include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
原型:
uint32_t inet_addr(const char *cp);
参数: char *cp:源IP地址的点分十进制字符串,例如 “192.168.1.10”;
返回值: 成功,返回转换后的网络字节序IP地址;
typedef uint32_t in_addr_t;
失败,返回INADDR_NONE (usually -1); 只能转换IPv4;
例子:
printf("%#X\n", inet_addr(IP));
2. 网络字节序--->点分十进制
1)inet_ntoa 常用
头文件:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
原型: char *inet_ntoa(struct in_addr in);
参数: struct in_addr in:指定要转换成点分十进制字符串的IP地址;
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
返回值: 成功,返回点分十进制字符串的首地址;
只能转换IPv4;
printf("%s\n", inet_ntoa(inp));
2)inet_ntop
头文件:
#include <arpa/inet.h>
原型:
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数:
int af:协议族
AF_INET IPV4
AF_INET6 IPV6
void* src:存储要转换成点分十进制字符串的IP首地址;
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
af == AF_INETa;
struct in6_addr
{
}
char *dst:存储转换后的结果,点分十进制的首地址;
socklen_t size:缓冲区大小,其实就是指定多大的空间用于转换IP;
返回值:
成功,返回字符串的首地址,就是dst;
失败,返回NULL,更新errno;
例子:
char ip[20];
if(inet_ntop(AF_INET, &inp, ip, sizeof(ip)) == NULL)
{
perror("ient_ntop");
return -1;
}
printf("%s\n", ip);
作业:用共用体的方式判断本机的字节序
#include<myhead.h>
//定义一个共用体
union Byte
{
unsigned int b;//无符号整数
char c;
};
int main(int argc, const char *argv[])
{
union Byte endianness;
endianness.b=0x87654321;
printf("endianness.c=%#x\n",endianness.c);
if(0x21==endianness.c)
printf("这是一个小端\n");
else if(0x87==endianness.c)
printf("这是一个大端\n");
return 0;
}
效果图: