传输层的七七八八

TCP

TCP提供一种面向连接、可靠的字节流服务。
面向连接 :两端各自维护一份数据结构,传输数据之前,先进行数据结构部分信息的状态同步,就是去建立连接,建立好之后才能传输数据,不需要的时候断开连接,然后释放相关数据结构
可靠性

  • 由TCP将报文段分段为合适的大小后交给IP层
  • TCP发出段后启动定时器,目的端在定时器到期之前没有确认应答,TCP会重发该段
  • 接收端收到数据段后需要确认应答,告知发送端数据已经接收到哪里了(通过Sequence Number和acknowledgement Number记录)
  • TCP的首部校验和由发送端计算和存储,接收端如果校验出错,将包丢弃不发送确认应答,等待重发
  • TCP分段后委托下层发送数据段,到达目的端如果出现乱序,TCP会重排序后交给应用层
  • 如果数据重复,则丢弃
  • 接收端会告知发送端能接受的最大数据段,实现流量控制

首部格式

各字段含义

  • Source Port: 发送端端口号,例如http:80
  • Destionation Port:接收端端口号
  • Sequence Number(Seq):初始值由主机随机生成,TCP流服务会对每个字节进行编号,对传输的Data部分进行计数(不包含数据链路层、IP首部、TCP首部)。目的端的ACK会将Seq+Tcp Segment Data的值返回到发送端,这个值就是发送端下次发送时的seq值。
    • 注:虽然SYN、FIN在首部,但传输他们时仍然会计数,单位为1byte,所以三次握手和四次挥手时,虽然Tcp Segment Data的长度为0,回复的ACK值仍然要加1。
  • Acknowledgement Number:Seq + Tcp Segment Data计算值后返回给发送端,表明该值和上次Seq值之间的数据我已经收到了,下次发送时以这个值作为Seq值发送
  • Data offset:TCP首部的长度,如果没有选项内容,首部长度为固定20 bytes,添加选项后最大首部长度为60 bytes(受限于该字段长度:4 bit,单位为4bytes)
  • Reserved:该字段为了以后扩展使用,通常设置为0
  • Control Flags:每一位代表一个标志,顺序如上图:
    • CWR(Congestion Window Reduced):在网络层的七七八八聊过,ECN(Explicit Congestion Notificat)的实现依靠IP首部记录路由器是否遇到拥塞,在返回包的TCP首部中通知发生拥塞。CWR标志和ECE标志设置为1时,会通知对方网络拥塞
    • ECE(ECN-Echo)
    • URG(Urgent Flag):为1时表示该数据段中有需要紧急处理的数据
    • ACK(Acknowledgement Flag):TCP规定除了SYN包之外,该标志都设置为1,表示应答有效
    • PSH(Push Flag):为1时表示将数据立即传给上层协议,不进行缓存
    • RST(Reset Flag):强制断开连接
    • SYN(Synchronize Flag):为1时表示想要建立连接,并设置Sequence Number的初始值(握手)
    • FIN(Fin Flag):为1时表示不再发送数据,希望断开连接。主机收到设置FIN标志的包后,两端主机对对方的FIN标志包进行确认应答。不必立即回复,可以等待缓冲区中的所有数据发送成功并删除后再回复(挥手)
  • Window Size:从ACK Number的位置开始,最大可以接收的数据,发送发发送的数据不能超过该窗口大小。窗口为0时,对端可以发送窗口探测包。(1 byte)
  • Checksum:校验数据是否正确,覆盖TCP首部和Data部分
  • Urgent Pointer:在URG标志位1时该字段有效,紧急数据是从数据部分的首位到紧急指针所指的位置为止。Telnet Ctrl + C时会有URG为1的包
  • Options:用于提高TCP的传输性能,长度最大为40bytes(首部最大60bytes - 固定部分20bytes),padding同IP首部的padding一样,作为对options的填充,调整为32 bit的整数倍。options分为多种类型:
    • 类型2:MSS(Maximum Segment Size),在建立连接时(发送SYN标志的报文段)中指定MSS,表示本端能接收的最大长度的报文段,通常来说MSS越大,网络利用率越高。长度为 (MTU - IP首部 - TCP首部),对于以太网MSS长度可达1460bytes,默认为536bytes
    • 类型3:WSOPT-Window Scale,可以提高TCP吞吐量,首部的windows size长度为16位,只能发送最大64KBytes,使用该选项可以扩展到1GBytes字节,提升了单位RTT的数据传输量,从而增加吞吐。
    • 类型8:上面介绍Sequence Number对传输数据的字节进行计数,受32位长度的影响,如果高速传输一个很大的数据包,Sequence Number超出了内核解决序号回绕问题的范围(回绕幅度2^31 - 1),那么接收端就无法判断正确的序号了,加上该选项可以区分新老序号

