网络编程基础知识

发展历程

协议TCP-IP协议(传输控制协议以及因特网协议)

TCP/IP协议族体系结构

应用层:HTTP(把OSP7层体系中的会话层、表示层、应用层合并到一个应用层)

TCP协议

①面向连接

②可靠传输

③流量控制:调整发送速度

物理层与MAC地址

IPV4逻辑地址:IPV6是解决地址不够用的情况,地址枯竭问题,128位的地址

MAC地址的作用场景核心职责

局域网通信:交换机通过MAC地址表转发帧

ARP协议:将目标IP地址解析为MAC地址(局域网内)

安全过滤:网络接入控制(如MAC地址白名单)

流程:

封装帧--->在物理层中转换为电信号通过网线传输---->交换机处理将电信号还原成帧---->主机B接收信号,在物理层还原为帧;MAC子层效验为MAC地址

网络层与IP协议

IP协议

通过IP地址唯一标识设备(IPV4:32位,IPV6:128位)

ARP协议

将IP地址解析成一个MAC地址

RARP协议

将MAC地址解析为IP地址

IP数据包结构

服务类型:定义服务质量;区分流量类别

一个C类地址:192.168.80.10

可以知道它的网络号为:192.168.80.0

广播地址:192.168.80.255

网关:192.168.80.1

私有IP地址:缓解IP地址短缺的问题

A类2^24 -2 =1677万个主机数目

B类2^16 -2 =65万

C类2^8 -2 =254

子网掩码

将大网络切割成小网络:子网掩码用来区分网络部分和主机部分

运作逻辑:按位与运算

子网的主机数量:2^(32-新掩码位数) -2;

如192.168.1.0/26;主机数为2^(32-26)-2=64-2=62;

可用IP范围为:192.168.1.1~192.168.1.62 ;广播地址为192.168.1.63

IPv6

单播、组播、任播

单播:一对一;类似于IPv4

组播:一对多;取代IPv4的一个广播

任播:数据包发送到最近的一个一组设备中

示例的写法:2001:0db8:85::8a2e:0370:7374

::代表连续0

路由

路由选择:根据路由表取决定下一跳的地址

路由表的生成方式是由静态配置(管理员手动进行配置)管理

OSPF以及BGP协议

数据包转发:查表后,从一个正确的接口进行发出

NAT(网络地址转发):将私有IP转换成公有IP

路由过程示例:

电脑(私有IP)访问百度服务器(公网IP)---->这个公网IP不在本地网络;查询路由表找到默认网关----->通过ISP网络逐跳进行转发;最终到达百度服务区

传输层与端口

传输层使命:是为了不同的主机的应用进程创建逻辑通信

网络层使命:负责主机到主机的通信

传输层:端到端的通信

传输层两大核心服务:

可靠传输(TCP):不丢包、无失序、无重复到达

高效传输(UDP):不可靠、无连接

端口本质:

定义:是一个16位的整数,与IP地址共同组成套接字的一部分(socket)

示例:是IP与端口的组合:192.168.1.100:8080;占用一个8080的端口

数据库:3306

端口的核心作用

客户端通过访问不同的端口号去访问特定的一个服务(服务标识)

服务端可以通过不同的端口同时去处理多个请求(并发支持)

端口的大小:0~65535:

分为共用端口(0~1023:是系统级服务;需要申请管理员权限去访问它)

注册端口(1024~49151:是用户级的端口;需要向INA进行注册使用)

动态端口(49151~65535:临时使用于客户端)

端口22:SSH远程     80:HTTP协议  443:加密网页

TCP、UDP协议

音视频传输会使用到UDP协议;

传输层

多路复用与解复用:

发送方(复用)将不同应用的数据封装到不同端口的报文段

接收方(解复用):根据目标端口号将数据分发给正确的应用进程

Socket =IP地址 +端口号 + 协议类型

为什么要使用端口号?

由于IP的局限性(不知道设备的应用);需要门牌号;部门的分机号;

端口冲突怎么解决?

端口冲突使服务端启动失败

1、可以终止当前的端口进程

2、更换服务端端口

TCP、UDP它们的端口是否相互独立?

是独立;比如TCP的80端口与UDP的80端口它们不会发生冲突

应用层与常见架构模式

应用层提供了给我们特定应用程序与网络服务的一个通信接口

核心特性:

面向用户进行操作;

