计算机网络的核心是连接与通信,从底层的物理信号到上层的应用服务,各层协议协同工作
---------------------------------------------------------------------------------------
一.计算机网络分类(按范围)
1•个人区域网(PAN)
通常是几米到几十米,用于连接个人设备,如手机、平板、手表等,包括蓝牙Bluetooth、紫蜂Zigbee(低功耗设备)、红外线IrDA(遥控器)
2•局域网(LAN)
通常覆盖一个建筑物,用于小范围内实现高速数据传输,包括以太网Ethernet(有线设备),Wi-Fi(无线设备)
3•城域网(MAN)
通常覆盖一个城市,用于连接多个局域网,提供城域范围内的数据传输服务,如光纤网络
4•广域网(WAN)
通常覆盖一个国家,用于连接多个局域网和城域网,实现长距离数据传输,如互联网Internet、企业专用网
---------------------------------------------------------------------------------------
二.计算机网络分层模型
将复杂网络通信划分为若干层,每一层执行特定功能,并为上一层提供服务,两层之间通过接口交互
1•OSI模型(国际标准模型)
物理层--数据链路层--网络层--传输层--会话层--表示层--应用层
2•TCP / IP模型(实践模型)
--链路层:包含物理层和数据链路层,涉及在物理网络(通过有线与无线媒介)上的数据通信,还负责帧同步、错误检测、校正等,包括以太网、Wi-Fi、PPP协议
--互联网层:负责将数据报文段从原地址路由到目的地址,定义了在各种网络结构中如何接发数据包,定义计算机设备地址,包括IP协议
--传输层:管理端到端的通信,负责为不同主机上的应用程序提供数据传输,定义设备应用地址(端口号),包括TCP、UDP协议
--应用层:使用下层提供的服务创建用户数据,实现客户的与服务端之间的通信,包括HTTP、SSH、MQTT协议
注意:后端编程是应用层中聚焦于服务器端逻辑实现的部分,前端编程是应用层直接面向用户的界面与交互层,两者协同完成整个应用的功能
3•数据传输单位
协议数据单元(PDU):包括头部(PCI)和负载(SDU),是各层交换信息的数据单位,在网络层称为数据包、在传输层称为报文段或数据报、在数据链路层称为帧
负载(SDU):是通信协议的特定层次上传递的数据单元,数据传递到下一层会进行封装,并附加控制信息PCI,转换成PDU
头部(PCI):是PDU的元数据部分,包括传输数据的控制信息,比如地址、端口号、控制标志、协议类型等,使得网络或传输实体能理解如何处理SDU
4•IP协议(网络层)
网络通信最基础的协议之一,分配IP地址给连接到计算机网络的每个设备的唯一标识符
IPv4:从0.0.0.0到256.256.256.256
IPv6:为了解决IPv4地址数量不足问题,设计出IPv6
私有IP地址:仅在局域网中使用的IP地址,不能直接访问互联网,也无法被互联网上的设备直接访问
公有IP地址:由互联网服务提供商分配,是设备在互联网上的唯一标识
静态IP地址:手动分配,不会改变,适用于需要长期保持相同IP的设备,如服务器
动态IP地址:由服务器动态分配,每次连接网络时可能会改变,适用于普通用户设备,如手机、电脑
路由器:工作在网络层,用于连接不同的网络,将数据包传输到目的地
5•DNS协议(应用层)
域名解析协议,用于将易读的域名(www.com)转换为计算机可以识别的IP地址
递归查询:输入域名-查询本地缓存静态文件-查询本地DNS服务器--查询根域名服务器--查询顶级域名服务器(查询不到该域名IP地址才会向上递归查询)
6•数据链路层
在两个网络实体之间提供数据链路的连接,负责导线(无线或有线媒介)一端到一端的数据传输
MAC地址:是网络设备的硬件地址,用在局域网(LAN)中唯一的设备ID
ARP协议:将IP地址转换为物理MAC地址,用于局域网中已知目标设备IP地址,不知MAC地址时,获得目标设备的MAC地址(通过IP地址广播获得)
7•物理层
负责传输数据的物理通道(模拟信号、物理信号),负责比特传输、物理连接、信号编码和调制、数据速率控制、传输模式、物理拓扑
RS232协议与RS485协议:RS232常用于短距离点对点通信,如鼠标、键盘;RS485常用于长距离多点通信,如工业自动化系统
---------------------------------------------------------------------------------------
三•TCP协议
TCP:面向连接的协议,数据交换前,两个通信段必须建立连接,包含可靠传输、流量控制、拥塞控制、数据排序、端到端通信
1•报文段格式
16 位源端口号--16位目的端口号:用于区分网络服务,实现端到端通信
32位序列号:用于标识数据字节流,给每个字节分配标号,TCP能根据标号重新排序乱序到达的报文段,实现数据排序
32位确认序列号:用于接收到数据的确认,是发送方期望从对方收到的下一字节的标号,也是对之前收到的数据的确认,实现可靠传输
4位数据偏移:用于表示TCP头部长度,除了固定20字节部分,还可以包含可变长度的选项字段,将首部与数据部分区分开来
6位保留位:还未确定功能,必须为0
6位控制位:URG(紧急标志)、ACK(确认标志)、PSH(立即上传标志)、RST(链接错误标志)、SYN(建立连接标志)、FIN(断开连接标志),控制不同行为
16位窗口大小位:用于控制对方发送的数据量,以避免接收区缓冲区溢出,实现流量控制
注意:TCP连接中,双方各自维护一个缓冲区,为防止缓冲区溢出,使用滑动窗口机制控制发送方的发送速率,接收方会在返回的ACK报文段设置窗口大小位
16位TCP校验和:用于检查整个报文段的错误,实现可靠传输
16位紧急指针数据偏移量:当URG标志为1时,代表为紧急数据,表明从当前序列号开始的紧急数据的字节偏移量
选项区:可能包含的各种可选字段
填充区:TCP是在32位体系结构设计,为了内存对齐以提升效率,需要确保头部长度为32位的整数倍
2•TCP状态
-- CLOSED:初始状态和最终状态,表示没有连接,也没有正在进行通信
-- LISTEN:服务器端监听来自客户端的连接请求,等待SYN报文进入连接
-- SYN_SENT:客户端发送SYN包连接请求到服务器端,转入SYN_SENT状态等待服务器确认
-- SYN_RECEIVED:服务器端接收到客户端的SYN报文后,响应SYN+ACK报文,同时进入SYN_RECEIVED状态,代表服务器端已同意连接请求
-- ESTABLISHED:服务器端和客户端连接已建立,可以进行数据传输,三次握手成功后,双方都会进入的状态
注意:以上为TCP的连接建立过程,又称为三次握手(客户端发送SYN-服务端发送SYN和ACK-客户端发送ACK)
-- FIN_WAIT_1:当一端已完成数据传输任务后,会发送FIN报文,表示已经完成数据发送,进入该状态
-- CLOSE_WAIT:接收到对方发送的FIN报文后,进入该状态,代表连接一方没有数据发送,但是本端可能还有需要发送的数据
-- FIN_WAIT_2:在发送FIN报文后,且对方回应ACK响应后,进入该状态,等待连接一方的FIN报文
-- TIME_WAIT:接收到对方的FIN报文并发送ACK响应后,进入该状态,该状态会持续一段时间,确保对方接收到最后一个ACK报文
注意:以上为TCP连接释放过程,又称为四次挥手(客户端发送FIN-服务端发送ACK-服务端发送FIN和ACK-客户端发送ACK)
3•拥塞控制
由于网络或其他环境因素影响,导致网络传输能力受限,发送方无法在超时前收到ACK,不断重传导致进一步加剧拥堵,所以设立拥塞窗口,由发送方维护
--慢启动:每接收到一个ACK,拥塞窗口的初始大小翻倍,这一阶段窗口大小呈指数增长
--拥塞避免:当拥塞窗口大小到达设立的慢启动阈值,进入该阶段,此阶段每接收到一个ACK,窗口大小加1,此阶段窗口大小呈线性增长
--超时重传:当出现报文段的丢失,则将慢启动阈值设为当前窗口大小的一半,然后将窗口大小设为初始值,重新开启慢启动阶段
--快速重传:累计确认的方式使得某个报文段发生丢失,即便收到了后面的数据也会回应相同的ACK,当连续接收到3个重复的ACK时,判定相应的报文段丢失,立即重传
--快速恢复:当发生快速重传后,将慢启动阈值设为当前拥塞窗口大小一半,且拥塞窗口直接设置为阈值,不执行慢启动
区别:接收窗口是接收方发送给发送方的ACK报文中设置可接收数据速度,拥塞窗口是发送方认为网络可以承担的发送数据速度,实际发送速率是他俩的最小值
注意:可使用可视化工具wireshark进行三次握手、数据传输、四次挥手的消息监测
4•TCP开发常用函数
套接字作为操作系统提供的网络编程接口(API),其内部已经封装了大量的底层网络通信细节,让开发者无需直接处理复杂的协议逻辑(可分层选择协议)
套接字内部实现:TCP(连接、各种机制、释放)、UDP、地址与端口管理、数据缓冲与I/O处理、连接状态管理、错误处理与信号通知等
套接字组成:网络地址(通常使用IP)、端口号(标识设备上的特定应用或进程)、协议(如TCP与UDP定义传输规则)
4•1 单连接服务端进程(使用IPv4为例)
--定义地址结构体:使用IPv4地址结构体的定义,struct sockaddr_in server_addr,client_addr,并使用memset(&addr,0,sizeof(addr)清空内容
--填写结构体中服务端地址协议:server_addr.sin_family = AF_INET,由于定义的IPv4地址结构体,所以此处必须选用AF_INET表达使用IPv4协议地址
--填写结构体中服务端IP地址:server_addr.sin_addr.s_addr = htonl(INADDR_ANY),让服务器绑定本机的所有可用IP地址(宏定义)
注意:htonl表示字节转换,网络协议要求使用大端字节序,主机CPU可能使用小端字节序,需要将主机字节序转换为网络字节序(htonl、htons、ntohs、ntohl)
--填写结构体服务端端口号:server_addr.sin_port = htons(1234),其中htons,h表示host,n表示net,s表示short,最终实现将端口号写入地址结构体
--创建:socket(通信域,创建类型,补充协议),通信域常使用AF_INET,表示使用IPv4互联网协议;创建类型常使用SOCK_STREAM或SOCK_DGRAM,表示使用TCP或UDP协议传输;补充协议涉及硬件补充设施,使用0会自动进行选择补充协议;函数成功会返回int类型的文件描述符sockfd
--绑定地址:bind(sockfd,结构体地址指针sockaddr,结构体地址长度),将sockaddr指定的地址绑定文件描述符sockfd(此处使用&server_addr)
--服务端监听:listen(sockfd,还未接收等待连接的队列长度),将地址绑定后,可以直接进入监听状态
--获取客户端连接:accept(sockfd,接收的客户端的地址,地址长度的指针),建立连接,可以接收到客户端的地址(此处地址使用&client_addr),并返回int类型客户端文件描述符clientfd,使用该文件描述符进行数据的传输,若没有客户端连接,则服务端将挂起等待
--创建发送消息进程与接收消息线程:使用pthread_create创建线程read_from_client、write_to_client,并将客户端描述符clientfd传入线程
--接收消息线程:malloc设置接收缓冲区,使用recv接收客户端描述符的数据,即客户端传送的数据,若返回值为0则代表客户端主动关闭连接,free释放接收缓冲区
--发送消息线程:接收控制台信息使用send发送数据给客户端描述符
--收尾:使用pthread_join收尾子线程,使用close关闭服务端和客户端文件描述符
4•2 单连接客户端进程(使用IPv4为例)
--定义地址结构体:使用IPv4地址结构体的定义,struct sockaddr_in server_addr,client_addr,并使用memset(&addr,0,sizeof(addr)清空内容
--填写结构体中客户端地址协议:client_addr.sin_family = AF_INET
--填写结构体中客户端IP地址:inet_pton(AF_INET,“192.168.0.0”,&client_addr.sin_addr.s_addr),让客户端绑定特定IP地址
--填写结构体客户端端口号:client_addr.sin_port = htons(5678)
--填写结构体中服务端地址协议:server_addr.sin_family = AF_INET
--填写结构体中服务端IP地址:inet_pton(AF_INET,“0.0.0.0”,&server_addr.sin_addr.s_addr),使用时需写上服务端的IP地址
--填写结构体服务端端口号:server_addr.sin_port = htons(1234)
--创建:socket(通信域,创建类型,补充协议),通信域常使用AF_INET,表示使用IPv4互联网协议;创建类型常使用SOCK_STREAM或SOCK_DGRAM,表示使用TCP或UDP协议传输;补充协议涉及硬件补充设施,使用0会自动进行选择补充协议;函数成功会返回int类型的文件描述符sockfd
--绑定地址:bind(sockfd,结构体地址指针sockaddr,结构体地址长度),将sockaddr指定的地址绑定文件描述符sockfd(此处使用&client_addr)
--主动连接服务端:connect(sockfd,连接的服务端地址,地址长度的指针),前提需要服务端处于挂起状态,返回值可以判断是否连接成功(此处使用&server_addr)
--创建发送消息进程与接收消息线程:使用pthread_create创建线程read_from_server、write_to_server,并将客户端描述符sockfd传入线程
--接收消息线程:malloc设置接收缓冲区,使用recv接收客户端描述符的数据,即服务端传送的数据,若返回值为0则代表服务端主动关闭连接,free释放接收缓冲区
--发送消息线程:接收控制台信息使用send发送数据给客户端描述符
--收尾:使用pthread_join收尾子线程,使用close关闭客户端文件描述符
注意:TCP连接是全双工属性,套接字读写操作各自对应发送、接收缓冲区的机制,所以可以实现同时的双向传输
4•3 多连接
服务端获取多个客户端连接:套入while(1)不断accept客户端,获得clientfd并创建与其对接的线程或进程,进行收发客户端信息
客户端连接服务端:客户端可以不绑定地址,直接由系统自动分配一个空闲的端口号给客户端
---------------------------------------------------------------------------------------
四•UDP协议
UDP:面向无连接的协议,通信前不需要建立连接,直接发送数据报,传输效率高,适用于对实时性要求高但可容忍少量数据丢失的场景,如直播、游戏、视频通话等
1•区别
相比TCP,UDP不存在握手与挥手,服务端不需要listen、accept,客户端不需要connect,两端都不能确定对方是否收到消息(发送方需要知道接收方地址)
2•UDP开发常用函数
2•1 服务端进程
--定义地址结构体--填写结构体中服务端地址协议--填写结构体中服务端IP地址--填写结构体服务端端口号--创建socket--绑定地址bind
--接收数据:recvfrom(sockfd,buf,1024,0,&client_addr,地址长度),将客户端发送的数据接收到buf中
--回复数据:sendto(sockfd,buf,4,0,&client_addr,地址长度),将buf内数据发送给客户端
2•2 客户端进程
--定义地址结构体--填写结构体中服务端地址协议--填写结构体中服务端IP地址--填写结构体服务端端口号--创建socket(客户端不需要绑定地址)
--接收数据:recvfrom(sockfd,buf,1024,0,&server_addr,地址长度),将服务端发送的数据接收到buf中
--发送数据:sendto(sockfd,buf,4,0,&server_addr,地址长度),将buf内数据发送给服务端
---------------------------------------------------------------------------------------
五.Socket IPC(进程通信)
专门处理同一台机器上的进程间通信,属于本地通信(不用走网卡)
1•定义结构体套接字
struct sockaddr_un socket_addr、client_addr;memset(&socket_addr,0,sizeof(socket_addr))
2•服务端流程
--创建套接字:socket(AF_UNIX,SOCK_STREAM,0),返回服务端描述符sockfd
--绑定路径:socket_addr.sun_family = AF_UNIX; strcpy(socket_addr.sun_path,unix_domain.socket),不同进程通过unix_domain.socket路径找到套接字
--绑定本地地址:bind(sockfd,&socket_addr,sizeof(socket_addr)),使套接字与该路径关联
--监听:listen(sockfd,5)
--接收:accept(sockfd,&client_addr,sizeof(client_addr)),返回客户端描述符connfd
3•客户端流程
--创建套接字:socket(AF_UNIX,SOCK_STREAM,0),返回客户端描述符sockfd
--绑定路径:client_addr.sun_family = AF_UNIX; strcpy(client_addr.sun_path,unix_domain.socket),和服务器端绑定相同路径才能找到
--连接:connect(sockfd,&client_addr,sizeof(client_addr))
4•收尾
--close(sockfd)、close(connfd)、服务器需要unlink(socket_addr.sun_path),避免下次绑定报错