跳到主要内容

使用 firewalld 放开关闭端口

· 阅读需 1 分钟
素明诚
Full stack development

使用 firewalld 放开/关闭端口

确保 firewalld 服务正在运行

sudo systemctl start firewalld
sudo systemctl enable firewalld

使用 firewall-cmd 命令放开 3000 和 3001 端口

sudo firewall-cmd --zone=public --add-port=3000/tcp --permanent
sudo firewall-cmd --zone=public --add-port=3001/tcp --permanent

关闭 3000 和 3001 端口

sudo firewall-cmd --zone=public --remove-port=3000/tcp --permanent
sudo firewall-cmd --zone=public --remove-port=3001/tcp --permanent

重载防火墙以使更改生效

sudo firewall-cmd --reload

验证端口是否已放开

sudo firewall-cmd --list-all

Docker 批量删除所有容器

· 阅读需 1 分钟
素明诚
Full stack development

docker 批量删除所有容器

docker ps 这是查看 Docker 容器的状态的命令。

-a 显示所有容器,包括未运行的。

批量操作容器

停止所有容器

docker stop $(docker ps -aq)

解释

docker stop 停止一个或多个运行中的容器。

$(docker ps -aq) 子命令获取所有容器的 ID。

删除所有容器

docker rm $(docker ps -aq)

解释

docker rm 删除一个或多个容器。

$(docker ps -aq) 同上,获取所有容器的 ID。

其他命令

查看所有容器

docker ps -a

删除所有停止的容器

docker container prune

清理系统(包括未使用的镜像和容器)

docker system prune

删除所有未使用的镜像

docker rmi $(docker images -q)

云计算领域的 IaaSPaaSSaaSCaaSFaaSDBaaS 是什么

· 阅读需 4 分钟
素明诚
Full stack development

IaaS:智能基础设施即服务的未来(Infrastructure as a Service)

IaaS,作为提供虚拟化计算资源的服务模型,涵盖了服务器、存储和网络。这种模式让企业可以按需购买、使用计算资源,避免了高昂的硬件投资和维护成本。技术上,IaaS 使用虚拟化技术保证了不同用户资源的安全隔离,确保了服务的高效运行。云服务提供商通过计算资源使用量获得收益。Amazon EC2、Microsoft Azure Virtual Machines 和阿里云的 ECS 是此模型的杰出代表。例如,Netflix 利用 AWS 的 EC2 支撑其全球流媒体业务,通过动态调整资源,优化了成本。

PaaS:全方位平台即服务模型(Platform as a Service)

PaaS 提供了包含开发、测试、部署、运行和管理应用的完整平台。开发者可以专注于代码编写和业务逻辑实现,而无需管理底层硬件和操作系统。PaaS 解决了企业自建开发环境的时间和成本问题,通常采用订阅或按使用量付费的盈利模式。Heroku 和 Google App Engine 是优秀的 PaaS 产品,而百度云的 BAE 和华为云的 AppEngine 也提供了相应服务。Slack 利用 Heroku 在初创阶段快速开发和部署其聊天应用,缩短了产品上市时间。滴滴出行也曾使用类似的 PaaS 平台快速迭代和部署其服务。

SaaS:便捷高效的软件即服务应用(Software as a Service)

SaaS 提供了互联网上可直接使用的软件应用,免除了用户的安装和维护需求。这些应用通常采用云端架构、多租户模式和自动化部署技术,保证了软件的高可用性和灵活性。Salesforce 和 Office 365 是典型的 SaaS 应用,分别提供在线 CRM 系统和办公软件。钉钉和腾讯文档在中国提供企业通讯和文档协作的 SaaS 解决方案。Dropbox 和 Zoom 为用户提供在线存储和视频会议服务,展示了 SaaS 模型解决软件安装、维护和升级复杂性的优势。

CaaS:针对微服务架构的容器即服务模型(Container as a Service)