不同服务对应不同协议(DNS用于域名解析,webSocket用于实时通信)

HTTP:用于web服务器与web浏览器之间相互传输超文本数据;实现浏览器去访问万维网;使用80端口/基于TCP协议实现

HTTPS:基于加密的网页进行传输;基于43端口/TCP协议

FTP:用于在网络上传输文件的协议;文件共享以及远程文件传输;21/TCP

STMP:邮箱传输协议;让邮箱实现发送以及接收邮件的功能

DNS:将URL域名解析成IP地址的协议;(域名->IP);53/UDP

架构模式

C/S架构(Client-Server——客户端-服务端架构)

核心思想:

客户端:发送请求(如微信桌面端);

服务端:集中处理请求并相应(如腾讯聊天服务区)

缺点:维护成本高;

客户端-----(HTTP请求)----->web服务器-------(响应数据)--->客户端

B/S架构(Browser-Server)

核心思想:

浏览器:统一客户端(如Chrome、Firefox)

服务区:提供web服务(Nginx、Apache)

应用层协议与架构模式的一些关联

1、协议驱动架构选择

HTTP/HTTPS天然适用于B/S架构的场景

MQTT适用于物联网的C/S架构

2、架构模式影响协议设计

CS架构需要支持状态保持:如FTP(分控制通道和数据通道)

BS架构的协议需要无状态的:如HTTP的无状态性(依赖cookie和section的扩展)

协议与应用架构的关系:共生的关系;

协议是架构的血液(如HTTP、FTP协议定义了数据的交换规则,支撑架构的整体运行);

架构是协议的载体(BS、CS架构决定了我们协议的使用场景以及优化方向)

持续远近功能:传统CS到云衍生的微服务;架构根据协议根据我们的协议以及技术不断地提升不断地发展

套接字与网络字节序

字节序的本质
定义:多字节数据(如int、float)在内存中的存储顺序

两种类型·

大端序(网络字节序):高地址存低位;高位存的是低字节,

比如0x1234存储为1234
小端序(主机字节序):高地址存高位;低位存的是低字节,

比如0x1234存储为3412

变量在内存里,是从 低地址 开始存放的!

第一个字节 = 最低地址

变量必须从 低地址 开始,往 高地址 方向放

大小端转换:
头文件:#include <arpa/inet.h>
1、htonl()函数 host-to-net-long
作用:主机→网络序(32位,如IPv4地址)
定义unsigned int
2、htons()函数 host-to-net-short
作用:主机→网络序(16位,如端口号)
定义unsigned int

3、ntohl()函数 net-to-host-long  长整型
作用:网络→主机序(32位)
4、ntohs(函数 net-to-host-short  短整型
作用:网络→主机序(16位)

如何将点分十进制转换为机器识别的数据?
inet_addr()函数
作用:将点分十进制字符串转换成网络字节序的无符号四字节整型

inet_ntoa()函数
作用:将网络字节序的无符号四字节整型转换成点分十进制字符串
函数原型:char *inet_ntoa(structin_addr in);

Socket套接字基础

TCP通信

TCP与UDP对比

对比 TCP UDP
角色

快递员

行使(不管对方在不在,送到就行);不讲顺序与完整性
特点 严格控制 准确率低
优点 可靠性强、顺序性、超时重传 传输效率高,实时性强,速度快
缺点 开销大 数据可能丢失、乱序(丢包)
使用场景 HTTP/HTTPS;文件传输(FTP);邮件(SMTP/IMAP) 视频流(RTP)、DNS查询、在线游戏、VoIP、轻量级交换、广播、组播消息

TCP网络编程流程

Socket()--->bind()--->listen()--->accept()--->send()/recv()--->close().

1.创建流式套接字--Socket()

socket编程

函数原型int socket(int domain,int type, int protocol);

参数选择

domain是通信域(或称为协议族)

AF_INET :ipv4

AF_INET:ipv6

type表示类型;常用类型:

SOCK_STREAM  TCP使用

SOCK_DREAM UDP使用

SOCK_RAW原始套接字

2.填充服务器的网络信息结构体--struct socketaddr_in

3.将套接字于服务器的网络信息结构体绑定--bind

服务器需要bind;客户端不需要bind;listen之前需要bind,客户端不需要去bind

4.将套接字设置成被动监听状态--listen();

原型:listen(int sockfd,int backlog)

sockfd表示要监听的套接字,backlog表示半连接队列的长度(即socket可以排队的最大连接个数)

5.阻塞等待客户端连接--accept()会处理套接字

函数原型:int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);

