OSI七层模型:
OSI 模型 --> 开放系统互联模型 --> 分为7层:
理想模型 --> 尚未实现
1.应用层 QQ
应用程序的接口
2.表示层 加密解密 gzip
将接收的数据进行解释(机器->人)
3.会话层 网络断开,连接状态,keep-close keep-alive
通信双方管理会话
4.传输层:tcp udp 协议 文件 视频,音频
(传数据)
5.网络层ip NAT
实现数据从源经过多条链路到目的地的转发(找主机)
6.链路层 交换机 数据的格式化 帧 校验
将电信号封装,建立数据链路,实现点对点数据传输
7.物理层:100Mb/8 Gbits 100MB 同轴电缆 10Gb 2.4G 5G
可通过物理介质传播的电信号
TCP/IP模型:
TCP/IP模型 --> 网际互联模型 --> 分为4层:
实用模型 --> 工业标准
1.应用层 ---> 应用程序(用户与应用程序的接口)(会话层+表示层+应用层)
2.传输层 ---> 端口号tcp udp (传数据)
3.网络层 ---> IP 地址(找主机)
4.接口层 ---> 网卡 驱动 1GB(连结互联网的基础设施)(物理层+链路层)
网络基础
IP地址 = 网络位 + 主机位
010 3333344444
IP地址的分类: 点分十进制 ipv4(4字节(32位)数据,42亿个地址,已耗尽)
ipv6(16字节(128位)数据,地址很多,未耗尽
A类: 超大规模性网络
8 8 8 8
1.0.0.0 - 126.255.255.255 126.1.1.1
126.1.1.2
255.0.0.0
私有:
10.0.0.0 - 10.255.255.255
127.0.0.1
B类: 大中规模型网络
128.0.0.0 - 191.255.255.255
128.2.1.2 128.2.7.2
255.255.0.0
私有:
172.16.0.0 - 172.31.255.255
C类: 中小规模型网络
192.0.0.0 - 223.255.255.255
255.255.255.0
私有:
192.168.0.0 - 192.168.255.255
静态路由
192.168.0.0
192.168.0.1 网关
192.168.0.255
D类: 组播和广播(广播:所有用户都能传播,组播:某个小范围组内能传播)
(无子网掩码)
224.0.0.0 - 239.255.255.255
192.168.0.255 == 255.255.255.255
235.1.2.3
192.168.1.0
192.168.0.1 网关
192.168.1.255 广播
E类: 实验
(无子网掩码)
240.0.0.0 - 255.255.255.255
子网掩码:1代表网络部分,0代表主机部分
C 类网络:
ip地址的前三组是网络地址,第四组是主机地址。
二进制的最高位必须是: 110xxxxx开头
十进制表示范围: 192.0.0.0 -223.255.255.255
默认网络掩码: 255.255.255.0
网络个数: 2^24 个 约 209 万个
主机个数: 2^8 个 254 个+2 --> 1个是网关(网络地址.0 的下一个地址.1) 另1个是广播(.255最后一个地址)
私有地址: 192.168.x.x 局域网地址。
网络接口(端口+ip --> 找到进程+找到主机)
1、socket 套接字 --> BSD socket --> 用于网络通信的一组接口函数。socket api application interface --> 进程到进程 --> 实现主机到主机通信
2、ip+port 地址+端口 --> 地址用来识别主机
端口用来识别应用程序
port分为TCP port / UDP port 范围都是: 1-65535(2^16,两个byte)
约定1000 以内的端口为系统使用。
网络字节序
--> 大端排序(高位数据放在高地址处)(高地址:值较大的地址)
--> 主机 --> 小端(高位数据放在低地址处)(从小往大走)
数字转换函数:
#include <arpa/inet.h>
主机转网络:uint32_t htonl(uint32_t hostlong);
ipv4 192.168.0.1 1~65535
uint16_t htons(uint16_t hostshort);
网络转主机:host to net long
net to host
uint32_t ntohl(uint32_t netlong); //对应16位转换与32位转换
uint16_t ntohs(uint16_t netshort);
主机转网络:in_addr_t inet_addr(const char *cp);
inet_addr("192.168.1.20");
网络转主机:char *inet_ntoa(struct in_addr in);
字符串转换函数:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
主机转网络:in_addr_t inet_addr(const char *cp);
cli.sin_addr
inet_addr("192.168.1.20");
网络转主机:char *inet_ntoa(struct in_addr in);
收发数据(UDP)
UDP:半双工,同一时刻要么收要么发
1、模式 C/S 模式 --> 服务器/客户端模型(client/server)
server:socket() ===>bind()===>recvfrom()===>close()
client:socket() ===>bind()===>sendto() ===>close()
socket()
int socket(int domain, int type, int protocol);
功能:程序向内核提出创建一个基于内存的套接字描述符
参数:domain 地址族,PF_INET(协议族) == AF_INET(地址族,IPv4) ==>互联网程序
PF_UNIX == AF_UNIX ==>单机程序
type 套接字类型:
SOCK_STREAM 流式套接字 ===》TCP
SOCK_DGRAM 用户数据报套接字===>UDP
SOCK_RAW 原始套接字 ===》IP
protocol 协议 --> 0 表示自动适应应用层协议。
返回值:成功 返回申请的套接字id
失败 -1;
bind()
2、int bind(int sockfd, struct sockaddr *my_addr,
socklen_t addrlen);
功能:如果该函数在服务器端调用,则表示将参数1相关
的文件描述符文件与参数2 指定的接口地址关联,
用于从该接口接受数据。
如果该函数在客户端调用,则表示要将数据从
参数1所在的描述符中取出并从参数2所在的接口
设备上发送出去。
注意:如果是客户端,则该函数可以省略,由默认
接口发送数据。
参数:sockfd 之前通过socket函数创建的文件描述符,套接字id
my_addr 是物理接口的结构体指针。表示该接口的信息。
struct sockaddr 通用地址结构
{
u_short sa_family; 地址族
char sa_data[14]; 地址信息
};
转换成网络地址结构如下:
struct _sockaddr_in ///网络地址结构
{
u_short sin_family; 地址族
u_short sin_port; ///地址端口
struct in_addr sin_addr; ///地址IP
char sin_zero[8]; 占位
};
struct in_addr
{
in_addr_t s_addr;
}
socklen_t addrlen: 参数2 的长度。
返回值:成功 0
失败 -1;
bind传参要进行强转,在实际使用中,struct sockaddr过于底层,不方便处理,而struct sockaddr_in专门用于IPv4地址,所以一般用struct sockaddr_in来定义
#typedef struct sockaddr * (SA);
struct sockaddr_in ser;
int ret = bind(sockfd,(SA)&ser,sizeof(ser));
一般bind的操作为:
struct sockaddr_in ser,cli;
bzero(&ser,sizeof(ser));
bzero(&cli,sizeof(cli));
// 大小端转化 host to net short
ser.sin_port = htons(50000);
ser.sin_addr.s_addr = inet_addr("192.168.203.128");
int ret = bind(sockfd,(SA)&ser,sizeof(ser));
if(-1 == ret)
{
perror("bind");
exit(1);
}
接收函数/发送函数
发送函数:
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
功能:用于UDP协议中向对方发送数据。
参数:sockfd 本地的套接字id
buff 本地的数据存储,一般是要发送的数据。
len 要发送的数据长度
flags 要发送数据方式,0 表示阻塞发送。
dest_addr: 必选,表示要发送到的目标主机信息结构体。
addrlen :目标地址长度。
返回值:成功 发送的数据长度
失败 -1;
接收函数:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
功能:用于UDP协议中获取对方发送的数据。
参数:sockfd 本地的套接字id
buff 要存储数据的内存区,一般是数组或者动态内存。
len 要获取的数据长度,一般是buff的大小。
flags 获取方式,0 阻塞
src_addr 可选,表示对方的地址信息结构体,
如果为NULL,表示不关心对方地址。(一般在用户端中,因为服务器地址一般默认
是已知恒定的,用户端是未知多变的,就是要
在服务端变量初始化中写入服务器的地址)
addrlen 对方地址信息结构体大小。
如果对方地址是NULL,则该值也为NULL。
返回值:成功 接收到的数据长度
失败 -1;
close()
5、close() ===>关闭指定的套接字id;
注意事项:
sendto与recvfrom都写的是对方的sockaddr_in结构体
服务器:
一般需要定义两个结构体,因为服务器需要知道客户端的ip
struct sockaddr_in ser,cli;
需要先接收客户端的地址(recvfrom),不然无法发送数据
typedef struct sockaddr *(SA);
socklen_t cli=sizeof(cli);
recvfrom(sockfd,buf_r,sizeof(buf_r),0,(SA)&cli,len_cli);
客户端:
一般不需要定义自己的sockadr_in结构体,且recv时可以写NULL
struct sockaddr_in ser;
recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
需要空发一下让服务器拿到(对于收发先后没有太大要求,recvfrom具有读阻塞的作用)
char buf[128];
sendto(sockfd,buf,sizeof(buf),0,(SA)&ser,sizeof(ser));
客户端recvfrom不需要最后两个参数可以是NULL,因为本来就有父进程的端口号和ip
recvfrom(sockfd,buf_r,sizeof(buf_r),0,NULL,NULL);