CaaS 为容器化应用的部署和管理提供了环境,特别适合于微服务架构的应用。这种模型简化了服务的部署、扩展和管理过程。Google Kubernetes Engine 和 Amazon EKS 提供了全面的容器编排和管理解决方案,而阿里云的 ACK 为开发者提供了强大的容器管理能力。例如,Spotify 使用 Google Kubernetes Engine 管理其庞大的微服务架构,实现了服务的高效扩展和故障恢复。

FaaS:无服务器计算的函数即服务框架(Function as a Service)

FaaS 提供了一个事件驱动的无服务器计算框架,使开发者能够编写由事件触发的短小业务逻辑函数,而无需关心底层服务器的管理。AWS Lambda 和 Azure Functions 允许用户编写函数响应各种事件,而阿里云的函数计算 FC 也提供了类似的执行环境。这些函数处理如图片上传后的自动缩放和优化,解决了服务器资源浪费和复杂扩展问题。

DBaaS:灵活高效的数据库即服务模型(Database as a Service)

DBaaS 提供了完整的数据库服务,用户无需关心底层硬件和数据库软件的维护。Amazon RDS 和 Azure SQL Database 允许用户快速部署和管理数据库,同时提供备份、恢复和扩展功能。腾讯云的云数据库 MySQL 在中国提供高效的数据库服务。电子商务网站利用 Amazon RDS 存储订单和用户信息,享受了高可用性和灵活扩展的优势。

TCP 三次握手的工作流程

· 阅读需 4 分钟
素明诚
Full stack development

工作流程

SYN:客户端发送一个 SYN(同步序列编号)包到服务器,并进入 SYN_SEND 状态,等待服务器确认。

SYN-ACK:服务器接收到 SYN 包,必须确认客户的 SYN(ACK)并同时自己也发送一个 SYN 包,即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态。

ACK:客户端收到服务器的 SYN+ACK 包后,向服务器发送确认包 ACK,此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。

详解

同步序列编号(SYN)

同步序列编号(SYN)是在 TCP 协议中用来标识数据包序列的数字,它是三次握手过程的一部分。在 TCP 连接的初始化阶段,SYN 标志被设置为 1,以表明一个连接请求或连接接受。序列编号是随机产生的,用于标记传输的数据包的顺序,确保数据的有序和完整性。

术语描述
SYN标志位,用于启动一个新连接,同时携带一个随机的序列号作为开始。

ESTABLISHED 状态

ESTABLISHED 状态表示 TCP 连接已经成功建立,两端的设备可以开始数据传输。在三次握手过程中,当双方交换完所有必要的确认消息后,连接状态就会变为 ESTABLISHED。这个状态是双向的,即数据可以从任一端向另一端发送。

打电话类似三次握手

  1. 拨打电话:当你拨打电话给某人时,你的电话会向对方的电话发送一个信号,请求建立连接(相当于发送 SYN)。
  2. 响铃并接听:对方的电话响铃,当他们接听时,他们的电话会发送一个信号回到你的电话,表明他们已准备好接受通话(相当于发送 SYN-ACK)。
  3. 开始通话:你听到对方接听的声音或信号后,通话开始(相当于发送 ACK),此时双方都知道连接已建立,可以开始通话了。

为什么是三次?两次不行吗?

在两次握手的情况下

  • 客户端发送一个带有 SYN 的数据包到服务器,请求建立连接。
  • 服务器返回一个带有 SYN 和 ACK 的数据包,表示接受连接请求。

这里的问题是,虽然服务器知道客户端能够接收数据(因为客户端收到了服务器的响应),但服务器没有确切的方式来确认客户端是否还在继续能够发送数据。如果在服务器响应之后,客户端发生故障或连接中断,服务器可能会在不知情的情况下保持一个无效的连接,这会浪费服务器资源。

防止旧连接请求的执行

三次握手的设计还可以防止已经失效的连接请求被错误地建立为新的连接。考虑这样一个场景

  • 客户端之前发送了一个连接请求(SYN),但因为某种原因(如网络延迟),这个请求在很长时间后才到达服务器。
  • 在两次握手的协议中,服务器会立即接受这个迟到的连接请求并发送确认(SYN-ACK),然后认为连接已建立。