参数:

sockfd表示处于监听状态的socket

addr用于保存客户端地址的结构体指针,如果不关心客户端的信息,可以直接传NULL

addrlen输入时为addr的缓冲区大小,输出时为实际地址长度,如果不关心客户端的信息,可以直接传NULL

返回值:成功返回新的socket文件描述符(由内核生成,代表着与返回客户端TCP连接,专用于与客户端通信),失败返回-1(并重置错误码)

作用:从全连接队列中去取出一个已经建立的连接,然后创建一个socket用于数据传输(进行一个队列的操作:全连接队列为空,进行一个阻塞等待状态,内核会复制监听socket的配置:协议缓存区大小)生成一个对应的可以与客户端连接通信的socket

6、收发数据--recv()/send()

recv函数

核心作用:通过socket去接收来自应用层的数据

(底层:内核接收缓冲区拷贝数据到用户空间)

send函数

参数

sockfd表示客户端的socket套接字

buf要发送的数据首地址

len数据大小

flags控制选项(如MSG_DONTWAIT非阻塞,MSG_PEEK窥视数据,0阻塞)

返回值:成功返回实际发送的数据字节数,失败返回-1(并重置错误码)

7、关闭套接字

close函数

作用:减少文件描述符的引用计数

(底层原理:多个进程共享socket的时候,所有的进程都要调用close才能进行真正的关闭)

内核会释放套接字所占用的所有资源(包括套接字的数据结构、缓冲区、以及正在等待的队列)

只有当所有的套接字进程退出或关闭时,内核才会回收这个套接字的所有资源

挥手

TCP客户端

TCP客户端的搭建流程

socket() --->connect()----->send()/recv()---->close()

流程:

1.创建流式套接字-----socket()

2.填充服务器的网络信息结构体---struct sockaddr_in

3.与服务器建立连接--connect()

connect函数

头文件:#include<sys/socket.h>

函数原型:int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);connect是一个阻塞函数

参数sockfd表示客户端的socket套接字

addr表示目标服务器的地址结构体

addrlen 地址结构体的长度

返回值:成功返回0,失败返回-1,(并重置错误码)

核心作用:触发三次握手;超时重传:重新传输(6次重传)

原理:发送SYN包-->接收SYN-Ack包---->发送回应包(Ack包)

4.收发数据--recv()/send()

5.关闭套接字--close()

TCP的三次握手和四次挥手

TCP建立连接是通过三次握手实现的

半连接队列与全连接队列

流程:

        三次握手发生在客户端的connect函数与服务端的accept函数之间(区间)

过程:

第一次握手:客户端发送一个携带SYN标志数据包(同步序列号)(这个包表示客户端请求建立连接)---->客户端会进入一个SYN-SENT的状态(发起方第一次发给接收方设置成的状态);启动重传计时器;

第二次握手:服务端收到请求之后回复一个SYN ACK标志的数据包;进入半连接队列;启动定时器(表示服务端已经接收到了客户端的请求,并同意建立连接);服务区会选择一个随机的序列号b,将它放置到带有SYN字段中;将这个带有SYN标记的数据包放到ACK的字段中;发回给客户端;服务器会进入SYN-RCVD的一个状态;就完成了第二次握手

第三次握手:客户端收到服务端发来的回复之后清空重传定时器;把它设置为已连接;分、再发送一个带有ACK回应标志的数据包;表示客户端确认收到来自服务端的响应并同意建立连接;客户端将服务端的一个初始序列号b,这个初始序列号加1,放到ACK标志的一个字段中;再将数据包重新发回到我们的服务器;服务端收到ACK之后重新去创建一个新的socket,从半连接队列中去删除旧的socket;将新socket加入到全连接队列中,然后从全连接队列中去取socket;于是客户端、服务端都进入一个称为ESABLISHED的状态;连接就建立完成了;握手完成

三次握手的过程保证了客户端与服务端都准备好去建立连接;并且可以正确地发送与接收数据了;

如果服务器没有收到客户端的请求或客户端没有收到服务端的响应;我们就重新发送请求;直到建立连接成功

三次握手保证了数据的可靠性与安全性,避免了重复的连接请求以及资源浪费的情况

