网络缓冲区的设计

一、linux 接受发送数据
在这里插入图片描述

1.接受网络数据的流程
a.网卡收到数据包,通过DMA将数据包写入内存(ringbuffer结构)
b.网卡向cpu发起硬中断,cpu收到硬中断请求,根据中断表查找中断处理函数,调用中断处理函数
c.中断处理函数将屏蔽中断,发起软中断(避免cpu频繁被网卡中断,使用软中断处理耗时操作,避免执行时间过长,导致cpu没法响应其他硬件中断)
d.内核ksoftirqd线程负责软中断处理,该线程从ringbuffer中逐个取出数据帧到sk_buff
e.从帧头取出IP协议,判断是ipv4还是ipv6,去掉帧头帧尾
f.从IP头看上一个协议是tcp还是udp,根据五元组找到socket,并将数据提取出来放到socket的接受缓冲区
g.应用程序通过系统调用将socket的接受缓冲区的数据拷贝到应用层缓冲区
2.发送网络数据的流程(tcp)
a.应用程序通过系统调用将用户数据拷贝sk_buff并放到socket的发送缓冲区(udp没有发送缓冲区)
b.网络协议栈从socket的发送缓冲区取出sk_buff,并克隆一个新的sk_buf(tcp支持丢失重传)
c.向下传递依次增加TCP/UDP头部、IP头部,帧头(MAC头部)、帧尾
d.触发软中断通知网卡驱动程序,有新的网络包需要发送
e.网卡驱动程序从发送队列依次取出sk_buff写ringbuffer(内存DMA区域,网卡读到)
f.触发网卡发送,发送成功,触发硬中断,释放sk_buff和ringbuffer内存(tcp对应的是克隆而来的,udp对应的是原始的)
g.当收到tcp报文的ack应答时,将释放原始的sk_buff

二、内核态网络缓冲区(tcp每个连接都有一个发送接受缓冲区,udp没有发送缓冲区)
1.接受缓冲区:
接受客户端的数据(生产者)
用户内核取出数据(消费者)
2.发送缓冲区:
业务逻辑产生的数据(生产者)
协议栈从缓冲区取出数据并发送(消费者)

使用缓冲区解决生产速度大于消费者的问题
在这里插入图片描述

read/write (tcp)
recv/send(tcp/udp都可以使用,如果第四个参数为0,与read/write一样)

WSASend/WASrecv(异步io) :先注册读写事件(每次都需要调用一次函数),准备一个用户缓冲区,向内核发送一个请求,有数据的时候内核把数据拷贝到用户缓冲区,内核发送一个通知(事件),用户可以从用户缓冲区取出数据
网络编程处理4件事
1.连接的建立
2.连接的断开
3.数据的接受
4.数据的发送

三、为什么要用户态网络缓冲区
接收时,如果收到的不是完整的数据包(拆包粘包),需要对数据进行缓存。
发送时,如果缓冲区内存不够,需要对未发送的数据进行缓存。

四、如何设计?
1.定长buffer设计

char m_cbRecvBuf[16384];
INT nResultCode=recv(m_hSocketHandle,(char *)m_cbRecvBuf+m_wRecvSize,sizeof(m_cbRecvBuf)-m_wRecvSize,0);

结构简单,易于实现
缺点:需要频繁腾挪数据 需要实现扩容缩容机制
使用场景:发送的数据小,发送频率不高
2.ringbuffer设计(逻辑上的环结构,通过取余)
逻辑上的环形 通过头尾指针实现
优点:不要腾挪数据
缺点:需要扩容缩容机制,可能造成不连续空间

//结构体定义
struct ringbuffer_s {
    uint32_t size;
    uint32_t tail;
    uint32_t head;
    uint8_t * buf;
};
static uint32_t
rb_remain(buffer_t *r) {
    return r->size - r->tail + r->head;
}

int buffer_add(buffer_t *r, const void *data, uint32_t sz) {
    if (sz > rb_remain(r)) {
        return -1;
    }

    uint32_t i;
    i = min(sz, r->size - (r->tail & (r->size - 1)));

    memcpy(r->buf + (r->tail & (r->size - 1)), data, i);
    memcpy(r->buf, data+i, sz-i);

    r->tail += sz;
    return 0;
}


int buffer_remove(buffer_t *r, void *data, uint32_t sz) {
    assert(!rb_isempty(r));
    uint32_t i;
    sz = min(sz, r->tail - r->head);

    i = min(sz, r->size - (r->head & (r->size - 1)));
    memcpy(data, r->buf+(r->head & (r->size - 1)), i);
    memcpy(data+i, r->buf, sz-i);

    r->head += sz;
    return sz;
}


3.chainbuffer
解决动态扩容 不挪数据
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值