蛮荆

计算机网络中的设计模式

2022-07-25

代理模式

代理模式可以在不改变客户端代码的情况下,通过代理对象来控制对实际对象的访问。

网络中最高频出现的设计模式之一,在传输层之上的应用层使用非常广泛。

  • HTTP 代理
  • DNS 代理
  • 防火墙

伪代码

// 代理处理器伪代码实现
func proxyHandler(w http.ResponseWriter, r *http.Request) {
    // 解析目标服务器 URL
    target, err := url.Parse("https://dbwu.tech/")

    ...

    // 将客户端的请求转发给目标服务器
    proxyReq, err := http.NewRequest(r.Method, ...)

    ...

    // 发送请求到目标服务器
    client := &http.Client{}
    resp, err := client.Do(proxyReq)

    ...

    // 复制目标服务器的响应头
    for key, values := range resp.Header {
        for _, value := range values {
            w.Header().Add(key, value)
        }
    }

    // 设置响应状态码
    w.WriteHeader(resp.StatusCode)

    // 将目标服务器的响应内容复制到客户端响应
    io.Copy(w, resp.Body)
}

func main() {
    // 将代理处理器注册到根路径
    http.HandleFunc("/", proxyHandler)

    // 启动 HTTP 服务器
    log.Fatal(http.ListenAndServe(":8080", nil))
}

装饰者模式

最经典的就是发送方从应用层到物理层的各类协议头部格式,层层包装,接收方从物理层到应用层,反过来,层层解包装。

伪代码

采用 Golang Functional Options 风格来实现。

package main

// 所有协议和装饰器都要实现这个 Packet 接口
type Packet interface {
	GetContent() string
}

// PacketDecorator 装饰器
type PacketDecorator struct {
	packet Packet
	header string
}

func (d *PacketDecorator) GetContent() string {
	return d.header + d.packet.GetContent()
}

// Option 设置 PacketDecorator 的属性
// Functional Options 模式
type Option func(*PacketDecorator)

// WithHeader 负责装饰,也就是各类协议的头部格式包装
func WithHeader(header string) Option {
	return func(d *PacketDecorator) {
		d.header = header
	}
}

// ApplicationLayer 应用层
type ApplicationLayer struct {
	data string
}

func (a *ApplicationLayer) GetContent() string {
	return a.data
}

// TransportLayer 传输层装饰器
func TransportLayer(packet Packet, opts ...Option) Packet {
	decorator := &PacketDecorator{packet: packet}
	for _, opt := range opts {
		opt(decorator)
	}
	return decorator
}

// NetworkLayer 网络层装饰器
func NetworkLayer(packet Packet, opts ...Option) Packet {
	decorator := &PacketDecorator{packet: packet}
	for _, opt := range opts {
		opt(decorator)
	}
	return decorator
}

// DataLinkLayer 数据链路层装饰器
func DataLinkLayer(packet Packet, opts ...Option) Packet {
	decorator := &PacketDecorator{packet: packet}
	for _, opt := range opts {
		opt(decorator)
	}
	return decorator
}

// PhysicalLayer 物理层装饰器
func PhysicalLayer(packet Packet, opts ...Option) Packet {
	decorator := &PacketDecorator{packet: packet}
	for _, opt := range opts {
		opt(decorator)
	}
	return decorator
}

func main() {
	// 创建应用层的数据
	appData := &ApplicationLayer{data: "Hello, World!"}

	// 逐层包装
	transportPkt := TransportLayer(appData, WithHeader("[TCP Header]"))
	networkPkt := NetworkLayer(transportPkt, WithHeader("[IP Header]"))
	dataLinkPkt := DataLinkLayer(networkPkt, WithHeader("[Ethernet Header]"))
	physicalPkt := PhysicalLayer(dataLinkPkt, WithHeader("[Physical Layer Header]"))

	// 打印物理层数据
	println(physicalPkt.GetContent())
}

运行上面的代码,输出如下:

[Physical Layer Header][Ethernet Header][IP Header][TCP Header]Hello, World!

状态模式

典型的例子就是 TCP 协议的实现 (一个复杂的状态机),使用状态模式可以简单地模拟 TCP 的状态机的工作机制,每个状态都可以表示为一个类,并且每个类都有相应的状态转换逻辑。

伪代码

一个完整的 TCP 状态机实现比较复杂,这里以 TCP 三次握手来演示状态模式。

为了节省篇幅,这里只演示 SYN_SENT, SYN_RECEIVED, Established, Closed 4 种 TCP 连接状态。

package main

import (
	"fmt"
)

// TCPState 定义状态行为接口
type TCPState interface {
	Handle(context *TCPConnection)
	String() string
}

// TCPConnection 是状态机上下文
// 包含当前状态机器所处状态
type TCPConnection struct {
	state TCPState
}