为什么不能两次握手?因为可能被中间商欺骗,中间商篡改数据使客户端和接收端收的请求和响应都发生错误;就像黑客,而且不能确认对方身份;三次握手可以确认彼此的身份,确保可靠连接通信

四次挥手

通过挥手断开连接

第一次挥手:客户端发送FIN报文(断开连接报文;这个报文会指定一个序列号);客户端会处于FIN-WAIT的状态(等待断开连接)

第二次挥手:服务端已经接收到了FIN报文;回发一个ACK报文(表示服务端收到了客户端的报文);服务端会处于CLOSE-WAIT的状态

第三次挥手:服务器也想断开连接,情况与客户端第一次挥手一样:发送FIN报文(FIN =1);指定一个序列号(seq =w);服务端会处于LASY-ACK的状态;给服务端最后的一个回应

第四次挥手:客户端收到了我们的FIN报文(收到来自于服务端请求的端口连接的一个报文);客户端回复一个ACK(回复报文)作为应答;并且我们将服务端的一个序列号的值加1 作为我们自己的ACK报文的一个序列值;这时客户端会处于TIME-WAIT的状态——过一段时间——确保服务端收到了自己的回应报文才进入CLOSE状态;这个TIME-WAIT等待时间是一个2MSL的时间,通常是一个报文的一个回应;时间大约为60秒;服务端CLOSE后客户端也就处于一个CLOSE的状态

为什么要四次挥手断开连接?

取决于TCP的全双工特性;TCP是双向独立关闭的特性:客户端、服务端各自都有独立的发送通道以及接收通道;一方发送:FIN表示我们不会再给你发送消息,但是我还能接收到来自对方的消息;需要双方独立进行一个确认关闭;

为什么两次、三次挥手不行?因为当我们的客户端发送完我们的FIN报文之后;我们服务端可能还没有完成完全发送完的数据(如剩余的响应);需要发送ACK报文去确认客户端的一个关闭请求;等待我们数据都发送完毕之后;我们再去发送自己的一个FIN请求(断开请求)——即第三次挥手;

状态含义:

CLOSE-WAIT状态时服务器在接收到我们的FIN报文之后进入了这个状态;进入这个状态时会等待我们的应用程序处理完剩余的数据,我们才会发送我们的FIN请求(FIN就是请求断开报文的含义)

TIME-WAIT状态是确保我们最后一个ACK回应我们到达服务器了;如果丢失了服务器;会重传我们的FIN报文;然后我们再进行一个回应;这样防止了旧链接的一个延迟报文去影响我们的一个新链接;我们会等待一个2MSL的时间,通常为60秒;如果超过了这个时间我们没有再次收到FIN报文,则代表对方成功(收到的是ACK报文);于是我们就进入CLOSE状态

UDP通信

1.无连接

2.面向数据报

UDP通信流程

客户端方面:

1.创建套机字

2.sendto()向目标地址发送数据,不需要绑定本地的端口,系统自动分配端口

recvfrom()接收服务器返回的数据;获取源地址信息

3.调用完函数使用close();关闭套接字,释放资源

服务端方面:

1.创建套机字

2.绑定端口:通过bind()函数绑定固定端口,如8888端口;确保客户端可以精确地去发送数据给服务器

3.recvfrom函数接收数据:去阻塞等待来自于客户端的数据;来获取数据以及客户端的地址

4.sendto发送应答:客户端的地址从recvfrom获取的地址精确发回客户端(即回复数据)

5.调用close释放套接字、释放资源

recvfrom函数

头文件

原型

参数sockfd

buf

len

flags:控制选项(如MSG_DONTWAIT非阻塞,MSG_PEEK窥视数据,0代表阻塞)

src_addr:源地址,获取发送方的信息:如IP地址端口号将这个信息存到这个源地址这个结构体

addrlen地址长度

作用:从接收缓冲区取出完整的UDP报文,缓冲区为空,会发生阻塞;

取出报文后会剥离UDP的头部,并且将我们的数据拷贝到用户空间中;然后记录源地址

sendto函数

socket

buf:发送的数据的首地址

dest_addr:目的地址

作用:1、将用户地址拷贝到内核缓冲区中,

2、封装UDP头部:源端口、目的端口、长度端、校验和

3、交给IP层--->路由--->发送

单播、组播和广播——搭建UDP的服务器和客户端

单播:一对一的通信

流程与各层职责

