DPDK用户态协议栈-Tcp Posix API 1

news2024/12/26 23:56:30

和udp一样,我们需要实现和系统调用一样的接口来实现我们的tcp server。先来看看我们之前写的unix_tcp使用了哪些接口,这边我加上两个系统调用,分别是接收数据和发送数据。

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <string.h>

int main(int argc, char* argv) {

    int sock = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in servaddr;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(9999);

    bind(sock, (struct sockaddr*)&servaddr, sizeof(struct sockaddr));
    listen(sock, 10);

    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);

    char buffer[64] = {0};

    int fd = accept(sock, (struct sockaddr*)&clientaddr, &len);

    while (1) {

        int nb_recv = recv(fd, buffer, 64, 0);

        if (nb_recv > 0) {

            printf("tcp recv : %s\n", buffer);

            send(fd, buffer, nb_recv, 0);

            memset(buffer, 0, 64);
        }
    }

    return 0;
}
  • socket

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240809084354.png

  • bind

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240809084428.png

  • listen

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240809084444.png

  • accept

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240809084509.png

  • recv

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240809084532.png

  • send

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240809084601.png

tcp的posix实现

bitmap

static struct localhost* get_host_fromfd(int sockfd) {

    struct localhost* htp = get_lhost_instance();
    struct localhost* host;
    for(host = htp; host != NULL; host = host->next) {

        if (host->fd == sockfd) {

            return host;
        }
    }

    struct ln_tcp_stream* stream;
    struct ln_tcp_table* table = get_tcp_table_instance();

    for(stream = table->streams; stream != NULL; stream = stream->next) {

        if (stream->fd == sockfd) {

            return stream;
        }
    }

    return NULL;
}

根据sockfd来搜索对应的控制块(tcp和udp同一个函数)。在之前的udp api实现的过程中实现了一个伪bitmap(后续会完善的)。现在加上了tcp,在函数中加上tcp控制块相关的遍历条件。

tcp server的最后两个状态

static int ln_tcp_handle_close_wait(struct ln_tcp_stream* stream, struct rte_tcp_hdr* tcphdr) {

    if (tcphdr->tcp_flags & RTE_TCP_FIN_FLAG) {

        if (stream->status == LN_TCP_STATUS_CLOSE_WAIT) {

            //
        }
    }

    return 0;
}

static int ln_tcp_handle_last_ack(struct ln_tcp_stream* stream, struct rte_tcp_hdr* tcphdr) {

    if (tcphdr->tcp_flags & RTE_TCP_ACK_FLAG) {

        if (stream->status == LN_TCP_STATUS_LAST_ACK) {

            stream->status = LN_TCP_STATUS_CLOSED;

            struct ln_tcp_table* table = get_tcp_table_instance();

            LL_REMOVE(stream, table->streams);
            table->count--;

            rte_ring_free(stream->recvbuf);
            rte_ring_free(stream->sendbuf);

            rte_free(stream);
        }
    }

    return 0;
}

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240809100144.png

修改一些之前的API

由于tcp和udp都有创建套接字,绑定等通用的部分,所以我们要在之前写的api上做一些增加和修改,让TCP和UDP都可以使用他们。

socket创建套接字
static struct localhost* get_host_fromip_port(uint32_t ip, uint16_t port, uint8_t proto) {

    struct localhost* htp = get_lhost_instance();
    struct localhost* host;
    for(host = htp; host != NULL; host = host->next) {

        if (host->localip == ip && host->localport == port && host->protocol == proto) {

            return host;
        }
    }

    return NULL;
}

