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()--->listen()-->accept()-->recv()-->close()
创建套接字-->关联接口地址-->-->收、发-->关闭
client:socket()-->connect()-->send()-->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);
}
接收函数/发送函数
read()/write () ///通用文件读写,可以操作套接字。
recv(,0) /send(,0) ///TCP 常用套机字读写
recvfrom()/sendto() ///UDP 常用套接字读写
ssize_t recv(int sockfd, void *buf, size_t len,
int flags);
功能:从指定的sockfd套接字中以flags方式获取长度
为len字节的数据到指定的buff内存中。
参数:sockfd
如果服务器则是accept的返回值的新fd
如果客户端则是socket的返回值旧fd
buff 用来存储数据的本地内存,一般是数组或者
动态内存。
len 要获取的数据长度
flags 获取数据的方式,0 表示阻塞接受。
返回值:成功 表示接受的数据长度,一般小于等于len
失败 -1;
close()
5、close() ===>关闭指定的套接字id;
注意事项:
服务器:
需要先接收客户端的地址(recvfrom),不然无法发送数据
typedef struct sockaddr *(SA);
socklen_t cli=sizeof(cli);
recvfrom(sockfd,buf_r,sizeof(buf_r),0,(SA)&cli,len_cli);
客户端:
需要空发一下让服务器拿到(对于收发先后没有太大要求,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);