《网络基础全链路深度解析:从Socket编程到HTTPS与TCP/UDP内核机制》----《Hello Linux!》(25)

文章目录

前言

网络通信的核心是"协议与编程"的双向协同,从应用层的HTTP/HTTPS交互,到传输层的TCP/UDP可靠/高效传输,再到内核层的Socket与文件管理,每一环都暗藏严谨的设计逻辑。本文以"底层原理+实操落地+安全延伸"为核心,构建覆盖全链路的网络知识体系:先铺垫端口、网络字节序、Socket核心接口等基础,通过代码实现UDP广播、TCP多版本服务端及客户端重连,拆解系统调用避坑点;再深入协议定制核心,用计算器案例演示序列化/反序列化的自定义实现与JSON工具用法,延伸讲解HTTP协议细节(URL、请求响应、状态码、Cookie);进而聚焦HTTPS安全机制,解析对称/非对称加密、CA认证及安全方案设计,破解网络传输安全难题。同时深耕UDP/TCP底层,详解协议段格式、三次握手/四次挥手、滑动窗口、拥塞控制等核心机制,补充Socket与Linux文件内核关联、异常场景处理等知识点,搭配调试指令、案例代码与习题,帮助读者打通"编程实操-协议解析-安全防护-内核逻辑"全链路,实现从理论理解到实战应用的能力跨越。

HTTPS协议

HTTPS协议就是在 HTTP 协议的基础上引入了⼀个加密层(是位于应用层和传输层之间的一个中间软件层)

HTTPS就是基于TLS/SSL加密保证安全传输的 (一般来说就是用的这个加密技术)

加密层常见的技术:比如SSL
加密就是把明文经过变换成为密文

解密就是把密文经过变换成为明文

在加密和解密的过程中,需要数据进行辅助,这些数据就叫做密钥
引申的一些知识点:

数字指纹:是利用Hash函数对信息进⾏运算,⽣成⼀串固定长度的数字摘要

作用:⽤来判断数据有没有被窜改

数字摘要经过加密就形成了数字签名--通过非对称加密来搞的

这里运算常见的算法:MD5

常见的加密方式

密钥都是服务器报管的,客户端只是临时拿着使用而已

对称加密

同⼀个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密

特征:加密和解密所用的密钥是相同的

特点:算法公开、计算量小、加密速度快、加密效率高
举例:按位异或:

7^5 =x ;这个x是密文 7是明文 5是密钥

然后x^5之后就得到了7

非对称加密

这种加密需要用到两个密钥,一个是公有密钥(公钥),一个是私有密钥(私钥)

可以通过公钥对明文加密,变成密文;通过私钥对密文解密,变成明文--一般用这种,比下面这种安全

也可以通过私钥对明文加密,变成密文;通过公钥对密文解密变成明文

设计一套安全的方案

目前已经有成熟的方案了,这里只是自己模拟设计一套较为安全的方案
方案一:只使用对称加密

这样的话,很不安全--因为密钥没有被加密
方案二:只使用非对称加密

也不安全--因为公开密钥提供给客户端时没有被加密
方案三:双方都使用非对称加密

具体流程:

服务端拥有公钥S与对应的私钥S',客户端拥有公钥C与对应的私钥C'

1.客户端和服务端交换公钥

2.客户端给服务端发信息:先⽤S对数据加密,再发送,只能由服务器解密,因为只有服务器有私钥S'

3.服务端给客户端发信息:先用C对数据加密,再发送,只能由服务器解密,因为只有服务器有私钥C'

但是效率太低了;而且还有安全问题

安全问题:中间人被黑客入侵,有公钥M,A和私钥M',A'--可以劫持S和C,然后把公钥换成M和A给双方,然后可以对传输内容解密之后再用S和C加密传走
方案四:非对称加密+对称加密

具体流程:

服务端具有非对称公钥S和私钥S'

客户端发起https请求,获取服务端公钥S

客户端在本地生成对称密钥C, 通过公钥S加密, 发送给服务器

只有刚开始时用的非对称加密,之后都是用的对称加密

但是也存在上面的那种安全问题 但是效率比方案三快很多--因为
这个问题就是因为客户端不能确定这个密钥是服务器发来的,所以就需要CA认证这个东西
方案五:方案四+CA证书

注意:客户端本来就是会内置很多CA机构的公钥的

CA认证