int nsocket(__attribute__((unused)) int domain, int type, __attribute__((unused)) int protocol) {

    int fd = get_fd_frombitmap();

    if (type == SOCK_DGRAM) {

        struct localhost* host = rte_malloc("localhost", sizeof(struct localhost), 0);
        if (host == NULL) {

            return -1;
        }
        memset(host, 0, sizeof(struct localhost));

        host->fd = fd;

        if (type == SOCK_DGRAM) {

            host->protocol = IPPROTO_UDP;
        }

        host->recvbuf = rte_ring_create("recv buf", RING_SIZE, rte_socket_id(), RING_F_SC_DEQ | RING_F_SP_ENQ);
        if (host->recvbuf == NULL) {

            rte_free(host);
            return -1;
        }

        host->sendbuf = rte_ring_create("send buf", RING_SIZE, rte_socket_id(), RING_F_SC_DEQ | RING_F_SP_ENQ);
        if (host->sendbuf == NULL) {

            rte_ring_free(host->recvbuf);
            rte_free(host);
            return -1;
        }

        pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
        pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
        rte_memcpy(&host->mutex, &blank_mutex, sizeof(pthread_mutex_t));
        rte_memcpy(&host->cond, &blank_cond, sizeof(pthread_cond_t));

        struct localhost* lhp = get_lhost_instance();
        LL_ADD(host, lhp);

    }
    else if (type == SOCK_STREAM) {

        struct ln_tcp_stream* stream = rte_malloc("ln_tcp_stream", sizeof(struct ln_tcp_stream), 0);

        if (stream == NULL) {

            return -1;
        }

        stream->fd = fd;
        stream->proto = IPPROTO_TCP;
        stream->next = stream->prev = NULL;

        stream->recvbuf = rte_ring_create("tcp recv buf", RING_SIZE, rte_socket_id(), RING_F_SC_DEQ | RING_F_SP_ENQ);

        if (stream->recvbuf == NULL) {

            rte_free(stream);
            return -1;
        }

        stream->sendbuf = rte_ring_create("tcp send buf", RING_SIZE, rte_socket_id(), RING_F_SC_DEQ | RING_F_SP_ENQ);

        if (stream->sendbuf == NULL) {

            rte_ring_free(stream->sendbuf);
            rte_free(stream);
            return -1;
        }

        pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
        pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;

        rte_memcpy(&stream->cond, &blank_cond, sizeof(pthread_cond_t));
        rte_memcpy(&stream->mutex, &blank_mutex, sizeof(pthread_mutex_t));

        struct ln_tcp_table* table - get_tcp_table_instance();
        LL_ADD(stream, table->streams);
        table->count++;
    }

    return fd;
}

https://imagehyj.oss-cn-hangzhou.aliyuncs.com/blog/20240813175727.png

bind绑定
int nbind(int sockfd, const struct sockaddr *addr, __attribute__((unused)) socklen_t addrlen) {

    void* hostinfo = get_host_fromfd(sockfd);

    if (hostinfo == NULL) {

        return -1;
    }

    struct localhost* host = (struct localhost*)hostinfo;

    if (host->protocol == IPPROTO_UDP) {

            if (host == NULL) {

            return -1;
        }

        const struct sockaddr_in* laddr = (const struct sockaddr_in*)addr;
        host->localport = laddr->sin_port;
        rte_memcpy(&host->localip, &laddr->sin_addr.s_addr, sizeof(uint32_t));
        rte_memcpy(host->localmac, nSrcMac, RTE_ETHER_ADDR_LEN);
    }
    else {

        struct ln_tcp_stream* stream = (struct ln_tcp_stream*)hostinfo;

        const struct sockaddr_in* laddr = (const struct sockaddr_in*)addr;
        stream->dport = laddr->sin_port;
        rte_memcpy(&stream->dip, &laddr->sin_addr.s_addr, sizeof(uint32_t));
        rte_memcpy(&stream->localmac, nSrcMac, RTE_ETHER_ADDR_LEN);

        stream->status = LN_TCP_STATUS_CLOSED;
    }

    return 0;
}
close
int nclose(int fd) {


    void* hostinfo = get_host_fromfd(fd);

    if (hostinfo == NULL) {

        return -1;
    }

    struct localhost* host = (struct localhost*)hostinfo;

    if (host->protocol == IPPROTO_UDP) {

        struct localhost* lhp = get_lhost_instance();

        LL_REMOVE(host, lhp);

        if (host->recvbuf) {

            rte_ring_free(host->recvbuf);
        }

        if (host->sendbuf) {

            rte_ring_free(host->sendbuf);
        }

        rte_free(host);
        set_fd_frombitmap(fd);
    }
    else if (host->protocol == IPPROTO_TCP){

        struct ln_tcp_stream* stream = (struct ln_tcp_stream*)hostinfo;

        if (stream->status != LN_TCP_STATUS_LISTEN) {

            struct ln_tcp_fragment* fragment = rte_malloc("close frag", sizeof(struct ln_tcp_fragment), 0);

            if (fragment == NULL) {

                return -1;
            }
            memset(fragment, 0, sizeof(struct ln_tcp_stream));

            fragment->sport = stream->dport;
            fragment->dport = stream->sport;

            fragment->acknum = stream->recv_next;
            fragment->seqnum = stream->recv_next;

            fragment->windows = LN_TCP_INITIAL_WINDOWS;
            fragment->hdr_off = 0x50;
            fragment->tcp_flags = RTE_TCP_FIN_FLAG | RTE_TCP_ACK_FLAG;

            rte_ring_mp_enqueue(stream->sendbuf, (void*)fragment);

            stream->status = LN_TCP_STATUS_LAST_ACK;

            set_fd_frombitmap(fd);
        }
        else {

            struct ln_tcp_table* tb = get_tcp_table_instance();
            LL_REMOVE(stream, tb->streams);

            rte_free(stream);
        }
    }

    return 0;
}