在这种情况下,如果没有第三次握手(客户端的 ACK),服务器无法确定客户端是否真的还想建立连接或者是否已经接收到了它的 SYN-ACK 响应。这样可能导致服务器开启一些根本不需要的连接。

所以如果是两次握手存在导致资源浪费的风险

TCP 与 UDP 区别和场景

· 阅读需 5 分钟
素明诚
Full stack development

TCP(Transmission Control Protocol,传输控制协议)

特点

  • 可靠性:TCP 提供可靠的数据传输服务,通过序列号、确认应答、重传机制等确保数据完整性和正确性。
  • 有序性:TCP 保证数据包的顺序,数据将按照发送顺序到达接收端。
  • 流控制:TCP 使用窗口机制进行流控制,根据接收方的接收能力调整发送速率,避免发送方过快发送数据使接收方来不及处理。
  • 拥塞控制:TCP 具有拥塞控制机制,能够根据网络条件调整数据发送速率,减少网络拥塞。

适用场景

  • Web 页面加载:确保网页数据完整无误地传输。
  • 文件传输:如 FTP(文件传输协议)需要可靠地传输大文件。
  • 电子邮件传输:如 SMTP(简单邮件传输协议)要求邮件数据准确无误地送达。
  • 远程服务器访问:如 SSH(安全外壳协议)和 Telnet 依赖于 TCP 的可靠连接。

TCP 滑动窗口

TCP 滑动窗口是一种流量控制机制,用于控制发送方在等待接收方确认之前可以发送的数据量。这个窗口的大小决定了可以发送的未被确认的数据包的数量或大小,这有助于防止接收方被过量的数据淹没。

  • 窗口大小:接收方根据其当前的处理能力和缓冲区的大小来告诉发送方窗口的大小,即发送方在接收方发回确认之前可以发送的数据的最大量。
  • 动态调整:窗口大小会根据网络条件动态调整。如果网络状况良好,窗口会增大,允许发送更多的数据;如果网络状况不佳,窗口会减小,限制数据流量,避免数据丢失。

拥塞控制

拥塞控制是一种网络控制机制,用来防止过多的数据包在网络中同时传输,从而导致网络拥堵。TCP 通过几种算法来实施拥塞控制,这些算法包括慢启动、拥塞避免、快重传和快恢复。

  • 慢启动(Slow Start):连接开始时,拥塞窗口(cwnd)从一个很小的值开始,例如 1 个数据包,随着每次传输轮次的成功完成,窗口大小指数级增长,直到达到一个阈值(ssthresh)。
  • 拥塞避免(Congestion Avoidance):一旦窗口大小达到或超过阈值,增长策略会转变为线性增长,即每个传输轮次只增加一个数据包的大小,以减少引起拥塞的风险。
  • 快重传和快恢复(Fast Retransmit and Fast Recovery):当发送方收到三个重复确认(表示一个数据包可能已丢失),它会立即重传丢失的数据包,而不是等待重传计时器超时。此时,拥塞窗口和阈值会调整以快速恢复传输。

UDP(User Datagram Protocol,用户数据报协议)

特点

  • 无连接:UDP 是无连接的,发送数据之前不需要建立连接,减少了开销和延迟。
  • 高效:UDP 头部开销小,仅有 8 字节,相较于 TCP 的 20 字节更为高效。
  • 不保证可靠性:UDP 不保证数据包的顺序、完整性或者可靠性,不进行错误校正。
  • 无拥塞控制:UDP 不进行拥塞控制,即使网络条件不佳,仍然会以相同的速率发送数据。

适用场景

  • 实时应用:如语音通信、在线游戏和实时视频会议、直播,这些应用需要快速传输,可以容忍一定程度的数据丢失。
  • 广播和多播应用:UDP 支持发送单个数据包到多个接收者(广播)或者选定的多个接收组(多播)。
  • DNS 查询:域名系统(DNS)查询使用 UDP 进行快速查找,通常不需要可靠传输。

