当前位置: 首页 > article >正文

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

和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.kler.cn/a/418148.html

相关文章:

  • Python语法基础(四)
  • 共享售卖机语音芯片方案选型:WTN6020引领智能化交互新风尚
  • Linux设置开启启动脚本
  • k8s 1.28 聚合层部署信息记录
  • 什么是人工智能大模型?
  • 芯科科技率先支持Matter 1.4,推动智能家居迈向新高度
  • 如何使用git fetch与git pull,在团队协作中二者有什么区别,具体案例分析并深入理解
  • AI开发-深度学习框架-PyTorch-torchnlp
  • 【看海的算法日记✨优选篇✨】第三回:二分之妙,寻径中道
  • 声音克隆技术:探索与实践 —— 从GPT-SoVITS V2到未来趋势20241201
  • 进程状态的学习
  • 【Qt中实现屏幕录制】
  • 用Leangoo领歌敏捷看板工具管理跨境电商物流出运的流程
  • redis面试复习
  • PyTorch介绍
  • 深度学习 | pytorch + torchvision + python 版本对应及环境安装
  • qt QLinearGradient详解
  • 【C++二分查找 前缀和】2333. 最小差值平方和|2011
  • Kubernetes集群操作
  • C++编程:模拟实现CyberRT的DataVisitor和DataDispatcher
  • openwrt利用nftables在校园网环境下开启nat6 (ipv6 nat)
  • AntFlow 0.20.0版发布,增加多数据源多租户支持,进一步助力企业信息化,SAAS化
  • Python基于 Opencv+wxPython 的人脸识别上课考勤系统,附源码
  • MySQL —— MySQL 程序
  • OpenCV4.8 开发实战系列专栏之 17 - 图像直方图
  • (SAST 检测规-5)不良授权和身份验证