1.网络发展史
【腾讯文档】internet历史
APRNET ---internet----移动互联网----物联网(万物互联)IOT Internet of Things
2.局域网和广域网
2.1局域网(LAN)
局域网的缩写是LAN,local area network,顾名思义,是个本地的网络,只能实现小范围短距离的网络通信。我们的家庭网络是典型的局域网。电脑、手机、电视、智能音箱、智能插座都连在路由器上,可以互相通信。局域网,就像是小区里的道路,分支多,连接了很多栋楼。
2.2广域网(Wan)
广域网(Wide Area Network)是相对局域网来讲的,局域网的传输距离比较近,只能是一个小范围的。如果需要长距离的传输,比如某大型企业,总部在北京,分公司在长沙,局域网是无法架设的。广域网,就像是大马路,分支可能少,但类型多,像国道、省道、高速、小道等,连接了很多大的局域网。
这时需要其它的解决方案。
第一,通过因特网,只需要办一根宽带,就实现了通信,非常方便,现在的宽带价格也比较便宜。
第二,通过广域网专线。
所以为了数据安全,不能连接因特网,需要用一条自己的专用线路来传输数据,这条线路上只有自己人,不会有其他人接入,且距离很远,这个网络就叫 “广域网”。
2.3.家庭用网
2.3.1.光猫
光猫是一种类似于基带modem(数字调制解调器)的设备,和基带modem不同的是接入的是光纤专线,是光信号。用于广域网中光电信号的转换和接口协议的转换,接入路由器,是广域网接入。
将光线插入左侧的灰色口,右侧网口接网线到路由器即可。
2.3.2交换机与路由器
交换机(二层):用于局域网内网的数据转发路由器(三层):用于连接局域网和外网
路由器有二层交换机的功能,反之不成立,二层交换机没有IP分配和IP寻址的功能。
交换机各个口是平等的,所有接入的设备需要自己配置IP,然后组成局域网。
路由器需要区分WAN口和LAN口,WAN口是接外网的(从Modem出来的或者从上一级路由器出来的),LAN口是接内网的,现在路由器都带无线功能,本质上无线接入就是LAN。
3.设备通信的要素
思考:你通过QQ发送一条消息给你的好友,最终谁处理了这条信息?
答:数据通过腾讯服务器的转发最终会到达好友主机里的QQ
找主机依赖的是:IP地址
找QQ软件进程依赖的是端口号
4.IP地址
4.1基本概念
- IP地址是Internet中主机的标识
- Internet中的主机要与别的机器通信必须具有一个IP地址
- IP地址为32位(IPv4)或者128位(IPv6)
- 表示形式:常用点分十进制形式,如202.38.64.10,最后都会转换为一个32位的无符号整数。
0000 0011.0000 0001.0000 0001.0000 0010
3.1.1.2
五类:A B C D E
4.2ip地址划分(IPv4)
二级划分 ip=网络号+主机号
网络号:表示是否在一个网段内(局域网)
主机号:标识在本网段内的ID,同一局域网不能重复
主机号的第一个和最后一个都不能被使用,第一个作为网段号,最后一个最为广播地址。
A类:1.0.0.1~126.255.255.254
B类:128.0.0.1~~191.255.255.254
C类:192.0.0.1~~223.255.255.254
D类(组播地址):224.0.0.1~~239.255.255.254
E类:保留待用 11110
4.3特殊地址
0.0.0.0:在服务器中,0.0.0.0指的是本机上的所有IPV4地址,如果一个主机有两个IP地址,192.168.1.1 和 10.1.2.1,并且该主机上的一个服务监听的地址是0.0.0.0,那么通过两个ip地址都能够访问该服务。
127.0.0.1:回环地址/环路地址,所有发往该类地址的数据包都应该被loop back。
5.NAT
5.1NAT技术出现的背景
所谓ip地址,就是用来识别每一台设备的标志,因此每台设备都应该有一个唯一不重复的地址,就好像如果很多人的地址都一样,那么快递员就不知道该把包裹送给谁了。
网络也是一样,本来互联网中所有的设备都应该有自己的固定地址,而且最早也确实是这样做的。那个时候没有内网和外网的区别,所有客户端都是直接连接到互联网的。
进入 20 世纪 90 年代之后,互联网逐步向公众普及,接入互联网的设备数量也快速增长,如此一来,情况就发生了变化。如果还用原来的方法接入,过不了多久,可分配的地址就用光了。
如果不能保证每台设备有唯一不重复的地址,就会从根本上影响网络包的传输,这是一个非常严重的问题。如果任由这样发展下去,不久的将来,一旦固定地址用光,新的设备就无法接入了,互联网也就无法继续发展了。
解决这个问题的关键在于固定地址的分配方式。
举例:假如有 A、B 两家公司,它们的内网是完全独立的。这种情况下,两家公司的内网之间不会有网络包流动,即使 A 公司的某台服务器和 B 公司的某台客户端具有相同的 IP 地址也没关系,因为它们之间不会进行通信。只要在每家公司自己的范围内,能够明确判断网络包的目的地就可以了,是否和其他公司的内网地址重复无关紧要,只要每个公司的网络是相互独立的,就不会出现问题。
5.2私有地址
解决地址不足的问题,利用的就是这样的性质,即公司内部设备的地址不一定要和其他公司不重复。这样一来,公司内部设备就不需要分配固定地址了,从而大幅节省了 IP 地址。
当然,就算是公司内网,也不是可以随便分配地址的,因此需要设置一定的规则,规定某些地址是用于内网的,这些地址叫作私有地址,而原来的固定地址则叫作公有地址。
私有地址的规则其实并不复杂,在内网中可用作私有地址的范围仅限
以下这些。
10.0.0.0 ~ 10.255.255.255
172.16.0.0 ~ 172.31.255.255
192.168.0.1 ~ 192.168.255.254
在制定私有地址规则时,这些地址属于公有地址中还没有分配的范围。换句话说,私有地址本身并没有什么特别的结构,只不过是将公有地址中没分配的一部分拿出来规定只能在内网使用它们而已。这个范围中的地址和其他公司重复也没关系,所以对于这些地址不作统一管理,不需要申请,任何人都可以自由使用。当然,如果在公司内部地址有重复就无法传输网
络包了,因此必须避免在内网中出现重复的地址。
尽管这样的确能节省一部分地址,但仅凭这一点还无法完全解决问题。公司内网并不是完全独立的,而是需要通过互联网和其他很多公司相连接,所以当内网和互联网之间需要传输包的时候,问题就出现了,因为如果很多地方都出现相同的地址,包就无法正确传输了。
于是,当公司内网和互联网连接的时候,需要采用下图这样的结构,即将公司内网分成两个部分,一部分是对互联网开放的服务器,另一部分是公司内部设备。其中对互联网开放的部分分配公有地址,可以和互联网直接进行通信,这一部分和之前介绍的内容是一样的。相对地,内网部分则分配私有地址,内网中的设备不能和互联网直接收发网络包,而是通过一种特别的机制进行连接,这个机制就叫地址转换(NAT,Network Address Translation)。
5.3NAT的基本原理
地址转换的基本原理是在转发网络包时对 IP 头部中的 IP 地址和端口号进行改写
DHCP服务器和NAT设备是网络中两种不同的功能,分别负责不同的任务。以下是它们的主要区别:
5.3.1DHCP服务器(动态主机配置协议)
- 功能:DHCP服务器的主要任务是自动分配IP地址、子网掩码、默认网关和DNS服务器ip等网络配置给网络中的设备(客户端),简化网络管理。
- 工作原理:
- 当一个设备(如电脑或手机)首次连接到网络时,它会发送一个DHCP请求。
- DHCP服务器接收到请求后,从预设的IP地址池中分配一个可用的IP地址,并返回给 客户端。
- 作用对象:DHCP服务器专注于局域网内部的IP地址管理。
5.3.2. NAT设备(网络地址转换)
- 功能:NAT设备的主要功能是将内部私有IP地址转换为公共IP地址,或反向操作,以便在局域网和外部网络(如互联网)之间进行通信。
- 工作原理:
- 当局域网内的设备访问互联网时,NAT设备记录设备的内部IP地址和源端口,并用公共IP地址替换。
- 响应的流量也会经过NAT设备,NAT通过记录的映射关系将数据包发送回正确的内部设备。
- 作用对象:NAT设备用于管理与外部网络(如互联网)的通信,通常作为路由器的一部分。
5.3.3总结
- 职责不同:DHCP服务器主要负责IP地址的分配,而NAT设备负责地址转换和流量转发。
- 使用场景不同:DHCP通常在局域网内部使用,NAT则主要用于连接局域网与外部网络。
6.子网掩码
网络号对应位全为1,主机号对应位全为0;
6.1子网掩码的格式
与IP地址一样长的32位整数,由一串连续的1,后面跟着一串连续的0组成。
默认子网掩码中,1的个数与IP地址中网络号的个数一致。0的个数与IP地址中主机号的 个数一致。
A类:8位网络号,24位主机号
1111 1111.0000 0000.0000 0000.0000 0000
255.0.0.0
B类:255.255.0.0
C类:255.255.255.0
6.1作用
6.1.1计算网段和主机ID
192.168.50.58
1100 0000.1010 1000. 0011 0010.0011 1010 x&1=x;x&0=0;
& 1111 1111.1111 1111. 1111 1111.0000 0000
1100 0000 1010 1000 0011 0010 0000 0000
192.168.50.0
ip&子网掩码=网络号
1100 0000.1010 1000. 0011 0010.0011 1010 x&1=x;x&0=0;
& 0000 0000.0000 0000. 0000 0000.1111 1111
0.0.0.58
ip&(~子网掩码)=主机号
6.1.2划分子网
假设 : 1000台计算机接入同一局域网,要求不能浪费ip
每一类IP地址所能盛放的主机数
C类最多254台:2^8-2=254
B类最多65534台 2^16-2=65536-2=65534台
如何实现?
随机选择一个子网号 101010
选择主机号2
生成随机IP:128.0.168.2
子网掩码为:255.255.252.0
ip&子网掩码
128. 0.1010 1000.0000 0010
& 255.255.1111 1100.0000 0000
128.0.10101000.0000 0000=128.0.168.0
7.TCP和UDP
共同点:都工作在传输层
TCP:传输控制协议
TCP(Transmission Control Protocol)是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)
适用情况:
1、适合于对传输质量要求较高,以及传输大量数据的通信。
2、在需要可靠数据传输的场合,通常使用TCP协议
3、MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议
缺点:
发送量较大,效率低
UDP :用户数据报协议
UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。
适用情况:
1、发送小尺寸数据(如对DNS服务器进行IP地址查询时)
2、在接收到数据,给出应答较困难的网络中使用UDP。
3、适合于广播/组播式通信中。
4、流媒体、VOD、VoIP、IPTV等网络多媒体服务中通常采用UDP方式进行实时数据传输
8.编程预备知识
8.1socket简介
1》1982 - Berkeley Software Distributions 操作系统引入了socket作为本地进程之间通信的接口
2》1986 - Berkeley 扩展了socket 接口,使之支持UNIX 下的TCP/IP 通信
3》现在很多应用 (FTP, Telnet) 都依赖这一接口
Socket
1、是一个编程接口,API---->函数Socket();
2、是一种特殊的文件描述符 (everything in Unix is a file)
3、并不仅限于TCP/IP协议
4、面向连接 (Transmission Control Protocol - TCP)
5、无连接 (User Datagram Protocol -UDP )
为什么需要Socket?
普通的I/O操作过程
•打开文件->读/写操作->关闭文件
•TCP/IP协议被集成到操作系统的内核中,引入了新型的“I/O”操作
•进行网络通信的两个进程在不同的机器上,如何连接?
•网络协议具有多样性,如何进行统一的操作
需要一种通用的网络编程接口:Socket
8.2socket类型
流式套接字(SOCK_STREAM) TCP
提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
数据报套接字(SOCK_DGRAM) UDP
提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
原始套接字(SOCK_RAW)
可以对较低层次协议如IP、ICMP直接访问。
8.3端口号 port
- 为了区分一台主机接收到的数据包应该转交给哪个进程来进行处理,使用端口号来区分
- TCP端口号与UDP端口号独立
- 端口用两个字节来表示 2byte 16bit
众所周知端口:1~1023(1~255之间为众所周知端口,256~1023端口通常由UNIX系统占用)
已登记端口:1024~49151 (选1000以上10000以下)
动态或私有端口:49152~65535
套接字创建以后,套接字对应的端口号可以指定,如果不指定,则随机分配
8.4字节序(大小端)
发送时需要将主机字节序转换成网络字节序(大端)
- 小端字节序:低序字节存储在低地址上;
- 大端字节序:低序字节存储在高地址上;
注意:数据的读取顺序都是从低地址往高地址读取,通过大小端转换得到结果。
面试题:写一个函数,判断当前主机的字节序?
8.4.1主机字节序到网络字节序
u_long htonl (u_long hostlong);
u_short htons (u_short short); //掌握这个
8.4.2网络字节序到主机字节序
u_long ntohl (u_long hostlong);
u_short ntohs (u_short short);
8.5IP地址转换
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
in_addr_t inet_addr(const char *cp); //从人看的到机器识别的32位无符
号整数
char * inet_ntoa(struct in_addr in); //从机器到人
192.168.50.58
- 将点分十进制转换成32位无符号整数
- 改变字节序
执行效果
9.TCP编程
C/S 客户端服务器架构
9.1流程
1.通信是基于套接字进行的通信,所以首先要有一个套接字 Socket();
2.服务器为了便于客户端寻找到自己,所以服务器要对外公开自己的IP和端口号,公开的前提是已经指定好了IP和端口号,这一步通过函数bind实现,名为绑定
3.每次连接都是客户端给服务器发送连接请求,而服务器套接字接收连接请求的前提是自己要进行变身,从主动套接字变为被动套接字,然后就可以等待连接请求发送过来了,此步由listen函数实现,名为监听。
4.如果接收到连接请求并评估可以建立连接,则产生一个新的套接字,这一步由accept函数实现,accept是一个阻塞函数,当没有连接可以建立的时候就阻塞住,有连接建立,就解除阻塞,同时创建一个新的套接字,此套接字用于通信,该套接字是accept的返回值
5.接收和发送,其实就是对通信的套接字进行读写
6.回收资源
9.2函数接口
9.2.1socket
int socket(int domain, int type, int protocol);
功能:创建套接字
参数:
domain:协议族
AF_UNIX, AF_LOCAL 本地通信
AF_INET ipv4
AF_INET6 ipv6
type:套接字类型
SOCK_STREAM:流式套接字----TCP
SOCK_DGRAM:数据报套接字---UDP
protocol:协议 - 填0 自动匹配底层 ,根据type系统默认自动帮助匹配对应协议
返回值:
成功 文件描述符
失败 -1,更新errno
9.2.2bind
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
功能:绑定 ipv4 ip和端口
参数
sockfd:文件描述符
addr:通用结构体,根据socket第一个参数选择的通信方式最终确定这需要真正填充传递的结构体是那个类型。强转后传参数。
addrlen:填充的结构体的大小
返回值:成功0 失败-1、更新errno
通用结构体:相当于预留一个空间
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
ipv4的结构体
struct sockaddr_in {
sa_family_t sin_family; //协议族AF_INET
in_port_t sin_port; //端口
struct in_addr sin_addr;
};
struct in_addr {
uint32_t s_addr; //IP地址
};
ipv6通信结构体:
struct sockaddr_in6 {
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
struct in6_addr {
unsigned char s6_addr[16];
};
//如果绑定使用通用地址可使用 INADDR_ANY宏(结合代码讲的时候再添加)
含义是自动绑定所有本机网卡的地址,
因为bind适用于IPV4、ipv6等网络通信,又由于不同的网络通信需要填充的结构体不一样,但是编译器要求第二参数数据类型固定,为避免编译器警告,所以我们使用了一个通用结构体来兼容所有的结构体,填充自己协议对应的结构体,然后强转传参即可。
9.2.3listen
int listen(int sockfd, int backlog);
功能:监听,将主动套接字变为被动套接字
参数:
sockfd:套接字
backlog:同时响应客户端请求链接的最大个数,不能写0.
不同平台可同时链接的数不同,一般写6-8个
返回值:成功 0 失败-1,更新errno
9.2.4accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept(sockfd,NULL,NULL);
阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,
则accept()函数返回,返回一个用于通信的套接字文件;
参数:
Sockfd :套接字
addr: 链接客户端的ip和端口号
如果不需要关心具体是哪一个客户端,那么可以填NULL;
addrlen:结构体的大小
如果不需要关心具体是哪一个客户端,那么可以填NULL;
返回值:
成功:文件描述符; //用于通信
失败:-1,更新errno
9.2.5recv
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能: 接收数据
参数:
sockfd: acceptfd ;
buf 存放位置
len 大小
flags 一般填0,相当于read()函数
MSG_DONTWAIT 非阻塞
返回值:
< 0 失败出错 更新errno
==0 表示客户端退出
>0 成功接收的字节个数
9.2.6connect
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:用于连接服务器;
参数:
sockfd:socket函数的返回值
addr:填充的结构体是服务器端的;
addrlen:结构体的大小
返回值
-1 失败,更新errno
正确 0
9.2.7send
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据
参数:
sockfd:socket函数的返回值
buf:发送内容存放的地址
len:发送内存的长度
flags:如果填0,相当于write();