计算机网络之 【TCP协议的启发】(C语言实现多态思想、UDP实现可靠传输思想)

目录

1.C语言实现多态思想

1.1虚函数表方式

1.2直接内嵌函数指针

2.UDP可靠传输思想

(2)场景选择


1.C语言实现多态思想

核心思想:分离接口与实现 + 函数指针动态绑定

(1)数据与行为分离

  • 数据:放在结构体中(如 struct socket)
  • 行为:通过函数指针指向具体实现(如 struct proto_ops)

(2)统一接口,多种实现

复制代码
// 统一的接口定义
struct proto_ops {
    int (*connect)(struct socket *sock);
    int (*send)(struct socket *sock, char *data);
};

// 不同的实现
const struct proto_ops tcp_ops = { tcp_connect, tcp_send };
const struct proto_ops udp_ops = { udp_connect, udp_send };

(3)运行时动态绑定

复制代码
// 编译时不知道具体类型
sock->ops->connect(sock);  // 运行时决定调用 tcp_connect 还是 udp_connect

1.1虚函数表方式

复制代码
#include <stdio.h>
#include <stdlib.h>

// 定义虚函数表(类似 struct proto_ops)
typedef struct {
    void (*speak)(void *obj);
    void (*run)(void *obj);
} AnimalOps;

// 定义基类结构(类似 struct socket)
typedef struct {
    AnimalOps *ops;  // 指向虚函数表
    char name[32];
} Animal;

// 具体的动物实现
typedef struct {
    Animal parent;  // 继承基类
    int bark_loudness;
} Dog;

typedef struct {
    Animal parent;
    int jump_height;
} Cat;

// Dog 的具体实现
static void dog_speak(void *obj) {
    Dog *d = (Dog *)obj;
    printf("%s: 汪汪!(音量: %d)\n", d->parent.name, d->bark_loudness);
}

static void dog_run(void *obj) {
    Dog *d = (Dog *)obj;
    printf("%s: 飞快地跑起来了\n", d->parent.name);
}

// Cat 的具体实现
static void cat_speak(void *obj) {
    Cat *c = (Cat *)obj;
    printf("%s: 喵喵~(跳跃高度: %dcm)\n", c->parent.name, c->jump_height);
}

static void cat_run(void *obj) {
    Cat *c = (Cat *)obj;
    printf("%s: 优雅地跳了 %dcm\n", c->parent.name, c->jump_height);
}

// 虚函数表实例(类似 inet_stream_ops)
static AnimalOps dog_ops = {
    .speak = dog_speak,
    .run = dog_run,
};

static AnimalOps cat_ops = {
    .speak = cat_speak,
    .run = cat_run,
};

// 创建具体对象
Dog* create_dog(const char *name, int loudness) {
    Dog *d = malloc(sizeof(Dog));
    snprintf(d->parent.name, sizeof(d->parent.name), "%s", name);
    d->parent.ops = &dog_ops;  // 绑定虚函数表
    d->bark_loudness = loudness;
    return d;
}

Cat* create_cat(const char *name, int height) {
    Cat *c = malloc(sizeof(Cat));
    snprintf(c->parent.name, sizeof(c->parent.name), "%s", name);
    c->parent.ops = &cat_ops;  // 绑定虚函数表
    c->jump_height = height;
    return c;
}

// 多态调用(类似 socket->ops->connect)
static inline void animal_speak(Animal *a) {
    a->ops->speak(a);
}

static inline void animal_run(Animal *a) {
    a->ops->run(a);
}

// 使用示例
int main() {
    Dog *dog = create_dog("旺财", 80);
    Cat *cat = create_cat("咪咪", 120);
    
    Animal *animals[] = {(Animal*)dog, (Animal*)cat};
    
    for (int i = 0; i < 2; i++) {
        animal_speak(animals[i]);  // 多态调用
        animal_run(animals[i]);
        printf("---\n");
    }
    
    free(dog);
    free(cat);
    return 0;
}

1.2直接内嵌函数指针

复制代码
#include <stdio.h>

// 基类定义(类似 struct socket)
typedef struct {
    void (*send)(void *obj, const char *data);
    void (*recv)(void *obj, char *buf);
} ProtocolOps;

// TCP 实现
typedef struct {
    ProtocolOps *ops;
    int port;
} TCPSocket;

void tcp_send(void *obj, const char *data) {
    TCPSocket *tcp = (TCPSocket*)obj;
    printf("TCP[port=%d] 发送: %s\n", tcp->port, data);
}

void tcp_recv(void *obj, char *buf) {
    printf("TCP 接收数据\n");
}

ProtocolOps tcp_ops = {
    .send = tcp_send,
    .recv = tcp_recv,
};

// UDP 实现
typedef struct {
    ProtocolOps *ops;
    int port;
} UDPSocket;

void udp_send(void *obj, const char *data) {
    UDPSocket *udp = (UDPSocket*)obj;
    printf("UDP[port=%d] 发送: %s (无连接)\n", udp->port, data);
}

void udp_recv(void *obj, char *buf) {
    printf("UDP 接收数据报\n");
}

ProtocolOps udp_ops = {
    .send = udp_send,
    .recv = udp_recv,
};

// 多态调用
void send_data(void *socket, ProtocolOps *ops, const char *data) {
    ops->send(socket, data);
}

int main() {
    TCPSocket tcp = {.ops = &tcp_ops, .port = 8080};
    UDPSocket udp = {.ops = &udp_ops, .port = 9090};
    
    send_data(&tcp, tcp.ops, "Hello TCP");
    send_data(&udp, udp.ops, "Hello UDP");
    
    return 0;
}
  • 在基类结构中存放函数指针表,让不同的派生类指向不同的函数实现,从而实现"同一个接口,不同行为"
  • 这是 Linux 内核中 struct socket + struct proto_ops 设计模式的核心思想,也是C语言实现面向对象编程的经典手法

2.UDP可靠传输思想

(1)为什么需要UDP可靠传输

对比项 TCP 原生UDP 可靠UDP
可靠性 确认重传 不保证 确认重传
顺序性 序号重组 可能乱序 序号重组
流量控制 滑动窗口 窗口机制
拥塞控制 复杂算法 简化版
头部开销 20字节 8字节 8+12~20字节
连接建立 三次握手 可选握手
传输效率 较低(HoL阻塞) 极高

队头阻塞(HoL阻塞)是指队列中首个数据包因丢失、延迟或拥塞而无法处理,导致后续所有包被迫等待的现象。 在不同场景中其严重程度不同:TCP按序交付机制导致连接级阻塞(影响较高),HTTP/1.1串行请求也存在类似问题;而在交换机架构中,通过虚拟输出队列(VOQ)等技术可将队头阻塞控制在较低水平,避免大幅影响整体吞吐(了解)

(2)场景选择

场景1:局域网文件传输(容忍少量丢包,要求简单)

  • 特点:丢包率低,延迟低
  • 方案:ACK确认 + 超时重传 + 固定窗口(类似TCP简化版)

场景2:实时游戏状态同步(要求低延迟,可丢旧包)

  • 特点:状态更新频繁,旧数据无意义
  • 方案:ACK + 最新状态覆盖 + 部分可靠(只重传关键包)

场景3:音视频流媒体(时序优先,可丢帧)

  • 特点:实时性强,丢失可容忍
  • 方案:选择性重传(NACK)+ FEC前向纠错

用UDP实现可靠传输本质上是在UDP之上重新实现TCP的核心机制(序号、ACK、重传、窗口、拥塞控制),但可以针对特定场景(游戏、视频、RPC)进行定制优化(如部分可靠、优先级调度、快速重传),牺牲通用性换取性能