FAQ:BT 和 P2P 用的是什么协议?

BT 下载(BitTorrent)和 P2P(Peer-to-Peer)下载通常使用 TCP 协议,这与它们对数据完整性的高要求有关。这些下载方式涉及将大文件分割成多个小片段,并从不同的对等方(peers)下载这些片段,最终在本地组合成完整的文件。以下是对这些协议选择的详细说明

  • TCP 的可靠性:由于 BT 和 P2P 下载的文件往往较大,且来自多个不同的数据源,确保每一个数据片段的完整性和正确顺序对于最终文件的完整性至关重要。TCP 协议通过其内置的错误检查和纠正机制(如数据重传、数据包排序等),保证了数据的可靠传输。
  • 数据完整性:在 BT 或 P2P 下载中,每个文件片段的完整和准确至关重要,因为最终的文件需要所有片段完整无误才能正确组装和使用。TCP 保证每个数据包都将按顺序并完整地到达,如果发生丢包,TCP 将会重新发送丢失的数据包。
  • 拥塞控制:TCP 还具备拥塞控制机制,可以根据网络条件调整数据传输速率,这在多对等方环境中尤其重要,因为网络状况可能因参与者的不同而大相径庭。这有助于优化整体网络性能并避免网络拥塞。

虽然 TCP 是 BT 和 P2P 协议中常用的传输协议,但这并不排除在某些特定场景下使用 UDP。例如,一些 P2P 实时视频流或实时数据广播可能会选择 UDP,以减少延迟。但对于文件下载这类对数据完整性要求极高的应用,TCP 是更常见的选择

TCP 四次挥手过程

· 阅读需 3 分钟
素明诚
Full stack development

四次挥手的步骤

第一次挥手:客户端发送 FIN

  • 动作:客户端决定结束发送数据,向服务器发送一个带有 FIN 标志的数据包。
  • 状态变迁:客户端发送 FIN 后,进入 FIN-WAIT-1 状态,等待服务器的确认(ACK)。

第二次挥手:服务器发送 ACK

  • 动作:服务器收到客户端的 FIN 后,返回一个 ACK 数据包,确认序号为收到的序号加 1。
  • 状态变迁:客户端接收到 ACK 后,进入 FIN-WAIT-2 状态。服务器进入 CLOSE-WAIT 状态,准备结束自己的发送。

第三次挥手:服务器发送 FIN

  • 动作:服务器完成其数据的发送后,向客户端发送一个带有 FIN 标志的数据包,请求关闭连接。
  • 状态变迁:服务器发送完 FIN 后,等待客户端的最终 ACK 确认,进入 LAST-ACK 状态。

第四次挥手:客户端发送 ACK

  • 动作:客户端收到服务器的 FIN 后,返回一个 ACK 数据包,确认序号为收到的序号加 1。
  • 状态变迁:客户端发送 ACK 后,进入 TIME-WAIT 状态,保持该状态足够的时间以确保服务器接收到 ACK。该时间通常是最大报文段生存时间(Maximum Segment Lifetime, MSL)的两倍。完成这个等待后,客户端最终转入 CLOSED 状态。
方向描述客户端状态服务器状态
客户端 -> 服务器客户端发送 FIN,请求关闭连接FIN_WAIT_1-
服务器 -> 客户端服务器确认,发送 ACK 响应FIN_WAIT_2CLOSE_WAIT
服务器 -> 客户端服务器发送 FIN,请求关闭连接-LAST_ACK
客户端 -> 服务器客户端发送 ACK,确认收到关闭请求TIME_WAITCLOSED

为什么挥手需要四次?

为了保证数据完整传输

CLOSE-WAIT

等待关闭

TIME-WAIT

为了解决网络的丢包和网络不稳定所带来的其他问题,确保连接方能在时间范围内,关闭自己的连接

如何查看 TIME-WAIT 状态的链接数量?

netstat -an |grep TIMEWAIT|wc -l 查看连接数等待timewait状态连接数