数据传输

在主机网卡抓包繁杂信息太多,在虚拟机起一个基本的tcp server,回显客户端发送的消息后关闭,代码如下:

python 复制代码
import socket
import sys


HOST = "0.0.0.0"
PORT = 8888

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server.bind((HOST, PORT))
server.listen(10)
connection, addr = server.accept()

data = connection.recv(1024)

print(data.decode())

connection.sendall(data)

connection.close()
server.close()

客户端输入消息发送,并接收服务端的消息打印

python 复制代码
import socket

HOST = "10.211.55.3"
PORT = 8888

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((HOST,PORT))

send_info = input("send message: ")
client.send(send_info.encode("utf-8"))

recv_info = client.recv(1024).decode("utf-8")

print(recv_info)
client.close()

三次握手建立连接

建立连接过程

  1. 客户端发包(active open):开启SYN标志,客户端的initial seq = x

客户端向服务端发起带SYN标志的段,端口就是我们server.py中定义的8888,TCP Segment Len为0表示没有传输数据,Acknowledgment Number为0(第一次发起,没什么好应答的),客户端生成的Sequence Number为4176525122,wireshark帮我们分析的relative sequence number为0(下文使用relative sequence number),客户端seq = 0

  1. 服务器回包(passive open):开启SYN标志,服务端的initial seq = y和ack,ack的值为x+1(SYN占用一个序号)

服务端收到请求后,向客户端发送SYN标志的段,数据段长度为0,服务端的seq为2608063952,同样relative值为0,因为上一个包的SYN是占用sequence number的,所以ack = 1(客户端的seq 0 + SYN标志位1,看raw值的话就是客户端的seq4176525122 + 1 = 41766525123)

  1. 客户端回包:开启ACK标志,ack的值为服务端的y+1,seq为x+1

客户端收到服务端的回包后,发送ACK段,数据段长度为0,客户端seq为1,同样ack = 1(服务端seq 0 + SYN标志位 1,raw值是2608063952 + 1 = 2608063953)

通过三次握手,客户端与服务端协商好,客户端下一次从2608063953处接收,服务端从41766525123处接收。

状态变迁

  1. server.py启动后服务端处于LISTEN状态,客户端client.py发送SYN标志包后处于SYN_SENT状态
  2. 服务端收到该包后会返回SYN标志的应答包,从LISTEN切换为SYN_RCVD状态
  3. 客户端发送ACK标志的应答包,切换为ESTABLISHED状态
  4. 服务端收到ACK标志的包后,切换为ESTABLISHED状态

之后就可以进行数据传输了。

数据传输

  1. 使用我们的client.py给server.py发送hello,数据段长度为5,根据上面的握手协商,本次发送数据的seq为41766525123,ack不变



  1. 服务端接收到数据后给客户端发送ack,ack的值为 客户端seq 41766525123 + tcp segment len 5 = 41766525128,服务端的seq为握手协商好的2608063953
  1. 应答后我们的server.py要将hello发回客户端,于是数据段长度为5,seq为2608063953,ack跟第2步一样
  1. 客户端收到包后给服务端发送ack应答,ack值为服务端seq 2608063953 + 数据段长度 5 = 2608063958

四次挥手断开连接

TCP是双向传输的(全双工),两端都各自维护一份连接状态,因此每个方向必须单独的进行关闭,所以终止连接需要四次挥手(每一方都需要给对端发送FIN标志位的包,并且都需要给对方回复一个应答包):

  1. 为了实现四次挥手,TCP提供了半关闭的能力,即客户端发送FIN包后表示不会再发送数据,但是仍然可以接收数据,服务器会应答该FIN包。
  2. 期间服务端可以继续向客户端发送未完成的数据,服务端也不需要再发送时,会向客户端发送FIN包,客户端应答该FIN包,双方连接彻底关闭。

