计算机网络的构成
计算机网络是由硬件、软件、协议(一系列规则和约定的集合)共同组成
细分可以分解为:
终端设备:
指的是计算机网络的用户端(客户端),包括个人计算机,笔记本电脑,手机等,这些设备通过网络连接到其他设备,并使用各种应用程序进行通信,资源共享和数据处理
服务器:
是网络上提供服务的主要设备。如网页服务器,文件服务器,数据库服务器等,由他们存储和管理资源,并相应终端设备的请求,提供所需的服务和数据
交换设备:
简单来讲指的是路由器或者交换机,用于在网络中转发数据包,交换机是在局域网内交换数据包,而路由器是在不同网络之间转发数据包,确保能沿着正确的路径到达目标
传输媒体:
传输媒体指的是数据在网络传输中的物理途径,包括导线(铜丝),光纤,无线电波等,不同的传输媒体具有不同的传输速度和传输距离
协议:
协议是设备之间通信的规则和约定,网络使用多种协议来控制数据包的形式,传输方式,错误检测和纠正等细节,常见的网络协议包括TCP/IP、HTTP,SMTP等
分层模型
分层是一种将复杂系统,结构或设计分为不同层次或级别的方法,每个层次都有特定的功能和职责,各层次之间通过自定义接口相互交互,形成一个有序的整体
分层设计的主要优势:
模块化:
分层设计将系统划分为相对独立的模块或层次,使得开发、维护和测试变得更加可管理和灵活
隔离性:
每个层次的功能是相对独立的,这意味着在更改一个层次时不会影响到其他层次,这种隔离性有助于降低风险,简化了系统的维护
标准化:
分层设计通常要求定义清晰的接口和协议,这使得不同层次之间可以交流合作,标准化有助于实现互操作性和组件的可替换性
可扩展性:
分层设计使得系统可以按需进行扩展,新的功能可以添加到特定层次而不必影响其他部分
理解和教育:
分层设计使得系统的结构和功能变得更加清晰和易于理解,它可以帮助新的开发人员更快的熟悉系统,一边更好理解各个组成部分之间的关系
OSI七层模型
国际标准化组织(ISO)发布的用于描述计算机网络通信过程的标准化体系结构
称为OSI七层模型
OSI七层模型详述:
(接收过程:物理层>>应用层)(发送过程:应用层>>物理层)
物理层:
此为网络的最底层,涉及到物理介质和电信号的传输,负责将比特流(0和1)传输到网络媒体上,如电缆,光纤等。物理层处理电压,电流,频率等电气特性,以及连接器,接口和传输速率等细节
数据链路层:
数据链路层负责在直接相连的节点之间传输数据,它将比特流组织成帧,处理帧的开始和结束,还负责错误的检测和处理(处理方式为丢弃原包,请求重新发送新包),以确保数据的可靠传输
tip:当出现较大文件传输时,数据链路层会将文件数据封装成帧(数据块前增加一个标志位,数据块后增加一个标志位使将其包裹起来形成帧),分成多块,后再数据后增加一个标志位负责检查错误,假设传输的内容是1010,就在后面增加检验标志位2(1+0+1+0)如果在传输后发现内容加和不等于2则代表这段数据出现错误,将此段文件数据丢弃并且请求重新发送数据包
网络层:
网络层负责数据的路由和转发以及在多个网络之间的数据传输,它通过逻辑寻址(如IP地址)将数据从源节点传输到目标节点,处理分组传输和路由选择,确保数据在网络中能正确到达
传输层:
传输层提供端到端的数据传输服务,确保数据的可靠传输和有序交付。它处理数据分割,流量控制,拥塞控制和错误恢复,以确保数据在网络中的高效传输
会话层:
会话层负责建立、管理和终止会话,即通信双方间的逻辑连接,它处理会话控制、同步、数据交换和故障恢复,以确保通信的顺利进行
表示层:
表示层处理数据的格式转换、加密、压缩和解压缩,以确保在不同系统之间的数据交换时能够正确解释和处理数据,它还负责数据的语法和语义转换
应用层:
应用层是最高层,直接面向用户的应用程序,它为用户提供了网络服务,如电子邮件,文件传输,远程访问等,应用层协议定义了应用程序和网络之间的接口
TCP / IP 四层模型
TCP / IP 将OSI模型简化为四层,分别为
应用层(应用层、会话层,表示层)
传输层
网络层
通信链路层(物理层、数据链路层)
IP地址与端口号
IP协议工作在网络层 TCP协议工作在传输层
网络层通过逻辑寻址(IP地址)将数据从源节点传输到目标节点,处理分组传输和路由选择,确保数据在网络中正确到达
端口号
在传输后期,假如数据通过逻辑寻址(IP地址)找到了唯一标识的主机,但是如果这台主机上同时安装了QQ或者微信两款或者多款接收信号,那么该数据如何区分自己应该表示在哪个APP上?
IP地址可以在整个网络中唯一标识一台主机,端口号可以在一台主机上唯一标识一个进程,这样我们就可以用“IP地址+端口号”在网络中唯一标识一个进程,有了唯一标识,也就有了被找到的可能
IPv4地址是一个4字节(32bit)的整数,端口号是一个2字节(16bit)的整数
IP地址的分类
IP地址分类为五种类型,分别为A类、B类、C类、D类、E类
A类:0开头+7位网络号 + 24(8+8+8)位主机号 网络号 : 主机号 = 8 : 24
B类:10开头+14位网络号 + 16(8+8)位主机号 网络号 : 主机号 = 16 : 16
C类:110开头+21位网络号 + 8位主机号 网络号 : 主机号 = 24 : 8
D类:1110开头 组播地址
E类:1111开头 留待后用
A类最大主机数:2^24 16777214
B类最大主机数:2^16 65534
C类最大主机数:2^8 254
无分类地址CIDR
子网划分:
未做子网划分的IP地址:网络地址+主机地址
做子网划分后的IP地址:网络地址+子网网络地址+子网主机地址
习题解析
1 通过子网数来算子网掩码
需将B类IP地址167.194.0.0划分成28个子网:
B类IP地址中的结构构成为16网络号+16主机号
可以将此IP地址子网掩码拆分为255.255.00000000.00000000
划分为28个子网,但是没有正好等于28的二进制次方数,所以要找最接近的
2^5 = 36 > 28
所以要在子网掩码主机号中改变前五个0为1也就是
255.255.11111000.00000000 = 255.255.248.0
2 通过主机数来计算子网掩码
将B类IP地址167.194.0.0划分成若干个子网,每个子网内有主机500台
B类子网掩码缺省:255.255.0.0
每个子网有主机500台(2^9 = 512)
B类主机号有16位,所以16 - 9 = 7
主机号前七位应为1
所以子网掩码可以表示为255.255.11111110.0 = 255.255.254.0
3 利用子网掩码计算中最大有效子网数
一个A类IP地址,子网掩码为255.254.0.0,它所能划分的最大有效子网数是多少
A类IP地址的子网掩码缺省为:255.0.0.0
题目中给定子网掩码为255.224.0.0,将主机号部分化为二进制为
11111111.11100000.00000000.00000000,子网号中有3个1,所以最大有效子网数为2^3=8个
4 利用子网掩码计算最大可用主机数
A类IP地址,子网掩码为255.252.0.0,将它划分成若干个子网络,每个子网络中可用主机数有多少
A类IP地址的子网掩码缺省为255.0.0.0
题中给出的子网掩码可以写为11111111.11111100.00000000.00000000
其中主机位有18个0,所以每个子网络中可用主机数为2^18 - 2 = 262142个
(注意去掉网段地址和广播地址)
5 利用子网掩码确定子网络的起止位置
B类IP地址172.16.0.0,子网掩码为255.255.192.0,它能划分的子网络起止地址为多少
B类IP地址子网掩码缺省为:255.255.0.0
题中所给出的子网掩码可以写为11111111.11111111.11000000.00000000
由于子网掩码主机位中有两个1,所以可以知道一共可以分配四种子网络
四种子网络一共可以容纳2^14 = 16384个主机(主机位有14个1的时候最大数量)
每个子网可以分配16384 / 255 = 64台主机
所以网络起止可以分为:
a 172.16.0.0 - 172.16.63.255
b 172.16.64.0 - 170.16.127.255
c 172.16.128.0 - 170.16.191.255
d 172.16.192.0 - 170.16.255.255
6
需将A类IP地址67.0.0.0划分成245个子网,求子网掩码
A类地址子网掩码缺省为255.0.0.0
需要划分为245个子网,由于2^8 = 256,所以子网号中要存储8个1
也就是11111111.11111111.00000000.00000000 = 255.255.0.0
7
需将A类IP地址67.0.0.0划分成若干个子网,每个子网内有主机30000台,求子网掩码
A类子网掩码缺省为255.0.0.0 = 11111111.00000000.00000000.00000000
需要使每个子网内有主机30000台
由于2^15 = 32768 > 30000
也就是在主机号中最后15位全部置0,则前24 - 15 = 9位为1
也就是11111111.11111111.10000000.00000000 = 255.255.128.0
8
A类IP地址,子网掩码为255.254.0.0,它所能划分的最大有效子网数是多少
由于是A类地址,子网掩码缺省为255.0.0.0 = 11111111.00000000.00000000.00000000
题目中给的子网掩码为255.254.0.0可以写为11111111.11111110.00000000.00000000
则最大有效子网数为2^7 = 128个
9
A类IP地址,子网掩码为255.224.0.0,将它划分成若干子网络,每个子网络中可用主机数为多少
由于是A类IP地址,子网掩码缺省为255.0.0.0
题目中给的子网掩码为255.224.0.0可以写成11111111.11100000.00000000.00000000
可用主机数 = 2^21 - 2个
------------------------------------------单线程非阻塞--------------------------------------------------------------------
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <arpa/inet.h>
#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
#define CLIEN_MAX 1024
int main(int argc, char* argv[]){
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd == -1){
perror("socket error");
exit(1);
}
struct sockaddr_in serveraddr, clientaddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVER_PORT);
serveraddr.sin_addr.s_addr = INADDR_ANY;
int ret = bind(lfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
if(ret == -1){
perror("bind error");
exit(1);
}
ret = listen(lfd, 64);
if(ret == -1){
perror("listen error");
exit(1);
}
socklen_t addr_len = sizeof(clientaddr);
int cfd = -1;
char buf[BUFFER_SIZE];
int read_ret = 0;
int cfds[CLIEN_MAX];
char clientip[32];
for(int i = 0; i < CLIEN_MAX; i++){
cfds[i] = -1;
}
int flags = fcntl(lfd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(lfd, F_SETFL, flags);
while(1){
cfd = accept(lfd, (struct sockaddr*)&clientaddr, &addr_len);
if(cfd == -1){
if(errno == EAGAIN){
}
else{
perror("accept error");
exit(1);
}
}
else if(cfd > 0){
printf("鏈夋柊鐨勫鎴风寤虹珛杩炴帴: IP = %s, PORT = %d\n", inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientip, sizeof(clientip)), ntohs(clientaddr.sin_port));
int flags = fcntl(cfd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flags);
for(int i = 0; i < CLIEN_MAX; i++){
if(cfds[i] == -1){
cfds[i] = cfd;
break;
}
}
}
for(int i = 0; i < CLIEN_MAX; i++){
if(cfds[i] < 0){
continue;
}
read_ret = read(cfds[i], buf, BUFFER_SIZE);
if(read_ret < 0){
if(errno == EAGAIN){
continue;
}
perror("read error");
exit(1);
}
else if(read_ret == 0){
printf("瀹㈡埛绔柇寮€杩炴帴 cfd = %d\n", cfds[i]);
close(cfds[i]);
cfds[i] = -1;
continue;
}
else{
write(STDOUT_FILENO, buf, read_ret);
write(cfds[i], buf, read_ret);
}
}
}
return 0;
}
select I/O复用
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/select.h>
#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
int main(int argc, char* argv[]){
int lfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字
if(lfd == -1){
perror("socket error");
exit(1);
}
struct sockaddr_in serveraddr,clientaddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVER_PORT);
serveraddr.sin_addr.s_addr = INADDR_ANY;
//绑定套接字
int ret = bind(lfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(ret == -1){
perror("bind error");
exit(1);
}
//将服务器从主动变为被动
ret = listen(lfd,64);
if(ret == -1){
perror("listen error");
exit(1);
}
socklen_t addr_len = sizeof(clientaddr);
int cfd = -1;
char buf[BUFFER_SIZE];
int read_ret = 0;
char clientip[32];
fd_set all_set,read_set;
//初始化读位图
FD_ZERO(&all_set);
//将lfd添加进all_set集合
FD_SET(lfd,&all_set);
int nfds = lfd + 1;
int select_ret = 0;
struct timeval timeout;
//timeout.tv_sec = 5;
//timeout.tv_usec = 50000;
while(1){
read_set = all_set;
select_ret = select(nfds,&read_set,NULL,NULL,&timeout);
if(select_ret < 0){
perror("select error");
exit(1);
}
else if(select_ret == 0){
//printf("超时时间已到,没有事件触发\n");
//sleep(2);
//continue;
}
else{
//printf("timeout.tv_set = %lu, timeout.tv_usec = %lu\n",timeout.tv_sec,timeout.tv_usec);
if(FD_ISSET(lfd,&read_set)){
cfd = accept(lfd,(struct sockaddr*)&clientaddr,&addr_len);
if(cfd == -1){
perror("accept error");
exit(1);
}
else if(cfd > 0){
printf("有新的客户端建立连接:IP=%s, PORT=%d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientip,sizeof(clientip)),ntohs(clientaddr.sin_port));
FD_SET(cfd,&all_set);
if(cfd > (nfds - 1)){
nfds = cfd + 1;
}
if(--select_ret == 0){
continue;
}
}
}
for(int i = lfd + 1;i < nfds;i++){
if(FD_ISSET(i,&read_set)){
read_ret = read(i,buf,BUFFER_SIZE);
if(read_ret < 0){
perror("read error");
exit(1);
}
else if(read_ret == 0){
printf("客户端断开连接\n");
FD_CLR(i,&all_set);
close(i);
continue;
}
else{
write(STDOUT_FILENO,buf,read_ret);
write(i,buf,read_ret);
}
if(--select_ret == 0){
break;
}
}
}
}
}
}
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/epoll.h>
#define SERVER_PORT 8000
#define BUFFER_SIZE 1024
#define EVENT_SIZE 1024
int main(int argc, char* argv[]){
int lfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字
if(lfd == -1){
perror("socket error");
exit(1);
}
struct sockaddr_in serveraddr,clientaddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVER_PORT);
serveraddr.sin_addr.s_addr = INADDR_ANY;
int ret = bind(lfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(ret == -1){
perror("bind error");
exit(1);
}
ret = listen(lfd,64);
if(ret == -1){
perror("listen error");
exit(1);
}
socklen_t addr_len = sizeof(clientaddr);
int cfd = -1;
char buf[BUFFER_SIZE];
int read_ret = 0;
char clientip[32];
int epoll_ret = 0;
int epfd = epoll_create(64); //创建epoll对象
struct epoll_event event; //创建结构体对象
struct epoll_event events[EVENT_SIZE]; //创建结构体数组
event.events = EPOLLIN; //设置监听事件为读事件
event.data.fd = lfd; //设置监听文件描述符
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&event); //将lfd增加到epoll对象里
while(1){
epoll_ret = epoll_wait(epfd,events,EVENT_SIZE,0);
if(epoll_ret < 0){
perror("epoll error");
exit(1);
}
else if(epoll_ret == 0){
}
else{
for(int i = 0; i < epoll_ret;i++){
int fd = events[i].data.fd;
if(events[i].events & EPOLLIN){
if(fd == lfd){ //判断是否是通信套接字
cfd = accept(lfd,(struct sockaddr*)&clientaddr,&addr_len);
printf("有新的客户端建立连接:IP=%s, PORT=%d\n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientip,sizeof(clientip)),ntohs(clientaddr.sin_port));
if(cfd < 0){
perror("accept error");
exit(1);
}
else{
event.events = EPOLLIN;
event.data.fd = cfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&event);
}
}
else{ //客户端套接字
read_ret = read(fd,buf,BUFFER_SIZE);
if(read_ret < 0){
perror("read error");
exit(1);
}
else if(read_ret == 0){
printf("客户端断开连接\n");
epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
close(fd);
}
else{
write(STDOUT_FILENO,buf,read_ret);
write(fd,buf,read_ret);
}
}
}
}
}
}
}
-------------------------------------------------需 看---------------------------------------------------------------------