服务端在使用HTTPS前,需要向CA机构申领⼀份数字证书,数字证书里含有证书申请者信息、公钥信息等。服务器把证书传输给浏览器,浏览器从证书里获取公钥就行了

这个证书的话,CA机构会核实申请认证提供的资料是不是对的--但是也有中间人篡改的风险,但是概率就非常小了

如果中间人单纯篡改证书的话--比如:篡改公钥--CA机构会去查这个公钥跟这个域名和申请者的关系

如果中间人掉包整个证书的话--必须找CA机构生成,那么申请者这些就不好填了
一般是先生成公钥和私钥对,然后再生成.csr文件,把这个文件传给CA机构

CA证书的形成过程

当服务端申请CA证书的时候,CA机构会对该服务端进行审核,并专门为该网站形成数字签名--CA机构有自己的非对称加密的私钥和公钥,用证书明文数据形成数据摘要,然后私钥加密形成数字签名,把签名附加在数据上,就成了一份CA证书

问题:

为什么签名不能直接加密传过去,而是要先形成摘要再加密形成签名传过去

原因:缩小签名长度,加快验证签名的速度

为什么不能直接传摘要过去,而是要加密形成签名之后传过去

原因:不加密的话,中间人篡改了,客户端也分辨不出来

再谈UDP协议

UDP协议端的格式:

UDP长度:表示整个数据报(首部+数据)的最大长度

这个检验和的话是用于检验数据有没有被篡改啥的
UDP没有真正意义上的发送缓冲区来暂存待发数据

UDP的接收缓冲区的话不维护传输顺序并且满则丢弃
管理UDP报文的话也是采用的先描述再组织

再谈TCP协议

TCP协议全称叫传输控制协议

朝网络发送数据的时候需要是完整的TCP协议段--所以把发送缓冲区里的数据封装成TCP协议段才行
注意:没有百分之百可靠的网络协议

--比如:TCP,最后一条消息是没有应答的 --没有应答的话,就不能保证数据百分之百是可靠的

TCP协议段格式

上面的选项和数据部分是可以没有的(除了选项和数据的部分叫做标准报头)

TCP的首部是指里面除了数据的部分--其长度是在20-60个字节之间的(标准报头部分是20个字节)

数据部分叫做有效载荷
上面各个部分的作用:

4位首部长度:就是4个二进制位来表示首部长度(它的基本单位是4个字节--也就是说0001就表示首部长度一共4个字节)

16位窗口大小:存的是自己的接收缓冲区的剩余空间的大小--流量控制需要用到

32位序号:保证数据的按序到达(因为传输过去的数据因为网络传输速度不一样,到达的顺序是不一样的)--给的是传输的部分的第一个的序号

--TCP给每个字节的数据都是编了号的(一般都不是从1开始编,是X+偏移量这样规定)

--TCP每次建立连接时,都会给一个随机的初始序号X --好处:和TIME_WAIT一样,来避免收到历史数据

32位确认序号:就是收到数据的末尾序号+1再传回去--让另一方知道下一次该从哪开始传

--传递的信息是确认序号之前的报文全部收到了

16位紧急指针: 标识哪部分数据是紧急数据

(紧急数据默认只允许1个字节的空间,紧急数据又被叫做带外数据)

--接收方会优先读取这个位置的数据 应用场景:比如关机指令

6个标志位:用来表明报文的类型(置为1表示有效,置为0表示无效)

URG: 紧急指针是否有效

ACK: 表示确认序号是否有效

PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走(可能是缓冲区快满了或者其他原因)

RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段

SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段

FIN: 通知对方, 本端要关闭了;我们把携带FIN标识的报文段称为结束报文段
要有序号和确认序号的原因:比如捎带应答就两个都需要,它既需要传递数据,又需要应答

确认应答机制和超时重传机制

确认应答机制就是发送给对方数据,对方要返回做出应答并且返回下一次该从哪里开始传

--传递数据时可以一段一段传,也可以多段一起传过去(多段一起传的话,不是最后那个应答丢失了都可以继续)

注意:在一般情况下,如果发送方没有收到应答的话,就认为对方没有收到数据--这个时候就会重新给对方传--这个就是超时重传机制
超时重传的超时时间的确定方法:

这个时间TCP是会动态计算的--因为跟网络环境有关

--如果设置的时间过长,会影响重传效率;如果设置的时间过短,会频繁发送重复的报文过去

一般超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍--并且会以指数形式递增(比如第一次是500*1,还没应答,那下次就是500*2)