断开连接过程

断开的请求可以由任意一端发起,server.py是让服务端将客户端发来的内容发送出去后直接关闭连接,所以本次抓包是从服务端发起断开的

  1. 服务端发起关闭:开启FIN标志,seq = x,len = 0,上一个包没有数据,ACK不变

因为之前应答过客户端的hello,服务端seq变为2608063958,之前数据段的应答包已经发送过了(有时候会合并发送),所以ack不变为4176525128

  1. 客户端回包:对FIN标志段进行应答,数据段长度为0,ack = x + 1,seq = y,len = 0

同样因为应答过服务端的hello,客户端seq为4176525128,FIN标志占用一个seq,所以ack为服务端seq 2608063958 + FIN标志 1 = 2608063959

  1. 客户端发起关闭:开启FIN标志,seq = y, ack = x + 1,len = 0

客户端发起关闭,开启FIN标志,seq为4176525128,第2步回包已经应答过服务端的FIN包,ack不变

  1. 服务端回包:对FIN标志段进行应答,seq = x + 1, ack = y + 1, len = 0

服务端发起应答,第2步客户端会FIN包后告诉服务端下次从seq = 2608063959发送,所以seq = 2608063959,ack为客户端seq 4176525128 + FIN标志 1 = 4176525129

至此,双方连接彻底关闭。

状态

借用刘超老师的图,根据我们抓包的情况把左边看成服务端,右边看成客户端

  1. 传输数据过程中客户端和服务端都是ESTABLISHED状态,服务端发出FIN标志数据段后进入FIN_WAIT_1,等待客户端回包。如果服务端收不到ACK回包,会重传该报文(重传次数由tcp_orphan_retries控制),超时会断开连接。
  2. 客户端收到FIN包后,发送ACK包并进入CLOSE_WAIT状态,如果这个ACK丢失,服务端没有收到,服务端会重传FIN包再次等待客户端的ACK。
  3. 服务端收到ACK包后进入FIN_WAIT_2状态,等待客户端的FIN包发来。
    1. 如果调用close关闭连接,超过tcp_fin_timeout规定的时间,客户端没有发来FIN包,那么服务端会直接关闭
    2. 如果服务端调用shutdown来关闭连接,仍然可以接收数据,如果客户端没有发送FIN标志的包,那么服务端会一直处于FIN_WAIT_2状态
  4. 客户端发送FIN包后进入LAST_ACK,等待服务端的ACK,如果等不到会重传FIN包,超过tcp_orphan_retries就会断开连接
  5. 服务端收到客户端的FIN包,并发送ACK回包后进入TIME_WAIT状态,等待2MSL的时间后关闭连接,如果中间收到了客户端重发的FIN包,会重置2MSL的定时器。如果没有进行2MSL的等待直接退出,可能会出现客户端的FIN包无法收到ACK的情况,这时客户端再次发送FIN包会收到服务端RST的回包(Connection reset by peer)
  6. 客户端收到服务端的ACK后彻底关闭
TIME_WAIT的2MSL

MSL(Maximum Segment Lifetime)是报文最大生存时间,MSL>=TTL的时间,确保了超过该时间报文会在网络传输中被丢弃,TIME_WAIT设置为2倍MSL的设值允许报文至少被丢弃1次,linux停留在TIME_WAIT的时间为60秒,所以我们的server.py启动并运行完之后立马再次启动会bind失败,告知端口占用。

如果TIME_WAIT等待的时间不够可能会将旧的seq插入新的连接数据中(序列号回绕并延迟到达)

异常断开连接

TCP是通过内核管理的,应用层需要通过send/recv来发送和接受数据,如果连接断开后应用层继续recv,会收到Connection reset by peer(之前的项目中,Prometheus相关的日志会出现大量的Connection reset by peer,原因是后端收集数据太慢,而server又使用python的WSGI server,无法支持长连接,导致读取时对端已经关闭的情况),如果是send则会收到Broken pipe

无数据传输异常断开(tcp keepalive)

socket通过设置SO_KEEPALIVE启动keepalive,可以通过sysctl -a查看系统设置,若开启了keepalive,两端没有数据传输,一端崩溃,另一端发送探测包经过 7200 + 75 * 9 = 7875秒后认为对端挂了