应用层:调用rend--->传输层----->网络层:封装IP头部即单播:通过路由表去转发;如默认网关---->数据链路层:ARP协议将目标IP的MAC地址封装:以太帧;单播(MAC地址)

组播:一对多的通信

组播工作流程

发送者:

1、创建套接字

2、填充组播信息结构体

3、发生数据

接收者:

1、创建套接字

2、填充组播信息结构体

3、将套接字与组播信息结构体绑定(bind)

4、设置加入多播组(setsockopt)

5、接收数据(recvfrom)

setsockopt函数

setsockopt函数
头文件:#include <sys/socket.h>
函数原型:int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
参数:
sockfd目标套接字描述符,表示需要配置的套接字
level选项层级(如1、SOL_SOCKET:通用套接字选项    2、IPPROTO_IP:IPv4选项    3、IPPROTO_TCP选项)

optname 选项名称(如S_REUSEADDR、IP_ADD_MEMBERSHIP)
optval指向选项值的指针,类型和长度需与optname匹配
optlen optval指向的数据长度
原理作用:setsockopt函数是一个系统调用,用户程序向内核去传递一个配置参数;内核会根据参数去修改套接字相关联的一些协议栈;大多数选项配置在套接字的生命周期内持续有效,直到显示去修改配置或者套接字关闭才会失效

构建广播发送端

广播:一对所有通信,数据包发送到同一网络(同一个局域网)内的所有主机

特点:1、受限广播:目标地址是一种特殊的网络地址为:255.255.255.255

2、定向广播:目标地址为子网的广播地址:192.168.1.255;这个网络需要路由器去允许系统转发;默认情况下是禁用这个状态,避免广播风暴(泛洪式的传输)

使用setsockopt函数去设置启用广播选项

工作流程与原理

发送者:设置套接字为SO_BRODCAST选项:表示运行去发送套接字(广播包);然后给广播包的目标地址设置成广播地址

接收端:设置套接字;接听所有绑定指定的端口;监听所有接口的数据

网络设备:交换机将广播包泛洪到所有的端口(除了源端口);然后路由器会默认去丢弃广播包,防止跨子网进行传输(默认禁用定向传播)

IO模型与非阻塞IO

IO分为内存IO、网络IO、磁盘IO(三种);通常我们使用的是后两种

阻塞IO、非阻塞IO、IO的多路复用

阻塞IO(同步IO):(优点:代码简单、适应低频场景;缺点:每个连接都会占用一个线程或进程,造成资源的浪费,并且在高并发场景会频繁切换线程,造成CPU开销过大)

用户层:

调用recv

recv(fd,buf,sizeof(buf),0);//

读阻塞:工作流程

1、检查接收缓冲区(检测是否有数据)

2、无数据时进入睡眠

3、数据到达时唤醒进程

写阻塞:缓冲区满时是写阻塞;直到缓冲区有新的空间可以供我们操作;

执行操作:数据到达内核时会唤醒进程并拷贝数据

非阻塞IO

fcntl,函数原型:int fcntl(int fd,int cmd;)

cmd:命令参数;有两种:F_GETFL获取文件描述符的状态;F_SETFL:设置描述符的状态

设置为F_GETFL时可不设置第三个参数,fcntl的返回值是成功返回文件描述符,失败为-1并重置错误码;设置为F_SETFL第三个参数要设置为int类型,fcntl函数的返回值是成功返回0,失败返回-1,并重置错误码;

例如设置为F_SETFL:fcntl(F_SETFL,O_NONBLOCK);//O_NONBLOCK是一个非阻塞标志位

fcntl函数是Linux/Unix系统下的系统调用函数;用于对已经打开的文件描述符进行控制操作:修改文件描述符;修改状态标志

非阻塞IO是异步操作

在网络编程中我们通常使用非阻塞IO与轮询或者信号结合使用的方式实现多个连接同时处理操作

流程:

1、打开文件描述符

2、使用fcntl函数去获取原来的文件的标志状态

3、设置非阻塞状态

弊端:CPU空转:内核的轮询会消耗大量的CPU资源;

内核轮询:检测设备或文件描述符是否有数据可读可写、发生异常的一种技术;循环检测IO接口,从IO接口中检测是否有数据产生;如果有就往下读取,如果没有就循环检测;这样会造成大量的无效轮询,浪费大量的CPU资源(CPU空转)

内核的工作