--如果累计到一定的重传次数还没接收,TCP认为网络或者对端主机出现异常, 强制关闭连接

连接管理机制

建立连接:需要三次握手--三次握手完毕,connect才会返回

三次握手就是上面的SYN--SYN+ACK--ACK

(本质是在检验客户端与服务器之间的 "全双工通信通路" 是否通畅)

建立好的连接会由OS来描述后用全连接队列进行管理,accept的话只是从全连接队列里面去拿而已

全连接队列的最大长度由listen的第二个参数backlog决定

--backlog+1就是全连接队列的最大长度

如果服务端收到了SYN,但是还没收到客户端对SYN+ACK的回应的话,此时的连接就是半连接(如果全连接队列满了的话,就会卡在这)--半连接队列的长度也是有限制的

--全连接不够了会优先搞半连接的来补

--半连接状态的连接不会被长时间维护
三次握手的作用:

1.建立连接 2.协商起始序号 3.协商双方额接受缓冲区大小
取消连接:需要四次挥手

四次挥手就是上面的FIN--ACK--FIN--ACK

发送FIN的话就表示关闭了自己的写端,就不能发数据了--跟发控制报文(eg:ACK)区分

主动断开连接的一方在发送完最后一个 ACK 后,立即进入 TIME_WAIT 状态,等待一段时间之后,会自动释放--此时连接还没有彻底断开,服务端的ipport还在被使用,无法立即被重新启用(客户端没关系,因为客户端本来就是随机端口)--此时的话可以通过setsocketopt解决
四次挥手有时也可以只3次

--就是客户端想FIN的时候,服务端也正好想FIN--那服务端就会把ACK捎带应答过去
为什么需要TIME_WAIT

1.让通信双方的历史数据得以消散 -- 可能会废弃的数据(比如已经被补发了的数据)还在传

2.让4次挥手有较好的容错性--让有重发的机会

TIME_WAIT一般设计的是等待两个MSL的时间

一个MSL是 TCP 报文段在网络中能存活的最长时间

这个可以查看或者人为改动:打开或者把proc/sys/net/ipv4/tcp_fin_timeout里面的值改了就行
注意:TIME_WAITCLOSE_WAIT区分
为什么全连接队列不能太长,但是也不能没有:

太长的话会太占用服务端的资源

没有的话不能充分利用服务端的资源--就像满客状态的餐厅,外面会有椅子让别人等
为什么设计成三次握手:

因为只有一次握手的话,容易有SYN洪水攻击服务器

只有两次握手的话,就相当于把风险给了服务器--因为服务器建立的可能客户端没建立--所以需要奇数次握手,把握手失败的成本给客户端承担

而且三次握手可以确认 "双向收发通道均正常"
但是三次握手也是有攻击的方法的:

用肉机去把服务端的全连接和半连接队列全部塞满

引申:释放全连接比释放半连接所需要的时间会长很多
为什么设计成四次挥手:

因为是全双工通信,双方都有可能发送数据,所以双方要各说两次--一次是我想结束了,一次是我收到你的回应了

流量控制

TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制

--如果太快了,容易丢包;如果太慢了又影响效率

--这个机制的话以可靠性为主,以提高效率为辅

这个机制是通过控制滑动窗口大小来实现的

窗口探测时是不会携带其他数据的
问题:

第一次发送数据时是怎么保证发送数据量是合理的

--三次握手时交换了报文了,已经协商了双方的接受能力

(注意:第三次握手其实可以携带数据过去了--比如:也搞个捎带应答)

滑动窗口

滑动窗口是发送缓冲区的一部分(每个TCP连接都有自己专属的发送缓冲区--互不干扰的)

发送缓冲区里面的数据可以分为:已发送已应答 、已发送未应答、未发送(通过指针和下标来把区域划分出来)

引申:已发送已应答的部分可以被覆盖使用了 --空闲的空间也可以划到这个部分来

滑动窗口里面的数据是:已发送未应答的 或者 已经准备好发送,但是还没发出的

--因为有滑动窗口的存在,才可以一次向对方发送大量的tcp报文
窗口越大, 网络吞吐量就越大, 传输效率就越高
滑动窗口不会在发送缓冲区中越界的原因:

tcp采用了类似环状算法来管理发送缓冲区


滑动窗口的大小和移动方向:

滑动窗口一般都是向右移动的--向左是异常现象

滑动窗口的大小是动态变化的