bash 复制代码
# 过了7200秒后无数据交互启动探测
net.ipv4.tcp_keepalive_time = 7200
# 探测间隔时间为75秒
net.ipv4.tcp_keepalive_intvl = 75
# 一共探测9次,一直无响应就认为对端挂了,关闭连接
net.ipv4.tcp_keepalive_probes = 9

如果服务端没有开启keepalive,这个tcp连接会一直处于ESTABLISHED状态,服务端重启失效

端口失效(RST标志)

一般异常关闭连接的时候会使用RST标志,发出或收到该标志的内核会清理该连接相应的内存资源、端口。接收到RST的一端会收到Connection reset或者Connection refused。大概情况:

  1. 端口现在不可用
  2. socket关闭
    1. 客户端消息发送完之前关闭了socket,会发一个RST到服务端
    2. 服务端关闭了socket,客户端再发送消息,服务端会回复一个RST包
进程崩溃

上面说到TCP栈是由操作系统管理的,如果一方进程发生了崩溃并被系统感知,操作系统会与对端进行四次挥手结束连接

数据传输中主机崩溃
  1. 客户端崩溃后重启,服务端利用超时重传机制重传报文,客户端之前连接的上下文都不存在了,会发送RST包到服务端关闭连接
  2. 客户端永久下线,服务端超时重传达到最大超时时间 或**最大重传次数,**服务端会断开连接并通过socket发送ETIMEOUT到应用程序

TCP状态机

该状态图中A为客户端,B为服务端。由客户端发起连接,也由客户端发起关闭。

  • (1)(2)(3)(4)(5)表示发起连接的状态,可以对照三次握手
  • (一)(二)(三)(四)(五)(六)表示断开连接的状态,可以对照四次挥手
  • 实线为客户端A,虚线为服务端B

触发重传机制

超时重传

上面的连接建立、传输数据、连接关闭都会出现数据包未被接收的情况,发送端触发重传机制,TCP提供可靠传输依靠确认接收端已经收到了数据,也就是说通过数据发出去到收到接收端发来的ack报文才算是完成这一报文段的传输,用收到ack的时间戳减去发送数据的时间戳得到的差值就是这个包的RTT(Round-Trip Time),其中的问题是出去的包可能会丢失,对端收到数据后返回的ack也可能丢失。TCP通过在发送时设定一个定时器,如果超过定时器就重传数据,具体的问题就在于如何设置定时器间隔时间和重传的频率。

  1. 如果定时器时间设置过大,会出现网络利用率低的情况,丢了很久了才重传
  2. 如果定时器时间设置过小,可能网络只是延迟略大,第一个包还没到,触发重传的第二个包就发出来了,给链路增加了不必要的负载

所以重传定时器的时间应略大于RTT比较合适,而网络环境的速率是经常变化的。所以跟踪测量RTT并且根据该值来动态设置重传定时器RTO (Retransmission Time Out)

快速重传

快速重传的机制拥塞控制会用到,如果发送端接收到了3个相同的ack后,会在超时重传的定时器过期之前重传丢失的seq,比如发送了seq1~seq5,但是seq2、seq3都丢失了,接收端回复ack时回复的都是seq2的ack,再收到seq3的3个ack后,才能再重传seq3的ack。网络利用率严重下降。现在Linux中会开启net.ipv4.tcp_sack=1net.ipv4.tcp_dsack=1,分别对应SACK( Selective Acknowledgment)重传机制和Duplicate SACK重传机制。

SACK

在TCP首部options里设置SACK,接收端将已经收到的数据信息发送给发送端,这样发送端在收到三次重复ACK后启动快速重传机制,但是根据这个字段可以看到丢失的数据,重发则发送丢失的那些seq就可以了

Duplicate SACK

SACK主要是告诉发送端,哪些数据是重复发送了。可以判断出是ack应答丢了导致的重发,还是发送方的数据包延时到达导致的重发。

流量控制

滑动窗口