tcp的专有API

listen监听
int nlisten(int sockfd, __attribute__((unused))int backlog) {

    void* hostinfo = get_host_fromfd(sockfd);

    if (hostinfo == NULL) {

        return -1;
    }

    struct ln_tcp_stream* stream = (struct ln_tcp_stream*)hostinfo;
    if (stream->proto == IPPROTO_TCP) {

        stream->status = LN_TCP_STATUS_LISTEN;
    }

    return 0;
}
accept建立连接
int naccept(int sockfd, struct sockaddr *addr, __attribute__((unused))socklen_t *addrlen) {

    void* hostinfo = get_host_fromfd(sockfd);

    if (hostinfo == NULL) {

        return -1;
    }

    struct ln_tcp_stream* stream = (struct ln_tcp_stream*)hostinfo;
    if (stream->proto == IPPROTO_TCP) {

        struct ln_tcp_stream* apt = NULL;

        pthread_mutex_lock(&stream->mutex);
        while ((apt = ln_get_accept_stream(stream->dport)) == NULL) {

            pthread_cond_wait(&stream->cond, &stream->mutex);
        }
        pthread_mutex_unlock(&stream->mutex);

        struct sockaddr_in* addri = (struct sockaddr_in*)addr;
        addri->sin_port = apt->sport;
        rte_memcpy(&addri->sin_addr.s_addr, &apt->sip, sizeof(uint32_t));

        return apt->fd;
    }

    return -1;
}

参考资料:https://github.com/0voice

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2251641.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记一次搞校园网的经历

接教室的校园网&#xff0c;到另一个屋子玩电脑&#xff0c;隔墙想放大一下AP的信号&#xff0c;发现死活不行 这是现状 由于校园网认证的存在&#xff0c;无法用桥接&#xff0c;桥接需要路由器有IP&#xff0c;而这个IP无法用未刷机的路由器来打开校园网页面认证 解决 将一…

RTC 实时时钟实验

利用 ALIENTEK 2.8 寸 TFTLCD 模块来显示日期和时间&#xff0c;实现一个简单的时钟。 STM32F1 RTC 时钟简介 STM32 的实时时钟&#xff08; RTC &#xff09;是一个独立的定时器。 STM32 的 RTC 模块拥有一组连续计数 的计数器&#xff0c;在相应软件配置下&#xf…

接口性能优化宝典:解决性能瓶颈的策略与实践

目录 一、直面索引 &#xff08;一&#xff09;索引优化的常见场景 &#xff08;二&#xff09;如何检查索引的使用情况 &#xff08;三&#xff09;如何避免索引失效 &#xff08;四&#xff09;强制选择索引 二、提升 SQL 执行效率 &#xff08;一&#xff09;避免不必…

2021陇剑杯-内存取证

内存分析&#xff08;问1&#xff09; 网管小王制作了一个虚拟机文件&#xff0c;让您来分析后作答&#xff1a; 虚拟机的密码是_____________。&#xff08;密码中为flag{xxxx}&#xff0c;含有空格&#xff0c;提交时不要去掉&#xff09;。 mimikatz一把梭了 flag{W31C0M3…

Ubuntu 安装 MariaDB

安装 MariaDB具体步骤 1、更新软件包索引&#xff1a; sudo apt update2、安装 MariaDB 服务器&#xff1a; sudo apt install mariadb-server3、启动 MariaDB 服务&#xff08;如果未自动启动&#xff09;&#xff1a; sudo systemctl start mariadb4、设置 MariaDB 开机启…

深度学习Python基础(2)

二 数据处理 一般来说PyTorch中深度学习训练的流程是这样的&#xff1a; 1. 创建Dateset 2. Dataset传递给DataLoader 3. DataLoader迭代产生训练数据提供给模型 对应的一般都会有这三部分代码 # 创建Dateset(可以自定义) dataset face_dataset # Dataset部分自定义过的…

Linux下的三种 IO 复用

目录 一、Select 1、函数 API 2、使用限制 3、使用 Demo 二、Poll 三、epoll 0、 实现原理 1、函数 API 2、简单代码模板 3、LT/ET 使用过程 &#xff08;1&#xff09;LT 水平触发 &#xff08;2&#xff09;ET边沿触发 4、使用 Demo 四、参考链接 一、Select 在…

Windows常用DOS指令(附案例)

文章目录 1.dir 查看当前目录2.cd 进入指定目录3.md 创建指定目录4.cd> 创建指定文件5.rd 删除指定空目录6.del 删除指定文件7.copy 复制文件8.xcopy 批量复制9.ren 改名10.type 在命令行空窗口打开文件11.cls 清空DOS命令窗口12.chkdsk 检查磁盘使用情况13.time 显示和设置…