--滑动窗口的大小 = min(接收窗口的大小,拥塞窗口的大小)

想要让滑动窗口变大的方法:延迟应答

让接受方收到报文后先不着急应答 -->给上层充足的时间去取走数据来让剩余空间变多

推荐做法:每次都让上层尽快通过recv、read啥的把数据从内核中拿出来
滑动窗口里面的数据出现丢包的话,处理方法:

1.数据包到了,但是ACK丢失了

这种情况没事,因为确认序号那个的存在本来就允许少量的ACK丢失

2.数据包丢失

那么确认序号就会一直卡在那里,导致确认应答是重复的--收到三次重复的确认应答,发送方就会重新发送 --这个叫做快重传(高速重发机制)

比如:传过去1001-5000,2001-3000丢了,那就重新传2001-3000过去,就和之间已经到了的联动了
快重传和超时重传:

超时重传相当于是兜底的

--不管有没有收到重复 ACK,只要某个数据段的超时计时器到期,就直接重传这个数据段
滑动窗口内的 "流式数据",必须按 MSS 的大小拆分成多个 TCP 分段发送

滑动窗口规定了 "当前能发的总数据量上限"

MSS 规定了 "每个 TCP 分段的最大数据量"

MSS的值就是在TCP首部的40字节变长选项中(kind=2)

TCP的单个数据报的最大消息长度, 称为MSS

拥塞控制

通信的时候,如果出现了少量的丢包--那是正常现象

如果出现了大量的丢包--tcp就会判定是网络出问题了(网络拥塞)--此时不能对报文进行超时重传!会加重拥塞的情况
TCP协议让大家达成了应对网络拥塞的共同规则--采用的同一个拥塞控制规则
拥塞控制的策略:引入了一个拥塞窗口 +慢启动机制

拥塞窗口:也是一个动态的窗口,是主机判断网络健康程度的指标;超过拥塞窗口的大小就会引发网络拥塞,反之则不会

发送开始的时候, 定义拥塞窗口大小为1;每次收到一个ACK应答, 拥塞窗口加1

慢启动机制:初始时慢, 但是增长速度非常快--初始时慢就是看网络是不是恢复正常了

当拥塞窗口的大小超过慢启动的阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长

当TCP开始启动的时候, 慢启动阈值等于窗口最大值

在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回

延迟应答

延迟应答是不能无限制延迟的,有两个约束条件:

数量限制:每隔 N 个包必须应答一次,不能无限攒包

时间限制:只要超过了最大延迟时间,就必须立刻应答

TCP协议小结

可靠性:

1.校验和 2.序列号保证按序到达 3.确认应答机制(核心) 4.超时重发

5.连接管理 6.流量控制 7.拥塞控制(还操心了网络)

提高性能:

1.滑动窗口 2.快重传 3.延迟应答 4.捎带应答

这些机制几乎在两端的机器上都起作用
对于TCP是面向字节流的理解:

tcp内核只把数据当成多个字节去传输--不关心上层协议和上层的报文格式(所以tcp的有效载荷不是以报文为单位的)

--用户层把请求发送给tcp,tcp当成多个字节去传;用户层接收的话,需要自行把多个字节组合重新形成回请求 (这个时候就容易出现粘包问题了)
内核的TCP报文里面不记录有效载荷长度的原因:

TCP是面向字节流的,本来有效载荷里面的数据就多半不是应用层完整的报文的,所以没必要
粘包问题:就是数据包和数据包的边界划分不清晰导致多划或者少划了

解决方法:制定协议(在应用层通过协议来明确报文和报文之间的边界)

1.定长报文

2.使用特殊字符

3.自描述字段+定长报文

4.自描述字段+特殊字符
TCP的异常情况:

进程终止(正常终止还是异常终止都一样):连接会以正常的方式断开

机器重启:会杀掉所有进程,连接关闭方式和进程终止一致

机器掉电或者网线断开:这里连接会非正常断开

a.发信息发现连接失效会强制重新连接

b.客户端网络没了会断开自己这边的连接,但是服务端不知道

c.长时间没有数据交互,若长时间没数据交互,TCP 的 "保活定时器" 会定期检测连接

如何用UDP实现可靠传输

这是一道经典面试题

这样做肯定是有特定的需要可靠传输的场景,但是又不需要TCP那样非常完美的可靠性--只需要按照需要模仿TCP的实现可靠性的机制改良UDP就行了(如何模仿着实现也要说出来)