1、检查接收缓冲区,无论是否就绪都立即返回结果

2、无数据是:返回EAGAIN错误码,进程继续执行(不进入睡眠)

IO多路复用

单线程管理多个文件描述符(如套接字);

为什么使用多路复用?

1、提高并发性能:如服务器去处理成千上万个客户端的一个连接请求;避免每一个连接都去创建一个线程或进程(减少资源消耗;上下文切换的开销)

2、高并发场景下:web服务器(实时通信系统)去高效地管理大量的连接

3、与非阻塞IO结合;实现单线程去处理多个任务

select函数

pllfd结构体

revents:文件描述符的操作结果事件;内核在调用返回时去设置short revents这个域(内核设置)

events是由用户去设置

poll函数

timeout = -1阻塞;=0立即返回;>0 等待指定数目的毫秒数

返回值 成功返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发送,poll()返回0,失败返回-1(并重置错误码)

poll函数比select函数性能高?为什么?

改进点:poll使用的是链表结构,突破select函数的fd=1024大小的限制(没有最大文件描述符的限制),支持更加精细的事件类型,内核的实现与select函数相似:需要管理多个文件描述符与轮询,遍历所有的fd,时间复杂度O(n);而select

epoll:性能更优,由事件进行驱动,仅关注活跃的文件描述符,不会遍历所有的fd(文件描述符),时间复杂度O(1);内置红黑树,存储监控的fd,支持快速增删改查;返回就绪的fd,避免全局扫描;没有fd数量的限制,仅受资源系统的约束(由事件进行驱动且高效地数据结构,显著提高高并发场景下的IO处理性能的一个方式)

流程:

1、注册fd以及事件(用户空间去注册)通过select、fd_set结构体去设置我们要监听的fd的集合到内核空间中

2、内核会轮询所有的fd:以select函数为例轮询所有的fdO(n)

3、调用select函数进行阻塞等待事件,传入所有fd集合,关注读的状态,写不关注,异常不关注

FD_ISSET检测fd是否就绪

4、应用程序进行处理;循环监听;(select函数每次调用前都会重置fd_set)

多线程、多进程并发服务器

流程与作用

服务器模型:

两种:循环服务器、并发服务器

并发服务器(与传统单线程服务器相比):具有更高的吞吐量与更低的响应时间(能更好地分配和利用资源,提高服务性能);更高的资源利用率

并发服务器模型又分为:

1、多线程模型:主线程负责监听连接,为每个连接去分配独立的线程去处理客户端的操作请求

2、多进程模型:父进程去生成子进程,处理客户端的请求

3、时间驱动模型:以selected为例,通过selected机制去轮询多个连接,处理客户端的请求

CPU在多个任务下一次只能处理一个执行;但是通过将CPU的使用在恰当的时机分配给不同的任务,让我们在多个任务下在视觉上看起来是在同一时刻执行的(由于CPU执行速度非常快,多任务切换的时间也短)

多核处理器:并发服务器会通过多进程多线程及IO等机制将任务分配到不同的核心,并行地处理;避免单个核心忙闲不均的问题;显著地提高了CPU的利用率,减少等待时间,允许并发服务器在等待一个请求的同时,同时去处理其他的请求,大大减少资源闲置的问题;避免单点阻塞导致服务崩溃(处理高并发请求、降低延时以及处理IO密集的任务)

多线程并发服务器 多进程并发服务器
架构 主从架构 通过创建多个子进程来同时处理多个客户端请求的一种服务端架构
核心思想 利用操作系统的多进程机制将每个客户端的请求分配给独立的子进程来进行处理,进而实现并发处理能力
内核工作机制

fork创建子进程内核会为子进程分配独立的进程控制块PCB,内存会复制父进程的内存地址空间,子进程继承父进程的打开文件描述符,内核会通过调度算法为多个子进程分配CPU时间片,实现并发处理

三目运算符:格式:表达式a? 表达式b :表达式c

执行步骤:

1、计算表达式a的值

2、如果表达式a的值为1,则执行表达式b;

3、如果表达式b的值为0,则执行表达式c;

三元运算符:max_fd = max_fd > acceptfd ? max_fd : acceptfd

拆解格式

条件 ? 成立取左值 : 不成立取右值

整句等价 if else

if(max_fd > acceptfd)