【Linux】匿名管道通信场景——进程池

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…

C#基础之集合讲解

文章目录 1 集合1.1 数组1.1.1 简介1.1.2 声明使用1.1.2.1 声明 & 初始化1.1.2.2 赋值给数组1.1.2.3 访问数组元素 1.1.3 多维数组1.1.3.1 声明1.1.3.2 初始化二维数组1.1.3.3 访问二维数组元素 1.1.4 交错数组1.1.5 传递数组给函数1.1.6 Array1.1.6.1 简介1.1.6.2 属性1.1…

Azure DevOps Server:使用甘特图Gantt展示需求进度

自从Azure DevOps Server取消与Project Server的集成后&#xff0c;许多用户都在关注如何使用甘特图来展示项目进度。 在Azure DevOps Server开放扩展Extension功能后&#xff0c;许多开发者或专业开发团队做了很多甘特图Gantt相关的开发工作&#xff0c;使用比较多的是(GANTT …

数据湖的概念(包含数据中台、数据湖、数据仓库、数据集市的区别)--了解数据湖,这一篇就够了

文章目录 一、数据湖概念1、企业对数据的困扰2、什么是数据湖3、数据中台、数据湖、数据仓库、数据集市的区别 网上看了好多有关数据湖的帖子&#xff0c;还有数据中台、数据湖、数据仓库、数据集市的区别的帖子&#xff0c;发现帖子写的都很多&#xff0c;而且专业名词很多&am…

Kali Linux怎么开python虚拟环境

相信很多朋友再学习的过程中都会遇到一些pip失效&#xff0c;或者报错的时候&#xff0c;他们要求我们要使用虚拟环境&#xff0c;但是不知道怎么搭建&#xff0c;下面这篇文章就来告诉你如何搭建虚拟环境&#xff0c;这个方法在所有Linux的服务器都通用&#xff0c;就两行命令…

Flink四大基石之State(状态) 的使用详解

目录 一、有状态计算与无状态计算 &#xff08;一&#xff09;概念差异 &#xff08;二&#xff09;应用场景 二、有状态计算中的状态分类 &#xff08;一&#xff09;托管状态&#xff08;Managed State&#xff09;与原生状态&#xff08;Raw State&#xff09; 两者的…

【数据结构计数排序】计数排序

非比较排序概念 非比较排序是一种排序算法&#xff0c;它不是通过比较元素大小进行排序的&#xff0c;而是基于元素的特征和属性排序。这种排序方法在特定情况下&#xff0c;可以做到比元素比较排序&#xff08;快排&#xff0c;归并&#xff09;更有效率。尤其是在处理大量数…

Java GET请求 请求参数在Body中使用Json格式传参

业务需要调个三方接口 使用GET请求方式 但是&#xff01;请求参数不在Query中&#xff0c;竟然在Body中&#xff0c;使用Json格式传参 在API调试工具里面可以调通 在java代码里&#xff0c;死活调不通 网上搜了搜&#xff0c;找到一个靠谱的&#xff0c;记录一下 import o…

Linux的文件系统

这里写目录标题 一.文件系统的基本组成索引节点目录项文件数据的存储扇区三个存储区域 二.虚拟文件系统文件系统分类进程文件表读写过程 三.文件的存储连续空间存放方式缺点 非连续空间存放方式链表方式隐式链表缺点显示链接 索引数据库缺陷索引的方式优点&#xff1a;多级索引…

[golang][MAC]Go环境搭建+VsCode配置

一、go环境搭建 1.1 安装SDK 1、下载go官方SDK 官方&#xff1a;go 官方地址 中文&#xff1a;go 中文社区 根据你的设备下载对应的安装包&#xff1a; 2、打开压缩包&#xff0c;根据引导一路下一步安装。 3、检测安装是否完成打开终端&#xff0c;输入&#xff1a; go ve…

从繁琐到高效:智能生成PPT的神秘力量

在这个技术爆炸的时代&#xff0c;一场精彩的演讲离不开一份出色的PPT。但制作PPT&#xff0c;就像是一场与时间的博弈&#xff0c;费尽心思构思版式、精炼文案、选择配图&#xff0c;稍不留神&#xff0c;就会被拖入无底深渊。可是你知道吗&#xff1f;现在只需动动手指&#…

二分法篇——于上下边界的扭转压缩间,窥见正解辉映之光(1)

前言 二分法&#xff0c;这一看似简单却又充满哲理的算法&#xff0c;犹如一道精巧的数学之门&#xff0c;带领我们在问题的迷雾中找到清晰的道路。它的名字虽简单&#xff0c;却深藏着智慧的光辉。在科学的浩瀚星空中&#xff0c;二分法如一颗璀璨的星辰&#xff0c;指引着我们…