为什么会 TIME-WAIT 过多?解决方法是怎样的?

  • 高频短连接:如果应用频繁地建立并快速关闭连接,那么在任意时刻都可能有大量的连接处于 TIME-WAIT 状态。这种情况在使用不当的客户端-服务器通信模型中很常见,如每个请求都建立新连接的 HTTP/1.0。而 HTTP/1.1 默认支持 HTTP Keep-Alive
  • 资源消耗:每个处于 TIME-WAIT 的连接都会占用服务器资源(如端口和内存),过多的 TIME-WAIT 连接可能耗尽这些资源,导致性能下降或无法建立新的连接。

Go 语言中的锁类型与应用场景详解

· 阅读需 3 分钟
素明诚
Full stack development

常用的锁的类型

锁类型描述用途
Mutex互斥锁,一次只允许一个 goroutine 访问某个资源。用于保护共享资源,避免并发写入时出现数据竞争。
RWMutex读写锁,允许多个读操作同时进行,但写操作是互斥的。适用于读多写少的场景,可以提高在频繁读取时的性能。
Once一次性锁,确保某个操作在全局范围内只被执行一次。主要用于资源的初始化,如单例模式的实现中保证只初始化一次。
Cond条件变量,用于等待或宣布事件的发生。当 goroutine 需要等待某个条件满足时使用,常与 Mutex 或 RWMutex 配合使用。

RWMutex vs. Mutex

Mutex(互斥锁)

  • 功能:提供基本的锁定功能,确保同一时刻只有一个 goroutine 可以访问资源。
  • 适用场景:适用于对数据进行频繁写操作的场景。

RWMutex(读写锁)

  • 优势:允许多个读取操作同时进行,而写操作则需独占资源。
  • 性能优化:在读多写少的应用场景中,RWMutex 比 Mutex 更高效,因为它减少了读操作的等待时间。

代码示例

Mutex(互斥锁)

package main

import (
"fmt"
"sync"
)

var mutex sync.Mutex
var balance int

func Deposit(amount int) {
defer mutex.Unlock()
mutex.Lock()
balance += amount
}

func Balance() int {
defer mutex.Unlock()
mutex.Lock()
return balance
}

func main() {
Deposit(100)
fmt.Println("Current Balance:", Balance())
}


RWMutex(读写锁)

package main

import (
"fmt"
"sync"
)

var rwMutex sync.RWMutex
var data2 int
var wg sync.WaitGroup

func ReadData(id int) {
defer rwMutex.RUnlock()
rwMutex.RLock()
fmt.Printf("Goroutine %d read data: %d\n", id, data2)

wg.Done()
}

func WriteData(id, d int) {
defer rwMutex.Unlock()
rwMutex.Lock()
data2 = d
fmt.Printf("Goroutine %d write data: %d\n", id, d)
wg.Done()
}

func main() {
// 启动多个读写goroutines
numGoroutines := 10
wg.Add(numGoroutines * 2) // 因为有numGoroutines个读goroutine和numGoroutines个写goroutine

for i := 0; i < numGoroutines; i++ {
go WriteData(i, i*10) // 每个goroutine写入不同的数据
go ReadData(i) // 同时进行读操作
}

wg.Wait() // 等待所有goroutine完成
fmt.Println("Final Data:", data2)
}


Once(一次性锁)

package main

import (
"fmt"
"sync"
)

var once sync.Once
var value int

func Setup() {
value = 42
fmt.Println("Value set")
}

func DoSetup() {
once.Do(Setup)
}

func main() {
go DoSetup()
go DoSetup()
go DoSetup()

// WaitGroup 等待 goroutines 完成
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
DoSetup()
}()
go func() {
defer wg.Done()
DoSetup()
}()
go func() {
defer wg.Done()
DoSetup()
}()
wg.Wait()
fmt.Println("value:", value)
}

Cond(条件变量)

package main

import (
"sync"
"time"
)

var cond = sync.NewCond(&sync.Mutex{})
var ready bool
var wg4 sync.WaitGroup