在我们数据传输部分的抓包中,本地网络栈加上数据包较小,都是一发一答的顺序来进行的。如果是远端服务器,RRT时间较长的话传输会变得低效,应答包不承载数据,只是告知发送端我的数据接收到哪里了,你下次从哪个seq开始发送,如果应答包丢了,发送端还得等着超时重传再收到ack后发送下一段。所以如果要提高传输效率,引入了滑动窗口的概念:接收端可以告诉客户端,我的缓冲区能放多少数据,你看着发。至于接收端发回的ack,如果中间的某次ack走丢了,比如200299的ack收到了,300399的走丢了,400~499的收到了,那么发送端就认为,500之前的所有数据接收端都收到了,不用重传,继续发送。或者是接收端先不发送ack,直接发送一个500的ack,这种方式叫做累计应答。发送端和接收端都要维护一个窗口用来限制收发数据的大小。

发送端窗口1

  • seq 1、2、3都发送并收到了确认
  • seq 4~9是已经发送但是未收到ack确认的
  • seq 10~12是发送端可以接着发送的
  • seq 13~15超过了当前窗口大小,发送端不能发送,否则发出去也会被接收端丢掉

接下来发送端继续发送10、11、12

服务端接收窗口1

  • 蓝色部分已经接收并发送了ack,但是应用层还没有读取,不占用窗口大小
  • 橙色部分是已经收到了数据,还没有确认,此时可以直接确认ack=7,发送端收到ack后就会知道4、5、6的数据包接收端已经接收到了。因为存在7、8、9还没有收到,所以无法发送ack=11的确认。
  • 红色部分超出窗口大小,无法接收

接下来服务端发送ack=7确认4、5、6已经收到,窗口滑动后如下

服务端接收窗口2

  • 4、5、6已经ack,窗口向右移动3个
  • 之前不能接收的13、14、15现在可以接收

客户端发送窗口2

  • 4、5、6已经ack,之前不可发送13、14、15已经发送等待服务端确认

窗口大小变化

应用程序无法及时读取缓存内容

  1. 窗口的大小是通过两端交互数据段中TCP首部的windows指定的,窗口大小即当前系统给TCP分配的缓存区大小受系统繁忙程度的影响,系统繁忙,应用层无法及时读取TCP缓冲区中的内容(图中蓝色部分),那么TCP的窗口大小就要减小,告诉发送端,那么发送端下次发送的数据就减少。
  2. 极端情况下减小到0,发送端不能再发送任何数据,这时会启动定时器来发送探测包看窗口何时变大,如果回复的windows大小仍然为0,就重新启动定时器。

操作系统减小TCP缓存

第一种情况缓冲区大小不变,只是改变接收窗口的大小。而更糟糕的情况是操作系统减小接收端缓冲区大小,TCP规定必须先减小窗口大小,然后才能减小缓冲区。如果发送端按照上次窗口的大小发送了120字节的数据,而应用层还没有处理缓冲区中的数据,操作系统将缓冲区减小60字节,此时接收端的窗口为60字节,发送窗口通告告诉客户端,但是消息已经发出,120字节的数据超过了当前窗口大小,发生丢包。

糊涂窗口综合症(Silly Window Syndrome)

接收端会通告一个小窗口,比方5字节的窗口,TCP首部的固定长度就有20字节,再加上IP首部等长度,发送端的一个包实际传输了5字节,但是包大小就有几十字节。显然网络利用率大大降低,这个症状就是糊涂窗口综合症。

为了避免该现象发生,根据TCP首部选项里的MSS大小做控制:

  1. 发送端满足以下条件之一才能发送
    1. 可以发送>=MSS长度的报文段
    2. 数据长度至少为接收端通告窗口大小的一半
    3. 之前数据的ack接收到之后
  2. 服务端通告窗口大小的方式
    1. 如果窗口大小小于MSS与1/2缓存大小中最小的一个,关闭窗口
    2. 窗口大小至少增长到MSS,或者超过1/2缓存大小,打开窗口

拥塞控制

流量控制是根据主机缓冲区大小调节滑动窗口来限制客户端的发送,而拥塞控制相当于慢慢试探网络带宽有多大,拥塞状况如何来调整发送端的发送速率,这里引入了拥塞窗口,实际的发送窗口等于滑动窗口和拥塞窗口中最小的一个。

拥塞窗口

拥塞窗口是由发送端决定的,试探的意思就是慢慢的增大拥塞窗口,如果触发了超时重传,就认为是网络拥塞,较小拥塞窗口

慢启动

