目录
[9.OSI 七层和四层模型](#9.OSI 七层和四层模型)
1.知识点概述
-
网络设计模式
-
B/S
-
broswer - 浏览器 -> 客户端
-
html
-
css
-
js
-
server -> 服务器
-
优势:
-
跨平台
-
开发成本低
-
缺点:
-
网络通信的时候, 必须要使用http协议
-
http / https -> 应用层协议
-
不能在磁盘缓存或者从磁盘加载大量数据

2.两种网络模型
-
C/S
-
client -> 桌面应用程序 -> Qt
-
QQ
-
迅雷
-
微信
-
server -> 服务器
-
特点:
-
优点:
-
使用的协议可以随意选择
-
可以在本地缓存或者加载大量数据
-
缺点:
-
研发成本高
-
在不同的平台开发不同的客户端版本

3.服务器开发是什么
服务器:
-
硬件: 配置比较高的主机
-
买阿里云, 百度云服务器
-
软件:
-
有一台主机, 主机上运行了一个进程, 这个进程可以处理网络协议, 称这台主机是一个服务器
-
nginx
-
服务器开发:
-
工作不是去开发web服务器
-
我们做的工作:
-
在一台装有服务器的主机上开发应用程序
-
应用程序:
-
斗地主
-
文件服务器

4.IP分类
IP和端口
-
IP地址分类
-
公网IP
-
可以访问Interface, 公网IP是唯一的
-
局域网IP
-
小的网络, 比如一个路由器对应的家里的网段
-
192.168.1.xxxx
-
在这个小的网络中IP是唯一的
-
在这个网络中的主机可以互相通信
-
如果局域网和外网连接, 那么通过局域网IP也可以连接外网

5.IP协议
-
IP协议
-
IPV4 - `"Internet Protocol Version 4`
-
现在应用很广泛的协议
-
IP地址的点分十进制字符串
-
`192.168.1.100`
-
本质是一个整形数, 4字节, 32位
-
通过3个点分成4分, 每份一个字节
-
字节的取值范围 0-255
-
最大的IP地址: `255.255.255.255`
-
可用的IP地址:
-
2的32次方 -1
-
现在IPv4的IP地址非常不够用, 在2011年就分配完了
-
IPV6 -> `"Internet Protocol Version 6`
-
现在应用不是很广泛, 将来主推的一种网络协议
-
本质也是整形数, 16字节, 128bit
-
IP地址表示:
-
分为8份, 每份2字节, 使用16进制的数字表示
-
`fe80::020c:29ff:fe40:473a`
-
ip地址的个数:
-
2的128次方 -1

6.查看IP地址和测试主机之间是否能够通信
```shell
查看IP地址
linux
$ ifconfig
windows
$ ipconfig
测试两台主机之间是否可用通信
ping ip地址
$ ping 192.168.1.6
PING 192.168.1.6 (192.168.1.6) 56(84) bytes of data.
64 bytes from 192.168.1.6: icmp_seq=1 ttl=128 time=1.88 ms
64 bytes from 192.168.1.6: icmp_seq=2 ttl=128 time=0.678 ms


7.端口
- 端口
```shell
1. 端口的本质?
无符号短整型数 -> unsigned short
2. 端口取值范围?
-
可以有多少个端口: 2的16次方
-
取值范围: 0 - 65535
3. 端口的作用?
定位某台主机上运行的某个进程
在电脑上运行了微信和QQ, 小明给我的的微信发消息, 电脑上的微信就收到了消息, 为什么?
-
运行的qq和微信在启动的时候都绑定的了不同的端口
-
小明给我的微信发送数据(点对点模型)
-
需要知道我的IP地址 -> 定位我电脑
-
如果定位电脑中的某一个进程呢? ---> 通过端口


8.IP和端口的使用以及域名作用
- ip和端口的使用
```shell
通过IP定位网络环境中的主机, 通过端口定位主机上的进程
比如通过浏览器进行网络服务器访问
服务器有IP地址/域名, 服务器需要绑定端口
b/s -> 协议http -> 使用的端口是80, https协议端口-> 443
如果服务器使用的就是协议的默认端口, 访问的时候端口是可以省略不写的


9.OSI 七层和四层模型
OSI/ISO 网络分层模型
> OSI(Open System Interconnect),即开放式系统互联。 一般都叫OSI参考模型,是ISO(国际标准化组织组织)在1985年研究的网络互联模型。
```shell
上层
|
| 七层模型 四层模型
|
| 应用层
| 表示层 应用层 http/ftp/ssh/ftps
| 会话层
| 传输层 传输层 tcp / udp
| 网络层 网络层 ip -> ipv4/ipv6
| 数据链路层 网络接口层 以太网帧协议
底层 物理层

10.网络协议是什么
> - 物理层负责最后将信息编码成电流脉冲或其它信号用于网上传输
> - 数据链路层:
> - 数据链路层通过物理网络链路供数据传输。
> - 规定了0和1的分包形式,确定了网络数据包的形式;
> - 网络层
> - 网络层负责在源和终点之间建立连接;
> - 此处需要确定计算机的位置,怎么确定?IPv4,IPv6
> - 传输层
> - 传输层向高层提供可靠的端到端的网络数据流服务。
> - 每一个应用程序都会在网卡注册一个端口号,该层就是端口与端口的通信
> - 会话层
> - 会话层建立、管理和终止表示层与实体之间的通信会话;
> - 建立一个连接(自动的手机信息、自动的网络寻址);
> - 表示层:
> - 对应用层数据编码和转化, 确保以一个系统应用层发送的信息 可以被另一个系统应用层识别;
> - 可以理解为:解决不同系统之间的通信,eg:手机上的QQ和Windows上的QQ可以通信;
> - 应用层:
> - 规定数据的传输协议

11.数据在网络环境中发送和接受过程
> - 网络协议:
>
> 计算机双方共同遵守的一组约定, 通过这个约定就可以完成连接的建立, 相互识别, 按照某种特定的数据格式进行网络通信
>
> - 发送端: 按照约定的数据格式组织数据
> - 接收端: 按照约定的数据格式解析数据
-
常见协议
-
TCP协议 -> 传输层协议

- UDP协议 -> 传输层协议

- IP协议 -> 网络层协议

- 以太网帧协议



12.套接字通信涉及知识点

- 数据的封装

```shell
两台主机A,B
-
A给B发送字符串 "hello, world" , 字符串不会以这种形态发送到Internet, 需要在发送之前需要封装
-
封装是由操作系统或者调用的API自动完成的, 程序猿无需关心
-
如果程序猿需要封装数据, 一般是在应用层做处理, 应用层数据可封装也可以不封装
数据 "hello, world" 在应用层 进行发送
-
在应用层对这个字符串封装(可选, 程序猿做的)
-
数据往下走-> 传输层 -> 根据协议格式打包数据 , tcp
-
数据往下走-> 网络层 -> 根据协议格式打包数据 , ip
-
数据往下走-> 网络接口层 -> 根据协议格式打包数据, 以太网帧协议
-
数据通过网口发送给B
B接收数据, 拆包
-
网络接口层 -> 根据协议格式拆包, 以太网帧协议拆开
-
得到了网络层的包 -> 拆包
-
传输层的包 -> 拆包
-
应用层的包
-
应用层如果使用协议封装了数据继续拆包, 如果没有封装得到了 "hello, world"
程序猿只需要处理应用层, 应用层以下, 不需要我们处理

13.字节序
- socket编程
> Socket套接字由远景研究规划局(Advanced Research Projects Agency, ARPA)资助加里福尼亚大学伯克利分校的一个研究组研发。其目的是将TCP/IP协议相关软件移植到UNIX类系统中。设计者开发了一个接口,以便应用程序能简单地调用该接口通信。这个接口不断完善,最终形成了Socket套接字。Linux系统采用了Socket套接字,因此,Socket接口就被广泛使用,到现在已经成为事实上的标准。与套接字相关的函数被包含在头文件sys/socket.h中。

```c
套接字通信就是网络通信, 跟语言没有关系, 因为通信是基于协议的, 所有的编程都需要基于这些协议对数据进行处理
// 1. 套接字是什么?
-
套接字是一套网络通信的接口, 这个接口中封装了传输层协议( tcp / udp )
-
接口就是 api, 也就是一套函数
// 2. socket(插座), 套接字通信的组成部分?
- 服务器端, 插座的作用
-
被动接受连接的角色, 不会主动发起连接的
-
绑定固定IP和端口, 这样客户端才能进行连接
- 客户端
-
主动发起连接的角色, 连接服务器
-
连接服务器需要地址:
-
IP + 端口
// 3. 怎么用? -> 所有编程语言的通信流程都是固定的
-
服务器有通信流程
-
客户端有通信流程


14.大小端存储举例
```c
// 支持ipv4 和ipv6 格式的ip地址
#include <arpa/inet.h> // 套接字通信只需要包含这个头文件就可以了
// p-> 主机字节序的 字符串类型的IP地址
// 将主机字节序的字符串类型的IP -> 大端的整形数
int inet_pton(int af, const char *src, void *dst);
参数:
-
af: 地址族协议
-
AF_INET: 使用ipv4的网络协议
-
AF_INET6: 使用ipv6的网络协议
-
src: 主机字节序的字符串类型的IP地址, 要被转换的数据
-
dst: 传出参数, 存储了转换之后的大端的IP地址
返回值:
成功: 0
失败: -1
// 大端的整形数 --> 主机字节序的字符串类型的IP
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数:
-
af: 地址族协议
-
AF_INET: 使用ipv4的网络协议
-
AF_INET6: 使用ipv6的网络协议
-
src: 传入参数, 要被转换的数据, 指针指向的内存中存储了大端的IP地址(整形数)
-
dst: 传出参数, 指针指向的内存中存储了主机字节序 字符串类型的IP地址
-
size: 参数dst指向的内存的大小
返回值:
失败: NULL
成功: 指针指向函数第三个参数 dst 指向的内存
```

15.IP和端口大小端转换函数
```c
// 在写数据的时候不好用
struct sockaddr {
sa_family_t sa_family; // 地址族协议, ipv4, ipv6
char sa_data[14]; // 端口(2字节) + IP地址(4字节) + 填充(8字节)
}
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
typedef unsigned short int sa_family_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))
struct in_addr
{
in_addr_t s_addr;
};
// sizeof(struct sockaddr) == sizeof(struct sockaddr_in)
struct sockaddr_in
{
sa_family_t sin_family; /* 地址族协议: AF_INET */
in_port_t sin_port; /* 端口, 2字节-> 大端 */
struct in_addr sin_addr; /* IP地址, 4字节 -> 大端 */
/* 填充 8字节 */
unsigned char sin_zero[sizeof (struct sockaddr) - sizeof(sin_family) -
sizeof (in_port_t) - sizeof (struct in_addr)];
};
```
3.4 套接字函数


16.字符串类型的主机字节序IP转网络字节序
#include <arpa/inet.h>
// 创建一个套接字(文件描述符), 用于通信或者监听都是可以的
int socket(int domain, int type, int protocol);
参数:
-
domain:
-
AF_INET: 使用ipv4的网络协议
-
AF_INET6: 使用ipv6的网络协议
-
type:
-
SOCK_STREAM: 使用流式传输协议
-
SOCK_DGRAM: 使用报式传输协议
-
protocol: 默认写0
-
流式协议默认使用tcp
-
报式协议默认使用udp
返回值:
成功: 返回一个文件描述符
失败: -1
// 将监听的套接字和本地IP和端口进行关联
struct sockaddr_in addr;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
-
sockfd: 用于监听的套接字, 通过调用socket函数创建的
-
add: 将本地IP和端口信息初始化给该结构体, ip和端口使用大端
-
绑定的时候服务器端一般ip地址使用 INADDR_ANY == 0
-
0 == 0.0.0.0
-
表示绑定本机的所有的ip地址
-
一台主机有多个网卡 -> 多个IP地址
-
先识别实际IP让后绑定
-
addrlen: 记录第二个参数指针指向的内存大小, sizeof(struct sockaddr)
返回值:
成功: 0
失败: -1

17.网络字节序转字符串类型的主机字节序IP地址
// 发送数据
// 写缓冲区中数据写满了, 写阻塞
ssize_t write(int fd, const void *buf, size_t len);
ssize_t send(int fd, const void *buf, size_t len, int flags);
参数:
-
fd: 通信的文件描述符
-
accept的返回值(服务器端)
-
通过socket函数创建得到的, 通过connect初始化连接(客户端)
-
buf: 要发送的数据, 数据进入到了通信的文件描述符的写缓冲区
-
写缓冲区数据是由内核维护管理的, 这里边有数据, 内核就会进行发送
-
len: 发送的数据的实际长度, strlen();
-
flag: 使用默认属性, 指定为0即可
返回值:
>0: 发送的实际字节数
=0: 没有发送任何数据
-1: 发送失败, 异常
// 客户端用来连接服务器
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
-
sockfd: 通信的文件描述符, 通过socket()得到
-
addr: 连接的服务器的IP和端口信息, 初始化该变量中, 使用网络字节序(ip+端口)
-
addrlen: 参数addr指向的内存大小
返回值:
成功: 0
失败: -1
```