{ max_fd = max_fd; //不变 }

else

{

max_fd = acceptfd; //更新成更大的fd

}

作用:保存当前最大的文件描述符

TCP/IP协议分析

TCP/IP协议数据封装过程

应用层:用户数据(应用层生成的实际信息:HTTP请求内容、FTP文件数据、电子邮件正文)

应用层协议:添加对应的应用层首部:用于标识应用类型、源数据(内容长度、编码方式、操作指令等)

传输层:添加TCP首部:确保数据可靠传输,并且能进行连接控制以及流量控制

网络层:IP首部:进行路由寻址,分片重组、跨网络传输,形成IP数据包

数据链路层:在原先IP数据报文的基础上分别添加以太网首部、以太网尾部:可以将这一段以太网帧用于物理网络传输以及错误检测以及本地设备寻址

抓包看具体报文

wireshark

loopback本地回环地址

粘包:接收端在读取操作中获取多个应用数据包,导致数据混乱驳杂;无法区分中间的消息边界与原始数据

拆包:应用层数据包被拆成多个TCP报文进行传输

TCP粘包问题原因

1、TCP协议特性:面向字节流;缓冲区机制

2、发送方行为:数据写入方式、Nagle算法

3、接收方行为:缓冲区读取、读取粒度

4、网路传输因素:分片与重组

解决方法:1、固定长度法

2、分隔符法

3、头部声明长度法:消息的头部去固定字段声明消息体的长度

TFTP协议与TFTP服务器/客户端实战

TFTP5种报文支持的格式(以块为单位传输:块大小:512字节)

1>RRQ(读请求)

2>WRQ(写请求)

3>DATA数据包

4>ACK回应

5>ERROR差错报文

读写请求的报文格式:

文件名表示客户在服务器上读或写的文件名;通常是以0作为标记作为结束

模式表示ASCII码串:有两种模式:1.netstart将数据看作行的ASCII码字符串;以回车与换行作为行结束符2、octet模式将数据当做一个以8比特为1组的字节流;以0作为标记结束

数据包:块编码:数据分组都会包含一个对应的块编号字段;它在之后的确认分钟中进行使用;

每一个数据分组的块编号在发送和确认的时候是一一对应的

以读一个文件为例:

TFTP客户端要发送一个读请求说明我们要读的文件名以及文件模式;如果这个文件能够被我们客户端所读取,TFTP服务器会返回块编号为1的数据块DATA(数据分组);TFTP客户端又会回发一个块编号为1的ACK块,服务器随后再去发送一个块编号为2的DATA2(数据块);客户端再去回发一个块编号为2的回应快,...一直重复此操作,直达文件传输完成(这个过程也是TFPT服务端与客户端通信的流程)

数据是由512字节为一组分组;直到最后一个分组,它会小于512

差错报文:错误码;给出一个标志(标志差错的一个数字);差错信息:说明差错原因和内容;每一种报文它都是遵从两个字节的一个操作码;两个字节的操作码加上数据的格式,TFTP轻量化的关键

TFTP通信流程:

请求——响应模式

客户端会随机地去选取一个端口;将目标端口固定为69;服务端收到请求后,使用一个新的随机端口与客户端进行通信

文件传输的具体流程:

以下载文件为例:

RRQ去读请求:客户端发送了一个RRQ包(操作码为1),包含了文件夹名以及模式;给服务端,服务端收到后从69端口进行一个响应;然后使用一个新的端口,新的端口去发送第一个数据块(块1);紧接着客户端收到块1之后回复一个ACK(ACK1);操作码为4;标识符为块1;紧接着服务端再次去发送我们一个块2 ,操作码为3;客户端再去回应对应的一个ACK;结束标志:直到DATA包的长度小于512(说明已经到最后一个包,传输就结束了

文件上传:客户端先发送了一个WRQ包;服务端回应的是ACK0(与文件下载区分)(预约);紧接着收到服务器的一个回应之后;客户端才会发送对应的一个上传文件(DATA1);服务端才会回应我们应该ACK1;然后一直重复这个步骤直到文件上传结束(写的请求流程)

块编号遵循递增的原则,每块增加1;块编号为16位无符号整数最大值是65535,到达65535之后会进行回绕,回绕到0

TFTP有对应的超时重传机制;默认5秒超时;发送方在发送数据之后,会启动定时器;超时没有收到ACK的回应包,我们会重复去发送数据包,最多重试5次,之后放弃重传;TFTP数据传输的规则

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