建立连接后,每收到一个ACK就将拥塞窗口加1(单位为1个MSS):

  1. 收到第一个ACK时,拥塞窗口为1+1 = 2.
  2. 发送两个报文,收到两个ACK,拥塞窗口为2+2=4
  3. 4+4 = 8,指数型增长

增长到ssthresh(slow start threshold)65535 bytes这个值后,启用拥塞避免

拥塞避免

  1. 上面拥塞窗口增长到8,收到8个ACK后,每个增长1/8
  2. 下次发送9个,收到9个ACK后,每个增长1/9
  3. 下次发送10个,线性增长

增长到触发重传机制后,表示网络发生拥塞,启动快速重传

快速重传

比如当前窗口是12,将之前的拥塞窗口减半为6,再将减半的值设置给ssthresh=6,再进入快速重传:

  1. 如果收到了3个重复的ACK(类似快速重传算法),拥塞窗口+3(为了尽快将丢失的包重传)
  2. 重传丢失的包
  3. 再收到重复的ACK后,把拥塞窗口+1
  4. 直到收到了新的ACK,再将ssthresh设置为进入快速重传之前的值6
  5. 进入避免拥塞算法

UDP

与TCP不同,UDP的首部格式简单,传输时也不需要事先建立连接,即不需要客户端和服务端维护双方交互的状态;因此TCP可以以数据流的形式发送,而进程产生一个UDP数据报,组装成一份待发送的IP数据报,只能发送一个数据报或者接收一个数据报,加上UDP并无控制可言,所以很多行为与IP层类似。

首部格式

各字段含义

  • Source Port:发送端端口号,如果不需要回复消息,该字段设置为0
  • Destination Port:接收端端口号
  • Length:UDP首部长度+Data长度(因为IP数据报的最大长度为65535,UDP头+Data实际长度不大于 65535 - IP头20 = 65515)
  • Checksum:与TCP相同,校验和覆盖UDP的首部和Data部分,校验数据包的正确性。与TCP不同的是,UDP可以设置为0表示不校验(包括IP首部的地址和UDP首部都不校验)。与IP层相同,如果发送端没有计算校验和而接收端发现校验和有差错,那么数据包会被直接丢弃而不产生差错报文

场景

  1. UDP适用于对丢包不敏感并要求时延极低的应用,不考虑网络拥塞,一股脑往出发。
  2. 因为UDP并不需要维护端对端连接,可以应用于广播或者多播协议。例如基于UDP协议的TFTP、DHCP、VXLAN等
  3. 因为自身的简单,只承载传输的任务。所以应用层可以更灵活的按照自己的需求来做定制开发,把需要维护的状态放在应用层来做。

传输层的端口号

传输层的端口号用来分辨交给哪个应用程序处理

端口的使用

  1. 对于UDP和TCP在内核中是独立存在的,所以可以绑定相同的地址和端口
  2. 如果两个TCP程序要绑定相同的端口,那么需要绑定不同的IP地址
  3. 对于TCP,如果上文重启server.py后会提示Address already in use,开启多路复用(socket设置SO_REUSEPORT)允许第一个socket处于TIME_WAIT的情况下,第二个socket可以使用该地址和端口

端口范围

知名端口号(0~1023)

相当于一种约定,例如HTTP服务用80端口,HTTPs用443端口,FTP用21端口,SSH用22端口

登记端口号(1024~49151)

我们自己实现的服务器,从该范围内申请端口长时间使用

客户端端口号(49152~65535)

操纵系统动态分派,客户端通信临时使用的,用完之后连接关闭,操作系统回收端口号,分配给其他的进程使用

学习自:

《趣谈网络协议》刘超
《图解TCP/IP》
《图解HTTP》
《网络是怎样连接的》
《TCP/IP详解 卷一》
小林coding
https://www.xiaolincoding.com/network/3_tcp/tcp_down_and_crash.html
https://xiaolincoding.com/network/3_tcp/tcp_feature.html
https://blog.csdn.net/GV7lZB0y87u7C/article/details/121186808

相关推荐
A小辣椒15 小时前
TShark:Wireshark CLI 功能
linux
A小辣椒19 小时前
TShark:基础知识
linux
AlfredZhao21 小时前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao1 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush42 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5202 天前
Linux 11 动态监控指令top
linux
网络研究院3 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展