func (c *TCPConnection) SetState(state TCPState) {
	c.state = state
}

func (c *TCPConnection) Handle() {
	c.state.Handle(c)
}

func (c *TCPConnection) String() string {
	return c.state.String()
}

// 下面是具体的状态转移和变化过程

// SYN_SENT 代表 TCP SYN_SENT 状态
type SYN_SENT struct{}

// SYN_RECEIVED 代表 TCP SYN_RECEIVED 状态
type SYN_RECEIVED struct{}

// Established 代表 TCP ESTABLISHED 状态
type Established struct{}

// Closed 表示 TCP 连接已关闭状态
type Closed struct{}

func (s *Closed) Handle(context *TCPConnection) {
	fmt.Println("Received SYN, transitioning to SYN_SENT")
	context.SetState(&SYN_SENT{})
}

func (s *Closed) String() string {
	return "CLOSED"
}

func (s *SYN_SENT) Handle(context *TCPConnection) {
	fmt.Println("Received SYN-ACK, transitioning to SYN_RECEIVED")
	context.SetState(&SYN_RECEIVED{})
}

func (s *SYN_SENT) String() string {
	return "SYN_SENT"
}

func (s *SYN_RECEIVED) Handle(context *TCPConnection) {
	fmt.Println("Received ACK, transitioning to ESTABLISHED")
	context.SetState(&Established{})
}

func (s *SYN_RECEIVED) String() string {
	return "SYN_RECEIVED"
}

func (s *Established) Handle(context *TCPConnection) {
	fmt.Println("Connection is already ESTABLISHED")
}

func (s *Established) String() string {
	return "ESTABLISHED"
}

func main() {
	// 默认情况下,连接处于关闭状态
	connection := &TCPConnection{state: &Closed{}}
	fmt.Println("Current State:", connection)
	fmt.Println("--------------------------")

	// 客户端发起建立连接请求,状态转移到 SYN_SENT
	connection.Handle()
	fmt.Println("Current State:", connection)
	fmt.Println("--------------------------")

	// 服务端接收到建立连接请求,状态转移到 SYN_RECEIVED
	connection.Handle()
	fmt.Println("Current State:", connection)
	fmt.Println("--------------------------")

	// 客户端接收到服务端的 ACK,状态转移到 ESTABLISHED
	connection.Handle()
	fmt.Println("Current State:", connection)
	fmt.Println("--------------------------")

	connection.Handle()
}

运行上面的代码,输出如下:

Current State: CLOSED
--------------------------
Received SYN, transitioning to SYN_SENT
Current State: SYN_SENT
--------------------------
Received SYN-ACK, transitioning to SYN_RECEIVED
Current State: SYN_RECEIVED
--------------------------
Received ACK, transitioning to ESTABLISHED
Current State: ESTABLISHED
--------------------------
Connection is already ESTABLISHED

策略模式

策略模式可以将请求的处理逻辑封装到不同的策略类中,并在运行时选择合适的策略来处理请求,使具体的算法处理逻辑可以灵活地扩展和修改。

TCP 协议中的拥塞控制算法、路由选择算法等都可以使用策略模式来确定具体使用哪种算法。

伪代码

package main

import "fmt"

// CongestionControl 定义拥塞控制算法接口
type CongestionControl interface {
	Execute()
}

// Reno 拥塞控制算法实现
type Reno struct{}

func (r *Reno) Execute() {
	fmt.Println("Using Reno Algorithm")
}

// Cubic 拥塞控制算法实现
type Cubic struct{}

func (c *Cubic) Execute() {
	fmt.Println("Using Cubic Algorithm")
}

// BBR 拥塞控制算法实现
type BBR struct{}

func (b *BBR) Execute() {
	fmt.Println("Using BBR Algorithm")
}

// Connection 是连接,可以选择使用具体的算法
type Connection struct {
	strategy CongestionControl
}

// SetStrategy 设置当前的算法
func (c *Connection) SetStrategy(strategy CongestionControl) {
	c.strategy = strategy
}

// Handle 执行当前的拥塞控制算法
func (c *Connection) Handle() {
	c.strategy.Execute()
}

func main() {
	connection := &Connection{}

	// 使用 Reno 拥塞控制算法
	reno := &Reno{}
	connection.SetStrategy(reno)
	connection.Handle()

	// 使用 Cubic 拥塞控制算法
	cubic := &Cubic{}
	connection.SetStrategy(cubic)
	connection.Handle()

	// 使用 BBR 拥塞控制算法
	bbr := &BBR{}
	connection.SetStrategy(bbr)
	connection.Handle()
}

运行上面的代码,输出如下:

Using Reno Algorithm
Using Cubic Algorithm
Using BBR Algorithm

扩展阅读

转载申请

本作品采用 知识共享署名 4.0 国际许可协议 进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,商业转载请联系作者获得授权。