// process 处理特定的进程,等待条件变量通知
func process(i int) {
defer wg4.Done()
defer cond.L.Unlock()
cond.L.Lock()
for !ready {
cond.Wait()
}
println("进程", i, "已准备就绪")
}

func main() {
for i := 0; i < 5; i++ {
wg4.Add(1)
go process(i)
}
println("所有协程已创建")

time.Sleep(2 * time.Second) // 确保所有子协程都进入等待状态

cond.L.Lock()
ready = true
cond.Broadcast()
cond.L.Unlock()

wg4.Wait() // 等待所有子协程完成
}


Go 盐值加密和密码生成

· 阅读需 4 分钟
素明诚
Full stack development

盐值加密是一种安全策略,用于增强存储在数据库中的密码的安全性。在这个过程中,每个密码都会随机添加一个数据片段,即“盐”,这样即使两个用户使用相同的密码,它们存储在数据库中的形式也会不同。这种方法可以有效地抵抗彩虹表攻击等密码攻击技术。我尽可能的用最简短的话讲明白这个流程。

流程详解

首次设定密码时的操作

  • 用户设置密码。
  • 系统生成一个随机的盐值。
  • 系统将这个盐值和用户的密码合并后,通过哈希函数生成哈希值。
  • 将生成的盐值和哈希值一起存储在数据库中(通常是同一字段,例如在 bcrypt 中,盐和哈希值是一起编码在一个字符串中)。

用户登录时的操作

  • 用户输入密码。
  • 系统从数据库中取出含盐的哈希值。
  • 系统从存储的哈希值中解析出盐值(对于 bcrypt 等库,这一步是自动完成的)。
  • 系统使用提取出的盐值和用户输入的密码重新进行哈希操作。
  • 系统比较这个新生成的哈希值和数据库中存储的哈希值,如果相同,则验证成功;如果不同,则密码错误。

关键点

  • 盐的重用:验证密码时使用的盐是第一次生成并存储的盐,不是每次都重新生成的。这保证了只要输入的密码正确,无论验证多少次,生成的哈希值总是一致的。
  • 存储方式:在像 bcrypt 这样的现代密码哈希库中,盐值是自动与哈希值一起生成和存储的。你不需要手动处理盐的存储和提取,库会为你管理这些细节。

Go 语言实现盐值加密的示例

以下是一个使用 Go 语言和golang.org/x/crypto/bcrypt库来实现盐值哈希的示例。这个库自动处理盐的生成和存储。

package main

import (
"fmt"
"golang.org/x/crypto/bcrypt"
)

// GenerateHash 使用bcrypt生成盐值哈希
func GenerateHash(password string) (string, error) {
// GenerateFromPassword 自动添加盐并生成哈希
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashedPassword), nil
}

// ComparePasswords 比较用户输入的密码和数据库中的哈希
func ComparePasswords(hashedPwd string, plainPwd string) bool {
// CompareHashAndPassword 比较哈希和密码
err := bcrypt.CompareHashAndPassword([]byte(hashedPwd), []byte(plainPwd))
return err == nil
}

func main() {
// 用户设置的密码
password := "securepassword123"

// 生成哈希
hashedPassword, err := GenerateHash(password)
if err != nil {
fmt.Println("Error hashing password:", err)
return
}
fmt.Println("Hashed Password:", hashedPassword)

// 验证密码
match := ComparePasswords(hashedPassword, "securepassword123")
fmt.Println("Password match:", match)
}

使用bcrypt.GenerateFromPassword生成一个盐值哈希。这个函数自动为每个密码生成一个随机盐,并将盐和哈希存储在同一个字符串中。当需要验证密码时,使用bcrypt.CompareHashAndPassword可以自动处理盐的提取和比对过程。这使得实现更加简单且安全。

需要注意的点

合适的哈希算法

  • 安全性:选择已经经过广泛验证且被认为是安全的哈希函数,如bcryptscryptArgon2,而不是快速的哈希函数如 MD5 或 SHA-1,因为它们容易受到快速暴力攻击。
  • 更新性:随着计算能力的提升,一些原本认为安全的算法可能变得不再安全,因此需要持续关注和更新所使用的算法。

盐的唯一性和随机性

  • 随机性:盐应该是完全随机生成的,以确保每个哈希都是唯一的,即使是对于相同的密码。
  • 唯一性:为每个密码实例使用不同的盐,即使是同一个用户的密码更新,也应重新生成新的盐。

存储安全

  • 分离存储:虽然盐值需要和哈希值一起存储,但应确保这些信息的存储方式足够安全,例如使用加密的数据库或安全的存储解决方案。
  • 访问控制:确保只有必要的应用程序和人员能访问存储密码的数据库。

性能考虑

  • 处理时间bcrypt和类似的算法设计时就考虑到了耗时较长,以阻碍暴力破解攻击。但这也意味着在实际应用中可能需要考虑到算法对性能的影响。
  • 负载调整:可以通过调整算法的工作因子来平衡安全性和性能需求,如bcryptcost参数。

iptables 和 ufw 的常用命令

· 阅读需 2 分钟
素明诚
Full stack development

iptables 基础命令

基本语法

iptables [-t table] command chain rule-specification [options]

常用命令

命令描述
-A, --append在指定链的末尾添加规则
-D, --delete删除指定的规则
-I, --insert在指定位置插入规则
-L, --list列出指定链中的所有规则
-F, --flush清空指定链中的所有规则
-N, --new-chain创建新的用户自定义链
-X, --delete-chain删除用户自定义链
-P, --policy设置指定链的默认策略

常用示例

  1. 查看当前规则:
sudo iptables -L
  1. 允许特定端口:
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
  1. 允许特定 IP:
sudo iptables -A INPUT -s 192.168.1.100 -j ACCEPT
  1. 删除特定规则:
sudo iptables -D INPUT 1

ufw 基础命令

常用命令

命令描述
ufw enable启用防火墙
ufw disable禁用防火墙
ufw status查看防火墙状态
ufw allow允许端口或服务
ufw deny拒绝端口或服务
ufw delete删除规则
ufw reset重置为默认设置

常用示例

  1. 开启防火墙:
sudo ufw enable
  1. 允许特定端口:
sudo ufw allow 80/tcp
  1. 允许特定服务:
sudo ufw allow ssh
  1. 删除规则:
sudo ufw delete allow 80

注意事项

  1. 在远程服务器上修改防火墙规则时要特别小心,确保不会把自己锁在外面
  2. 建议先添加允许 SSH 连接的规则,再添加其他规则
  3. 对于生产环境,建议使用 ufw,它更简单且不容易配置错误
  4. 重要的更改前先备份当前规则

Go 自动解引用

· 阅读需 2 分钟
素明诚
Full stack development

访问数组或切片元素

当你拥有一个指向数组或切片的指针时,可以直接使用索引访问其元素,而无需手动解引用。

arr := [3]int{10, 20, 30}
pArr := &arr
fmt.Println(pArr[1]) // 输出 20,编译器自动处理了 (*pArr)[1]

访问结构体字段

指向结构体的指针,可以直接访问该结构体的任何字段,就像你在操作结构体实例一样

type Person struct {
Name string
Age int
}

p := &Person{"Alice", 30}
fmt.Println(p.Name) // 输出 "Alice",编译器自动处理了 (*p).Name

调用方法

type Person struct {
Name string
Age int
}

func (p *Person) Greet() {
fmt.Println("Hello,", p.Name)
}

func main() {
person := Person{"Bob", 25}
p := &person
person.Greet() // 可以直接调用
p.Greet() // 同样可以直接调用,Go自动处理了 (*p).Greet()
}

在这个例子中,不论是结构体实例还是其指针,Greet 方法都可以被调用,Go 语言运行时会自动调整。

注意

  • 在函数参数传递时,Go 中的所有参数都是按值传递的。这意味着如果你将一个结构体传递给函数,它会被复制。但是如果使用指针,则原始数据可以被函数修改。
  • Go 不支持像 C++那样的指针操作。如果你需要进行类似操作,通常可以使用切片或索引进行。