socket和文件的关系

socket对于开发者说是接口;但是在Linux内核里面是被当作特殊文件来看待的

Linux内核里面文件通过struct file存储的,socket也是这样

--const struct file_operations *f_op是存文件的操作方法的,普通文件是指向读写磁盘的方法,socket是指向网络相关的方法

void *private_data来指向socket的核心结构体struct socket

作业部分

cpp 复制代码
文件传输使用的协议是(B)
A.SMTP//简单邮件协议
B.FTP
C.UDP//用户数据报协议
D.TELNET//Internet远程登录服务的标准协议
cpp 复制代码
以下关于HTTP协议叙述正确的是(C)
A.HTTP协议是建立在 TCP/IP 协议之上的网络层规范,分为三个部分:状态行、请求头、消息主体
//HTTP协议是TCP/IP协议栈中应用层的协议
B.Cookie数据在消息主体(body)中传输//Cookie数据是在HTTP协议头部字段中传输
C.HTTP协议支持一定时间内的TCP连接保持,这个连接可以用于发送/接收多次请求
D.HTTP是有状态的,每个请求都是独立的
cpp 复制代码
以下对http请求方法描述正确的是(ABC)
A.POST请求永远不会被缓存,且对数据长度没有限制
B.我们可以从浏览器历史记录中查找到GET请求
C.我们无法从浏览器历史记录中查找到POST请求

引申:HTTP GET请求提交参数有长度限制--是特定的浏览器及服务器对它的限制
cpp 复制代码
如何在响应报文头部去设置字符编码(AC)
A.Content-Type: charset=utf-8
B.Content-language: charset=utf-8
C.Content-Type: charset=UTF-8
D.Content-language: charset=UTF-8
cpp 复制代码
【多选题】cookie有什么用?(ABC)
A.记录用户的ID
B.记录用户的密码
C.记录用户浏览过的商品记录
D.记录用户的浏览器设置
cpp 复制代码
单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
session并不一定完全依赖于cookie实现--只是一般是用cookie搞的
一般session的有效期默认是30分钟
cpp 复制代码
【多选题】以下关于http和https说法正确的是(ABCD)
A.http是超文本传输协议
B.https是超文本传输安全协议
C.http是明文传输
D.https是加密传输
cpp 复制代码
【多选题】FTP服务的控制端口与数据端口默认是(AB)
A.20
B.21
C.22
D.23

FTP协议中,20端口号用于传输数据,21端口号用于传输控制
cpp 复制代码
关于UDP的说法正确的是(B)
A.UDP的包大小没有限制//UDP报文限制传输报文大小为必须小于64k
B.UDP不会进行错误重传
C.UDP跟TCP一样提供可靠的数据报协议//UDP不保证数据安全及有序到达对端
D.UDP有简单的流控制//UDP是数据报传输,没有流控制
cpp 复制代码
主机甲向主机乙发送一个(SYN=1,seq=11220)的TCP段,期望与主机乙建立TCP连接
若主机乙接受该连接请求,则主机乙向主机甲发送的正确的TCP段应该是(B)

A.(SYN=1,ACK=1,seq=11220,ack=11220)
B.(SYN=1,ACK=1,seq=11221,ack=11221)
C.(SYN=0,ACK=0,seq=11221,ack=11221)
D.(SYN=0,ACK=1,seq=11220,ack=11220)

seq:发送方的本条报文起始序号
ack:是接收到的序号的末尾+1
相关推荐
lunzi_08261 小时前
《图解HTTP》--第3章 HTTP报文内的HTTP信息
网络·网络协议·http
IT大白鼠1 小时前
Linux系统安全及应用:技术配置与检测实战
linux·运维·系统安全
小肝一下1 小时前
3.linux——进程控制
linux·运维·服务器·进程控制
北山有鸟1 小时前
linux设备全解析
linux·运维·服务器
paeamecium1 小时前
【PAT甲级真题】- Shuffling Machine (20)
c++·算法·pat考试·pat
不想写代码的星星1 小时前
C++协程从入门到放弃?不,是从入门到手搓调度器
开发语言·c++
Jurio.1 小时前
当 AI 不再只是对话:Codex app 的自动化功能
运维·人工智能·ai·自动化·codex
redaijufeng1 小时前
C++构造函数详解:从基础原理到实际应用
java·jvm·c++
Carl_.Net软开2 小时前
c#-SECS/GEM协议入门
网络·secs