一、网络通信模式:
大部分的网络应用系统可以分为两个部分:客户(Client)和服务器(Server)
网络服务程序架构分为两种:CS模式和BS模式
CS模式:Client/Server(客户机/服务器)结构:
优点:
交互性强、具有安全的存取模式、网络通信量低、响应速度快、利于处理大量数据。
缺点:
该结构的程序是针对性开发,变更不够灵活,维护和管理的难度较大。该结构的每台客户机都需要安装相应的客户端程序,分布功能弱且兼容性差,不能实现快速部署安装和配置,因此缺少通用性,具有较大的局限性。
应用:
QQ为CS模式 需要安装客户端软件才能访问服务器。
BS模式:Browser/Server(浏览器/服务器)结构:
就是只安装维护一个服务器(Server),而客户端采用**浏览器(Browse)**运行软件。B/S结构应用程序相对于传统的C/S结构应用程序是一个非常大的进步。
优点:
分布性强、维护方便、开发简单且共享性强、总体拥有成本低。
缺点:
数据安全性问题、对服务器要求过高、数据传输速度慢、软件的个性化特点明显降低,难以实现传统模式下的特殊功能要求。例如通过浏览器进行大量的数据输入或进行报表的应答、专用性打印输出都比较困难和不便。此外,实现复杂的应用构造有较大的困难。
应用:
网站、学校教务系统等这些通过浏览器访问的模式叫BS模式。
二、OSI 七层网络模型:
应用层,表示层,会话层,传输层,网络层,数据链路层,物理层。
OSI七层网络模型,是把网络通信在逻辑上的定义,定义了通用的网络通信规范。而我们的数据在网络中传输的过程,实际上就是如下图的封装和解封装的过程,发送方通过各种封装处理,把数据转换成比特流的形式,比特流在信号传输的硬件媒介中传输,接收方再把比特流进行解封装处理。
数据的发送和接收的过程 :
三、TCP/IP协议-四层结构
1. 应用层:
网络中的应用程序, qq, weixin , vnc,
p2p ,ftp
不同的应用程序,有不同的私有协议。
2. 传输层: (tcp, udp)
规定数据是如何传输的。
TCP协议 : transport Control Protocol 传输控制协议
他是面向连接的传输层的协议。
提供可靠性,数据无丢失,数据无失序,数据无重复(类似于打电话)。
通过"三次握手"才能建立连接
(1)Client首先向Server发送连接请求报文段,同步自己的seq(x),Client进入SYN_SENT状态。
(2)Server收到Client的连接请求报文段,返回给Client自己的seq(y)以及ack(x+1),Server进入SYN_REVD状态。
(3)Client收到Server的返回确认,再次向服务器发送确认报文段ack(y+1),这个报文段已经可以携带数据了。Client进入ESTABLISHED状态。
(4)Server再次收到Client的确认信息后,进入ESTABLISHED状态。
应答机制: 接收方收到数据时候,要应答。
什么场景会用TCP传输: ftp服务器, 对数据要求不能错,1对1。
UDP协议 : User Datagram Protocol 用户数据包协议
是不可靠的无连接的传输层协议。
不需要连接,不需要应答,数据不可靠,数据可能丢失,数据可能失序,数据可能重复(类似于发快递)
什么场景会用UDP传输: VNC ,直播 ,视频流,可以广播,可以1对多
3.网络层:(ip, icmp,igmp)
IGMP协议: Internet 组管协议。
icmp协议: Internet control message protoctl 控制报文协议
他是TCP/IP 协议组的一个子协议,用于在IP主机,路由器之间传递控制信息
IP协议: ipv4, ipv6
通过给网络上每台电脑主机分配一个 id, 用来表示这台主机====》ip地址
4. 设备驱动和硬件层
网卡, MAC地址(网卡上的物理地址,全世界唯一)标识一个网卡
如何查看:
windows: ipconfig -all
Linux:ifconfig
Inet 172.20.10.8 ip地址
Netmark 255.255.255.240 子网掩码
Broadcast 172.20.10.15 广播地址
四、互联网地址(IP地址)
互联网上给每个接口(网卡)必须有一个唯一的Internet地址
也称为IP地址,是协议上的一个逻辑地址。 ip地址分为ipv4(32bit),ipv6(128bit)
ip地址的组成
一般我们把IPV4的地址,分为2个部分: 像日常 电话号码: 区号0731+主机号87654321
32bit = 网段号bits + 主机号bits
网段号占左边的连续的 bits
主机号占右边的连续的 bits
网段号到底占多个bits ? 由 netmask 子网掩码来决定
子网掩码由连续的 1 组成,后面跟着连续的 0。
网段号的所有位为1 主机号的所有位为0。
若一个网段的netmask为255.255.255.0
255.255.255.0 ->11111111 11111111 11111111 00000000
高24位为网段号,低8位为主机号
若设置这个网段,则最多有256-2台主机(去除IP地址和广播地址)
255.255.255.130 ====>error netmask 是一个错误的子网掩码
(255.255.255.130->11111111.11111111.11111111.10000010 不连续)
IP地址分类:
A: 0网络号(7bit) 主机号(24bit)
网段号: 00000000 ---- 01111111
IP地址: 0.0.0.0 ----- 127.255.255.255
B: 10网络号(14bit) 主机号 (16bit)
网段号: 10000000 0000000---- 10111111 11111111
IP地址: 128.0.0.0 ----- 191.255.255.255
C: 110网络号(21bit) 主机号 (8bit)
网段号: 11000000 0000000 00000000---- 11011111
11111111 11111111
IP地址: 192.0.0.0 ----- 223.255.255.255
D: 1110网络号(28bit) D类地址主要用来进行多播和组播11100000----11101111:
224.0.0.0 --- 239.255.255.255
E: 留待后用 240.0.0.0 ---247.255.255.255
IP地址用来唯一的表示 网络中的主机
一台主机上 又有 多个 应用程序: QQ, IE, VNC,微信,腾讯视频....
发送数据到一个IP地址,他又是如何知道 发给哪个 应用程序? 端口
号
五、端口号
网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,如何区分?
IP地址唯一标识网络中的设备(主机),端口号唯一标识设备中的进程(应用程序)。
实际上通过"IP地址+端口号"来区分不同的服务的
而且,网络应用从传输层可以分为: TCP应用, UDP应用 。不同的应用程序 端口号不同:
http==== 80
ftp =====21
qq ======808
tcp的端口 和 udp端口是独立的
一台主机上的应用程序: IP地址+传输层(UDP,TCP协议)+PORT(端口号) ,利用 协议 + IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。
六、字节序
大端模式:存储器的地地址存放寄存器的高字节:
网络顺序属于大端模式。
小端模式:存储器的地地址存放寄存器的低字节,
主机顺序属于小端模式
相关函数:
主机字节序转换为网络字节序的函数
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
网络字节序转换为主机字节序的函数
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
点分十进制IP地址转换为二进制IP地址函数
int inet_aton(const char *cp, struct in_addr *inp);//仅适用于ipv4
int inet_pton(int af, const char *src, void *dst);//适用于ipv4和ipv6
二进制IP地址转换为点分十进制IP地址函数
char *inet_ntoa(struct in_addr in);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
七、socket 套接字
Linux系统中,硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。为了表示和区分已经打开的文件,Linux会为每个文件分配一个ID,这个文件就是一个整数,被称为文件描述符。
网络连接也是一个文件 有文件描述符为socket
socket 是一个编程接口(网络编程的API)socket 是一个特殊的文件描述符,普通的文件操作函数如: read, write同样适用于socket。
socket 接口不仅限于 TCP/IP
socket 类型:
1. 流式套接字 : SOCK_STREAM 针对于 TCP 传输层
2. 数据包套接字: SOCK_DGRAM 针对于 UDP 传输层
3. 原始套接字 : SOCK_RAW -->直接跳过传输层
Ping
八、socket 编程的流程 及 API函数
TCP 流程:
TCP server 服务器
1.创建套接字(socket)
2.将socket与IP地址和端口绑定(bind)
(如果不调用bind ,内核会分配 IP+端口号port来绑定 ,但作为 TCP服务器,必须要绑定一个
“众所周知”的端口号。好让客户端来连接你 )
3.监听被绑定的端口(listen)
4.接收连接请求(accept)
5.从socket中读取客户端发送来的信息(read)
6.向socket中写入信息(write)
7.关闭socket(close)
TCP client 客户端
1.创建套接字(socket)
2.连接指定计算机的端口(connect)
3.向socket中写入信息(write)
4.从socket中读取服务端发送过来的消息(read)
5.关闭socket(close)
UDP 流程:
UDP server 服务器:
1.使用函数socket(),生成套接字文件描述符;
2.通过struct sockaddr_in 结构设置服务器地址和监听端口;
3.使用bind() 函数绑定监听端口,将套接字文件描述符和地址类型变量(struct sockaddr_in )进行绑定;
4.接收客户端的数据,使用recvfrom() 函数接收客户端的网络数据;
5.向客户端发送数据,使用sendto() 函数向服务器主机发送数据;
6.关闭套接字,使用close() 函数释放资源;
UDP client 客户端:
1.使用socket(),生成套接字文件描述符;
2.通过struct sockaddr_in 结构设置服务器地址和监听端口;
3.向服务器发送数据,sendto() ;
4.接收服务器的数据,recvfrom() ;
5.关闭套接字,close() ;
socket API函数
1.socket()
socket(int domain, int type, int protocol);
参数:
domain: 协议族
AF_UNIX, AF_LOCAL Local communication 本地协议族AF_INET IPv4 Internet protocols IPV4协议
AF_INET6 IPV6协议
type: 指定套接字的类型
SOCK_STREAM 流式套接字,TCP
SOCK_DGRAM 数据报套接字, UDP
SOCK_RAW 原始套接字
protocol: 应用层协议,可以为0
返回值: 成功 返回一个 新的文件描述符 失败 -1
2.socket 地址结构:
struct sockaddr
{
sa_family_t sa_family; // 协议族== AF_INET
char sa_data[14]; // 存放 IP(4字节32bits) + PORT(2字节16bits)
}
struct sockaddr_in
{
sa_family_t sa_family; // 协议族== AF_INET
u_int16_t sin_port; // 端口号
struct in_addr sin_addr; // IPV4地址char sin_zero[8]; // 填充用的 ,为了保持与他的的协议族
的结构体大小一样
}
struct in_addr
{
in_addr_t s_addr; // ipv4 地址是 unsigned 32bits number
}
3.bind()
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd: 套接字描述符
struct sockaddr *addr:socket地址结构
socklen_t addrlen: 地址的长度,一般用sizeof(struct sockaddr_in)
表示
4.listen()
int listen(int sockfd, int backlog);
参数:
sockfd:socket系统调用返回的服务端socket描述符
backlog:指定在请求队列中允许的最大的请求数,大多数系统默认为5
返回值:成功返回0,失败返回-1
5.accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd: 套接字描述符
struct sockaddr *addr: 用来保存客户端的地址信息
socklen_t *addrlen:客户端的地址长度
返回值:连接描述符,这个连接描述符就唯一代表与一个客户端的连接。服务器后续的所有与该客户端的数据收发都通过 该描述符.
6.connect()
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数:
sockfd: 套接字描述符
const struct sockaddr *addr: 服务端的ip地址和端口号的地址结构指针
socklen_t addrlen: 服务器的地址长度
7.send()、recv()(TCP收发信息)
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
sockfd:套接字描述符
buf:要发送的内容
len:发送内容的长度
flags:设置为MSG_DONTWAITMSG 时 表示非阻塞,设置为0时 功能和write一样
返回值:成功返回实际发送的字节数,失败:返回 -1
ssize_t recv(int sockfd, const void *buf, size_t len, int flags);
参数:
sockfd:在哪个套接字接
buf:存放要接收的数据的首地址
len:要接收的数据的字节
flags:设置为MSG_DONTWAITMSG 时 表示非阻塞,设置为0时 功能和read一样
返回值:成功返回实际发送的字节数,失败:返回 -1
8.sendto()、recvfrom()函数(UDP收发信息)
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_taddrlen);
参数:
返回值:成功则返回发送的字节数,失败返回-1。
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
参数: