您的位置:首页 > 科技 > IT业 > 网页设计模板图片代码_苏州资讯网站建设_seo的实现方式_关键词优化推广排名软件

网页设计模板图片代码_苏州资讯网站建设_seo的实现方式_关键词优化推广排名软件

2024/11/18 11:12:11 来源:https://blog.csdn.net/weixin_45605341/article/details/142289159  浏览:    关键词:网页设计模板图片代码_苏州资讯网站建设_seo的实现方式_关键词优化推广排名软件
网页设计模板图片代码_苏州资讯网站建设_seo的实现方式_关键词优化推广排名软件

本机环境为 Ubuntu20.04 ,dpdk-stable-20.11.10

发送数据包的通用步骤

初始化DPDK环境: 调用 rte_eal_init() 来初始化DPDK的EAL(环境抽象层),这是所有DPDK应用程序的第一步,用于初始化硬件、内存和逻辑核心。

创建内存池使用: rte_pktmbuf_pool_create() 创建一个内存池(mempool),用于存储将要发送的网络数据包。每个数据包都会被分配一个内存缓冲区来存储数据。

初始化网卡设备:

  1. 调用rte_eth_dev_configure() 配置网卡,指定要使用的发送和接收队列数量。
  2. 调用rte_eth_rx_queue_setup() rte_eth_tx_queue_setup() 分别设置接收队列和发送队列。
  3. 使用 rte_eth_dev_start() 启动网卡设备。

创建UDP数据包 : 使用 rte_pktmbuf_alloc() 从内存池中分配一个 mbuf,并为数据包填充数据。构建UDP包的各个层次(以太网头部、IP头部、UDP头部)

发送数据包: 使用 rte_eth_tx_burst() 将数据包发送到指定的网卡端口和队列。该函数会发送一个或多个数据包,并返回实际发送的包数。

处理未发送成功的数据包: 如果数据包未成功发送,需要检查返回的数量,并适时释放未发送的 mbuf,避免内存泄漏。

信号处理机制

信号注册: 在 main() 函数中, 将 SIGINT(Ctrl+C) 和 SIGTERM 信号绑定到自定义的信号处理函数 signal_handler() 上。当程序运行时,如果用户发送这些信号(例如按下 Ctrl+C),就会调用 signal_handler() 函数。

signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);

信号处理函数: signal_handler() 是一个处理信号的函数,接收信号类型作为参数(signum)。该函数的作用是处理接收到的信号,并将全局变量 force_quit 设为 true,以通知主程序需要退出。

static void signal_handler(int signum)
{if (signum == SIGINT || signum == SIGTERM) {printf("\n\nSignal %d received, preparing to exit...\n",signum);force_quit = true;}
}

退出条件: 在主程序循环(例如 app_lcore_main_loop() 函数)中,会周期性地检查 force_quit 变量的状态。如果检测到 force_quit 为 true,则会打破循环,准备执行程序的清理和退出工作。

IP 和 UDP 头部设置

根据协议内容:完成IPv4和UDP头部的初始化和设置,包括端口号、数据包长度、IP地址、TTL等字段的填充,同时计算并设置了IP首部的校验和。

// 设置并初始化 IPv4 和 UDP 的头部信息
static void setup_pkt_udp_ip_headers(struct rte_ipv4_hdr *ip_hdr,struct rte_udp_hdr *udp_hdr,uint16_t pkt_data_len)
{uint16_t pkt_len;// 初始化UDP头部pkt_len = (uint16_t)(pkt_data_len + sizeof(struct rte_udp_hdr));udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);udp_hdr->dgram_cksum = 0;  // 不使用UDP校验// 初始化IP头部pkt_len = (uint16_t) (pkt_len + sizeof(struct rte_ipv4_hdr));ip_hdr->version_ihl   =IP_VERSION|IP_HDRLEN;ip_hdr->type_of_service   = 0;ip_hdr->fragment_offset = 0;ip_hdr->time_to_live   = IP_DEFTTL;ip_hdr->next_proto_id = IPPROTO_UDP;ip_hdr->packet_id = 0;ip_hdr->total_length   = rte_cpu_to_be_16(pkt_len);ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR); // 换为网络字节序ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR);// IP 首部校验和ip_hdr->hdr_checksum = 0; uint32_t ip_cksum = 0;// 将 IP 头部作为 16 位无符号整数数组处理uint16_t *ptr16 = (uint16_t *)ip_hdr;for (int i = 0; i < sizeof(struct rte_ipv4_hdr) / 2; i++) {if (i != 5) { // 校验和字段需要跳过ip_cksum += ptr16[i];}}// 循环进位,将结果压缩为 16 位并处理溢出。while (ip_cksum >> 16) {ip_cksum = (ip_cksum & 0xFFFF) + (ip_cksum >> 16);}ip_hdr->hdr_checksum = (uint16_t)(~ip_cksum & 0xFFFF);
}

数据包的组装

创建一组数据包缓冲区,并将指定的以太网头部(Ethernet header)、IPv4头部(IPv4 header)、UDP头部(UDP header)和数据(buf)拷贝到每个数据包的适当位置。

数据包的内存布局是以链表形式管理的。每个数据包(rte_mbuf结构体)可能由多个片段(segments)组成,每个片段包含一部分数据。如果直接将数据拷贝到错误的位置或者跨越多个片段拷贝而不考虑片段边界,会导致数据包的结构不正确,可能导致网络协议栈无法正确解析或处理这些数据包。

//  将一个内存缓冲区的内容(buf)拷贝到一个 DPDK 的数据包缓冲区中的多个片段中
static void copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset)
{struct rte_mbuf *seg = pkt;unsigned copy_len;void *seg_buf;// 定位到正确的片段while (offset >= seg->data_len) {offset -= seg->data_len;seg = seg->next;}// 从当前片段开始拷贝数据while (len > 0) {// 计算当前片段中需要拷贝的数据长度copy_len = seg->data_len - offset;if (len < copy_len) {copy_len = len;}seg_buf = rte_pktmbuf_mtod_offset(seg, char *, offset);rte_memcpy(seg_buf, buf, copy_len);len -= copy_len;buf = (char *)buf + copy_len;offset = 0;if (len > 0) {seg = seg->next;if (seg == NULL) {break; // 防止访问空片段}}}
}static inline void copy_buf_to_pkt(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset)
{if (offset + len <= pkt->data_len) {rte_memcpy(rte_pktmbuf_mtod_offset(pkt, char *, offset),buf, (size_t) len);return;}// 处理跨多个片段的拷贝操作copy_buf_to_pkt_segs(buf, len, pkt, offset);
}// 创建一组数据包缓冲区
static void create_pkt_mbuf_array(){struct rte_mbuf *pkt;struct rte_ether_hdr eth_hdr;unsigned pkt_data_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_udp_hdr) + sizeof(buf);for (uint16_t i = 0; i <MAX_PKT_BURST ; i++){// 分配一个bufpkt = rte_mbuf_raw_alloc(pktmbuf_pool);if (pkt == NULL) {printf("error: no enough pool!\n");continue; // 处理分配失败情况,继续下一个循环}// 重置pkt头部空间rte_pktmbuf_reset_headroom(pkt);pkt->data_len = pkt_data_len;pkt->next = NULL;// 设置以太网头部rte_ether_addr_copy(&des_eth_addrs, &eth_hdr.d_addr);rte_ether_addr_copy(&src_eth_addrs, &eth_hdr.s_addr);eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);// copy copy_buf_to_pkt(&eth_hdr, sizeof(eth_hdr), pkt, 0); // Ethcopy_buf_to_pkt(&pkt_ip_hdr, sizeof(pkt_ip_hdr), pkt, sizeof(struct rte_ether_hdr)); // IP headercopy_buf_to_pkt(&pkt_udp_hdr, sizeof(pkt_udp_hdr), pkt, sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr)); // UDP headercopy_buf_to_pkt(&buf, sizeof(buf), pkt, sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_udp_hdr)); // Data// 设置 rte_mbuf 参数:pkt->nb_segs = 1; pkt->pkt_len = pkt->data_len;pkt->ol_flags = 0;pkt->vlan_tci = 0;pkt->vlan_tci_outer = 0;pkt->l2_len = sizeof(struct rte_ether_hdr);pkt->l3_len = sizeof(struct rte_ipv4_hdr);pkt->l4_len = sizeof(struct rte_udp_hdr);mbuf_list[i] = pkt;      }
}

发送主循环

主要工作就是:设置数据包的IP和UDP头部,通过一个while循环发送数据包,调用create_pkt_mbuf_array()函数准备数据包。调用send_burst(port_id, tx_queue_id)函数发送数据包,并返回成功发送的数据包数量。使用rte_eth_tx_done_cleanup()清理已发送的数据包。

// 在一个网口发送数据包 
static inline int
send_burst(uint8_t portid, uint8_t queueid)
{uint16_t send;// 上锁,防止多线程同时访问发送队列rte_spinlock_lock(&spinlock_conf);// 发送数据包,send 是实际发送的数据包数量send = rte_eth_tx_burst(portid, queueid, mbuf_list, MAX_PKT_BURST);printf("-------- Data sent: %d packets\n", send);// 解锁rte_spinlock_unlock(&spinlock_conf);// 如果未能全部发送,释放未发送的数据包if (unlikely(send < MAX_PKT_BURST)) {for (uint16_t i = send; i < MAX_PKT_BURST; i++) {rte_pktmbuf_free(mbuf_list[i]);}}// 统计已发送数据包总数send_total += send;return send;
}
static int app_lcore_main_loop(__attribute__((unused)) void *arg)
{unsigned lcoreid;uint32_t count = 0; // 记录发送的包数uint32_t num = 0;   // 记录发送的包总数uint16_t ret;       // 记录每次发送成功的包数uint16_t pkt_data_len = sizeof(buf);  // 数据包的总长度struct rte_eth_stats port_stats;      // 记录端口统计数据struct timeval tv;                    // 用于计算时间lcoreid = rte_lcore_id(); // 获取当前核心 IDif (lcoreid == lcore_id) {printf("------- Sending from core %u\n", lcore_id);// 重置端口统计数据rte_eth_stats_reset(port_id);// 打印初始的统计数据if (rte_eth_stats_get(port_id, &port_stats) == 0) {printf("Initial stats:\n");printf("Received packets: %ld    Sent packets: %ld\n", port_stats.ipackets, port_stats.opackets);printf("Received bytes: %ld      Sent bytes: %ld\n", port_stats.ibytes, port_stats.obytes);printf("Receive errors: %ld      Send errors: %ld\n", port_stats.ierrors, port_stats.oerrors);printf("Missed packets: %ld     RX no buffer: %ld\n", port_stats.imissed, port_stats.rx_nombuf);}// 开始计时gettimeofday(&tv, NULL);int starttime = tv.tv_sec * 1000000 + tv.tv_usec; // 转换为微秒// 设置数据包的 IP 和 UDP 头部setup_pkt_udp_ip_headers(&pkt_ip_hdr, &pkt_udp_hdr, pkt_data_len);printf("setup_pkt_udp_ip_headers\n");while (num < SEND_TOTAL) {if (force_quit) // 检查是否需要退出break;// 准备数据包并发送create_pkt_mbuf_array(); // 组装数据包ret = send_burst(port_id, tx_queue_id); // 发送数据包rte_eth_tx_done_cleanup(port_id, tx_queue_id, 0); // 清理已发送的包count += ret; // 累加成功发送的包数num++;        // 累加总发送包数}// 结束计时gettimeofday(&tv, NULL);int endtime = tv.tv_sec * 1000000 + tv.tv_usec; // 转换为微秒int time = endtime - starttime; // 计算总耗时// 打印发送后的统计数据if (rte_eth_stats_get(port_id, &port_stats) == 0) {printf("Final stats:\n");printf("Received packets: %ld    Sent packets: %ld\n", port_stats.ipackets, port_stats.opackets);printf("Received bytes: %ld      Sent bytes: %ld\n", port_stats.ibytes, port_stats.obytes);printf("Receive errors: %ld      Send errors: %ld\n", port_stats.ierrors, port_stats.oerrors);printf("Missed packets: %ld     RX no buffer: %ld\n", port_stats.imissed, port_stats.rx_nombuf);}// 打印发送的总包数和耗时printf("------- Total sent: %d  Count: %d  Time: %d microseconds\n", send_total, count, time);    }return 0;
}

编译运行

在这里插入图片描述

完整代码

完整代码也可见 github

// 标准C库头文件
#include <stdio.h>         // 标准输入输出库
#include <stdlib.h>        // 标准库,包含内存分配、进程控制等
#include <stdint.h>        // 定义固定大小的整数类型
#include <inttypes.h>      // 提供用于格式化固定大小整数类型的宏
#include <string.h>        // 字符串处理函数库
#include <stdarg.h>        // 用于处理变长参数的宏
#include <errno.h>         // 错误号处理
#include <stdbool.h>       // 布尔类型定义
#include <time.h>          // 时间处理函数
#include <sys/types.h>     // 定义数据类型,如`size_t`,`ssize_t`
#include <linux/if_ether.h> // 以太网常量// Linux系统库头文件
#include <sys/queue.h>     // 定义队列和链表的宏和类型
#include <getopt.h>        // 命令行选项解析
#include <signal.h>        // 信号处理库
#include <sys/time.h>      // 获取时间的库函数(用于高精度时间)// DPDK 核心库头文件
#include <rte_common.h>        // DPDK中的通用定义
#include <rte_log.h>           // DPDK日志系统
#include <rte_memory.h>        // DPDK内存管理相关函数
#include <rte_memcpy.h>        // 高效的内存拷贝函数
#include <rte_memzone.h>       // DPDK内存区域管理
#include <rte_malloc.h>        // DPDK内存分配函数
#include <rte_ring.h>          // DPDK环形缓冲区
#include <rte_mempool.h>       // DPDK内存池管理
#include <rte_mbuf.h>          // DPDK数据包缓冲区结构和操作// DPDK 环境初始化库头文件
#include <rte_eal.h>           // DPDK环境抽象层(EAL)初始化
#include <rte_per_lcore.h>     // DPDK每核特定变量
#include <rte_launch.h>        // 启动函数
#include <rte_atomic.h>        // 原子操作
#include <rte_spinlock.h>      // 自旋锁
#include <rte_cycles.h>        // CPU周期数函数
#include <rte_prefetch.h>      // 缓存预取函数
#include <rte_lcore.h>         // 核心绑定函数
#include <rte_branch_prediction.h> // 分支预测优化
#include <rte_interrupts.h>    // 中断处理
#include <rte_pci.h>           // PCI设备管理
#include <rte_random.h>        // 随机数生成
#include <rte_debug.h>         // 调试相关功能// DPDK 网络相关头文件
#include <rte_ether.h>         // 以太网帧的定义和处理
#include <rte_ethdev.h>        // DPDK中的以太网设备驱动// DPDK IP、TCP、UDP协议栈相关头文件
#include <rte_ip.h>            // IP协议处理
#include <rte_tcp.h>           // TCP协议处理
#include <rte_udp.h>           // UDP协议处理// DPDK 字符串操作头文件
#include <rte_string_fns.h>    // 字符串处理函数库// IP地址  UDP端口
#define IP_SRC_ADDR ((192U << 24) | (168 << 16) | (131 << 8) | 152)
#define IP_DST_ADDR ((192U << 24) | (168 << 16) | (131 << 8) | 130)
#define UDP_SRC_PORT 1024
#define UDP_DST_PORT 1024#define MAX_PKT_BURST 32
#define RX_RING_SIZE 128  //发送环形缓冲区
#define NUM_MBUFS 8191  //数据包缓冲池
#define MBUF_CACHE_SIZE 256  //内存池中每个缓存的大小(以数据包为单位)
#define BURST_SIZE 32  //批量处理的大小
#define SEND_TOTAL 1 //发送#define IP_DEFTTL 64 
#define IP_VERSION 0x40 
#define IP_HDRLEN 0x05 //默认头部为20字节static volatile bool force_quit;//程序强制退出标识符
struct rte_mempool *pktmbuf_pool;
struct rte_mbuf *mbuf_list[MAX_PKT_BURST];//对应的rte_mbuf结构指针数组。32static struct rte_ipv4_hdr  pkt_ip_hdr;
static struct rte_udp_hdr pkt_udp_hdr;
struct rte_ether_addr des_eth_addrs;//目的mac
struct rte_ether_addr src_eth_addrs;//源mac
struct rte_ether_addr eth_addrs;unsigned socket_id;
unsigned port_id;
unsigned lcore_id;
unsigned rx_queue_id;
unsigned tx_queue_id;uint32_t send_total = 0;rte_spinlock_t spinlock_conf = RTE_SPINLOCK_INITIALIZER; //自旋锁,来保证对一个网口竞争访问;static const struct rte_eth_conf port_conf_default = {.rxmode = { .max_rx_pkt_len = RTE_ETHER_MAX_LEN,/*.offloads = DEV_RX_OFFLOAD_VLAN_STRIP|DEV_RX_OFFLOAD_VLAN_FILTER|DEV_RX_OFFLOAD_MACSEC_STRIP,// 启用硬件 VLAN 过滤功能 启用硬件 VLAN 标签剥离功能 启用硬件 CRC 去除功能*/},/*.rx_adv_conf={.rss_conf={.rss_key = NULL,.rss_hf = ETH_RSS_IP,},},*/.txmode={.mq_mode = ETH_MQ_TX_NONE, // 不使用多队列模式}
};char buf[64]="Partial string initialization";static void signal_handler(int signum)
{if (signum == SIGINT || signum == SIGTERM) {printf("\n\nSignal %d received, preparing to exit...\n",signum);force_quit = true;}
}static void print_ethaddr(const char *name, const struct ether_addr *eth_addr)
{char buf[48];rte_ether_format_addr(buf, 48, eth_addr);printf("%s%s \n", name, buf);
}// 设置并初始化 IPv4 和 UDP 的头部信息
static void setup_pkt_udp_ip_headers(struct rte_ipv4_hdr *ip_hdr,struct rte_udp_hdr *udp_hdr,uint16_t pkt_data_len)
{uint16_t pkt_len;// 初始化UDP头部pkt_len = (uint16_t)(pkt_data_len + sizeof(struct rte_udp_hdr));udp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);udp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_len);udp_hdr->dgram_cksum = 0;  // 不使用UDP校验// 初始化IP头部pkt_len = (uint16_t) (pkt_len + sizeof(struct rte_ipv4_hdr));ip_hdr->version_ihl   =IP_VERSION|IP_HDRLEN;ip_hdr->type_of_service   = 0;ip_hdr->fragment_offset = 0;ip_hdr->time_to_live   = IP_DEFTTL;ip_hdr->next_proto_id = IPPROTO_UDP;ip_hdr->packet_id = 0;ip_hdr->total_length   = rte_cpu_to_be_16(pkt_len);ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR); // 换为网络字节序ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR);// IP 首部校验和ip_hdr->hdr_checksum = 0; uint32_t ip_cksum = 0;// 将 IP 头部作为 16 位无符号整数数组处理uint16_t *ptr16 = (uint16_t *)ip_hdr;for (int i = 0; i < sizeof(struct rte_ipv4_hdr) / 2; i++) {if (i != 5) { // 校验和字段需要跳过ip_cksum += ptr16[i];}}// 循环进位,将结果压缩为 16 位并处理溢出。while (ip_cksum >> 16) {ip_cksum = (ip_cksum & 0xFFFF) + (ip_cksum >> 16);}ip_hdr->hdr_checksum = (uint16_t)(~ip_cksum & 0xFFFF);
}//  将一个内存缓冲区的内容(buf)拷贝到一个 DPDK 的数据包缓冲区中的多个片段中
static void copy_buf_to_pkt_segs(void *buf, unsigned len, struct rte_mbuf *pkt, unsigned offset)
{struct rte_mbuf *seg = pkt;unsigned copy_len;void *seg_buf;// 定位到正确的片段while (offset >= seg->data_len) {offset -= seg->data_len;seg = seg->next;}// 从当前片段开始拷贝数据while (len > 0) {// 计算当前片段中需要拷贝的数据长度copy_len = seg->data_len - offset;if (len < copy_len) {copy_len = len;}seg_buf = rte_pktmbuf_mtod_offset(seg, char *, offset);rte_memcpy(seg_buf, buf, copy_len);len -= copy_len;buf = (char *)buf + copy_len;offset = 0;if (len > 0) {seg = seg->next;if (seg == NULL) {break; // 防止访问空片段}}}
}static inline void copy_buf_to_pkt(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset)
{if (offset + len <= pkt->data_len) {rte_memcpy(rte_pktmbuf_mtod_offset(pkt, char *, offset),buf, (size_t) len);return;}// 处理跨多个片段的拷贝操作copy_buf_to_pkt_segs(buf, len, pkt, offset);
}// 创建一组数据包缓冲区
static void create_pkt_mbuf_array(){struct rte_mbuf *pkt;struct rte_ether_hdr eth_hdr;unsigned pkt_data_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_udp_hdr) + sizeof(buf);for (uint16_t i = 0; i <MAX_PKT_BURST ; i++){// 分配一个bufpkt = rte_mbuf_raw_alloc(pktmbuf_pool);if (pkt == NULL) {printf("error: no enough pool!\n");continue; // 处理分配失败情况,继续下一个循环}// 重置pkt头部空间rte_pktmbuf_reset_headroom(pkt);pkt->data_len = pkt_data_len;pkt->next = NULL;// 设置以太网头部rte_ether_addr_copy(&des_eth_addrs, &eth_hdr.d_addr);rte_ether_addr_copy(&src_eth_addrs, &eth_hdr.s_addr);eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);// copy 这里优化可以使用dpdk的函数copy_buf_to_pkt(&eth_hdr, sizeof(eth_hdr), pkt, 0); // Ethcopy_buf_to_pkt(&pkt_ip_hdr, sizeof(pkt_ip_hdr), pkt, sizeof(struct rte_ether_hdr)); // IP headercopy_buf_to_pkt(&pkt_udp_hdr, sizeof(pkt_udp_hdr), pkt, sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr)); // UDP headercopy_buf_to_pkt(&buf, sizeof(buf), pkt, sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_udp_hdr)); // Data/* dpdk优化版struct rte_ether_hdr* eth_hdr;struct rte_ipv4_hdr *ip_hdr;struct rte_udp_hdr *udp_hdr;ip_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);*ip_hdr = pkt_ip_hdr;ip_hdr->total_length = rte_cpu_to_be_16(pkt_data_len - sizeof(struct rte_ether_hdr));udp_hdr = (struct rte_udp_hdr *)(ip_hdr + 1);*udp_hdr = pkt_udp_hdr;udp_hdr->dgram_len = rte_cpu_to_be_16(pkt_data_len - sizeof(struct rte_ether_hdr) - sizeof(struct rte_ipv4_hdr));void *pkt_data = (void *)(udp_hdr + 1);rte_memcpy(pkt_data, buf, sizeof(buf));*/// 设置 rte_mbuf 参数:pkt->nb_segs = 1; pkt->pkt_len = pkt->data_len;pkt->ol_flags = 0;pkt->vlan_tci = 0;pkt->vlan_tci_outer = 0;pkt->l2_len = sizeof(struct rte_ether_hdr);pkt->l3_len = sizeof(struct rte_ipv4_hdr);pkt->l4_len = sizeof(struct rte_udp_hdr);mbuf_list[i] = pkt;      }
}// 在一个网口发送数据包 
static inline int
send_burst(uint8_t portid, uint8_t queueid)
{uint16_t send;// 上锁,防止多线程同时访问发送队列rte_spinlock_lock(&spinlock_conf);// 发送数据包,send 是实际发送的数据包数量send = rte_eth_tx_burst(portid, queueid, mbuf_list, MAX_PKT_BURST);printf("-------- Data sent: %d packets\n", send);// 解锁rte_spinlock_unlock(&spinlock_conf);// 如果未能全部发送,释放未发送的数据包if (unlikely(send < MAX_PKT_BURST)) {for (uint16_t i = send; i < MAX_PKT_BURST; i++) {rte_pktmbuf_free(mbuf_list[i]);}}// 统计已发送数据包总数send_total += send;return send;
}static int app_lcore_main_loop(__attribute__((unused)) void *arg)
{unsigned lcoreid;uint32_t count = 0; // 记录发送的包数uint32_t num = 0;   // 记录发送的包总数uint16_t ret;       // 记录每次发送成功的包数uint16_t pkt_data_len = sizeof(buf);  // 数据包的总长度struct rte_eth_stats port_stats;      // 记录端口统计数据struct timeval tv;                    // 用于计算时间lcoreid = rte_lcore_id(); // 获取当前核心 IDif (lcoreid == lcore_id) {printf("------- Sending from core %u\n", lcore_id);// 重置端口统计数据rte_eth_stats_reset(port_id);// 打印初始的统计数据if (rte_eth_stats_get(port_id, &port_stats) == 0) {printf("Initial stats:\n");printf("Received packets: %ld    Sent packets: %ld\n", port_stats.ipackets, port_stats.opackets);printf("Received bytes: %ld      Sent bytes: %ld\n", port_stats.ibytes, port_stats.obytes);printf("Receive errors: %ld      Send errors: %ld\n", port_stats.ierrors, port_stats.oerrors);printf("Missed packets: %ld     RX no buffer: %ld\n", port_stats.imissed, port_stats.rx_nombuf);}// 开始计时gettimeofday(&tv, NULL);int starttime = tv.tv_sec * 1000000 + tv.tv_usec; // 转换为微秒// 设置数据包的 IP 和 UDP 头部setup_pkt_udp_ip_headers(&pkt_ip_hdr, &pkt_udp_hdr, pkt_data_len);printf("setup_pkt_udp_ip_headers\n");while (num < SEND_TOTAL) {if (force_quit) // 检查是否需要退出break;// 准备数据包并发送create_pkt_mbuf_array(); // 组装数据包ret = send_burst(port_id, tx_queue_id); // 发送数据包rte_eth_tx_done_cleanup(port_id, tx_queue_id, 0); // 清理已发送的包count += ret; // 累加成功发送的包数num++;        // 累加总发送包数}// 结束计时gettimeofday(&tv, NULL);int endtime = tv.tv_sec * 1000000 + tv.tv_usec; // 转换为微秒int time = endtime - starttime; // 计算总耗时// 打印发送后的统计数据if (rte_eth_stats_get(port_id, &port_stats) == 0) {printf("Final stats:\n");printf("Received packets: %ld    Sent packets: %ld\n", port_stats.ipackets, port_stats.opackets);printf("Received bytes: %ld      Sent bytes: %ld\n", port_stats.ibytes, port_stats.obytes);printf("Receive errors: %ld      Send errors: %ld\n", port_stats.ierrors, port_stats.oerrors);printf("Missed packets: %ld     RX no buffer: %ld\n", port_stats.imissed, port_stats.rx_nombuf);}// 打印发送的总包数和耗时printf("------- Total sent: %d  Count: %d  Time: %d microseconds\n", send_total, count, time);    }return 0;
}int
main(int argc, char **argv)
{int ret;uint32_t nb_lcores;uint32_t nb_ports;unsigned lcoreid;uint8_t  nb_rx_queue, nb_tx_queue;uint16_t nb_rx_desc, nb_tx_desc;struct rte_eth_dev_info default_eth_dev_info_before;struct rte_eth_dev_info default_eth_dev_info_after;struct rte_eth_rxconf default_rxconf;struct rte_eth_txconf default_txconf;struct rte_eth_desc_lim 	rx_desc_lim;struct rte_eth_desc_lim 	tx_desc_lim;nb_rx_queue = 1;    //端口接收队列数量nb_tx_queue = 1;    //端口传输队列数量nb_rx_desc = 128;   //端口接收队列描述符数量nb_tx_desc = 512;   //端口传输队列描述符数量rx_queue_id = 0;    //仅使用接收队列 0 tx_queue_id = 0;    //仅使用传输队列 0 port_id = 0;		//仅使用端口 0 lcore_id = 1;       //仅使用的逻辑核 1force_quit = false;ret = rte_eal_init(argc, argv);if (ret < 0)rte_panic("Cannot init EAL\n");signal(SIGINT, signal_handler);signal(SIGTERM, signal_handler);//端口数量nb_ports = rte_eth_dev_count_total();if (nb_ports > RTE_MAX_ETHPORTS)nb_ports = RTE_MAX_ETHPORTS;//逻辑核数量nb_lcores = rte_lcore_count();printf("number of lcores: %d    number of ports: %d\n", nb_lcores, nb_ports);//主逻辑核 CPU 插槽编号socket_id = rte_lcore_to_socket_id(rte_get_master_lcore());//创建内存池char s[64];//内存池名称snprintf(s, sizeof(s), "mbuf_pool_%d", socket_id);pktmbuf_pool = rte_pktmbuf_pool_create(s,NUM_MBUFS, MBUF_CACHE_SIZE, 0,RTE_MBUF_DEFAULT_BUF_SIZE, socket_id);if (pktmbuf_pool == NULL)rte_exit(EXIT_FAILURE, "Cannot init mbuf pool on socket %d\n", socket_id);elseprintf("Allocated mbuf pool on socket %d\n", socket_id);//获取端口mac 地址rte_eth_macaddr_get(port_id, &src_eth_addrs);print_ethaddr("SRC1  Mac Address:", &src_eth_addrs);rte_eth_macaddr_get(port_id + 1, &eth_addrs);print_ethaddr("SRC2  Mac Address:", &eth_addrs);//目的mac 地址void *tmp;tmp = &des_eth_addrs.addr_bytes[0];//*((uint64_t *)tmp) = (((uint64_t)0x59 << 40) | ((uint64_t)0x41 << 32) | ((uint64_t)0x02 << 24) | ((uint64_t)0x4A << 16) | ((uint64_t)0x53 << 8) | (uint64_t)0x2C);//*((uint64_t *)tmp) = (((uint64_t)0x30 << 40) | ((uint64_t)0x05 << 32) | ((uint64_t)0x05 << 24) | ((uint64_t)0x0A << 16) | ((uint64_t)0x11 << 8) | (uint64_t)0x00);*((uint64_t *)tmp) = (((uint64_t)0xFF << 40) | ((uint64_t)0xFF << 32) | ((uint64_t)0xFF << 24) | ((uint64_t)0xFF << 16) | ((uint64_t)0xFF << 8) | (uint64_t)0xFF);print_ethaddr("DES  Mac Address:", &des_eth_addrs);//端口配置ret = rte_eth_dev_configure(port_id, nb_rx_queue, nb_tx_queue, &port_conf_default);if (ret < 0)rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%d\n",ret, port_id);//检查Rx和Tx描述符的数量是否满足来自以太网设备信息的描述符限制,否则将其调整为边界 nb_rx_desc =128,nb_tx_desc=128ret = rte_eth_dev_adjust_nb_rx_tx_desc(port_id, &nb_rx_desc,&nb_tx_desc);//获取端口默认配置信息rte_eth_dev_info_get(port_id, &default_eth_dev_info_before);//端口 TX 队列配置fflush(stdout);default_txconf = default_eth_dev_info_before.default_txconf;tx_desc_lim = default_eth_dev_info_before.tx_desc_lim;printf("config before ---- tx_free_thresh : %d ,desc_max :%d ,desc_min : %d \n",default_txconf.tx_free_thresh, tx_desc_lim.nb_max, tx_desc_lim.nb_min);default_txconf.tx_free_thresh = (uint16_t) MAX_PKT_BURST;ret = rte_eth_tx_queue_setup(port_id, tx_queue_id, nb_tx_desc, socket_id, NULL);if (ret < 0)rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup: err=%d, port=%d\n", ret, port_id);//端口 RX 队列配置fflush(stdout);default_rxconf = default_eth_dev_info_before.default_rxconf;rx_desc_lim = default_eth_dev_info_before.rx_desc_lim;printf("config before ---- rx_free_thresh : %d ,desc_max :%d ,desc_min : %d \n",default_rxconf.rx_free_thresh, rx_desc_lim.nb_max, rx_desc_lim.nb_min);default_rxconf.rx_free_thresh = (uint16_t) MAX_PKT_BURST;ret = rte_eth_rx_queue_setup(port_id, rx_queue_id, nb_rx_desc, socket_id, NULL, pktmbuf_pool);if (ret < 0)rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup: err=%d,port=%d\n", ret, port_id);rte_delay_ms(5000);//延迟5秒memset(&default_txconf, 0, sizeof(default_txconf));memset(&default_rxconf, 0, sizeof(default_rxconf));memset(&tx_desc_lim, 0, sizeof(tx_desc_lim));memset(&rx_desc_lim, 0, sizeof(rx_desc_lim));//获取端口默认配置信息rte_eth_dev_info_get(port_id, &default_eth_dev_info_after);default_txconf = default_eth_dev_info_after.default_txconf;tx_desc_lim = default_eth_dev_info_after.tx_desc_lim;printf("config after  ---- tx_free_thresh : %d ,desc_max :%d ,desc_min : %d \n",default_txconf.tx_free_thresh, tx_desc_lim.nb_max, tx_desc_lim.nb_min);default_rxconf = default_eth_dev_info_after.default_rxconf;rx_desc_lim = default_eth_dev_info_after.rx_desc_lim;printf("config after  ---- rx_free_thresh : %d ,desc_max :%d ,desc_min : %d \n",default_rxconf.rx_free_thresh, rx_desc_lim.nb_max, rx_desc_lim.nb_min);/*开启端口网卡 */ret = rte_eth_dev_start(port_id);if (ret < 0)rte_exit(EXIT_FAILURE, "rte_eth_dev_start: err=%d, port=%d\n",ret, port_id);printf("started: Port %d\n", port_id);/* 设置端口网卡混杂模式 */rte_eth_promiscuous_enable(port_id);/*等待网卡启动成功*/#define CHECK_INTERVAL 100 /* 100ms */	#define MAX_CHECK_TIME 50 /* 5s (50 * 100ms) in total */uint8_t count;struct rte_eth_link link;for (count = 0; count <= MAX_CHECK_TIME; count++) {if (force_quit)return 0;memset(&link, 0, sizeof(link));rte_eth_link_get_nowait(port_id, &link);if (link.link_status)printf("Port %d Link Up - speed %u Mbps - %s\n", (uint8_t)port_id,(unsigned)link.link_speed,(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?("full-duplex") : ("half-duplex\n"));elseprintf("Port %d Link Down\n",(uint8_t)port_id);rte_delay_ms(CHECK_INTERVAL);}printf("调用逻辑核执行任务\n");/*调用逻辑核执行任务*/rte_eal_mp_remote_launch(app_lcore_main_loop, NULL, CALL_MASTER);/*等待逻辑核退出*/RTE_LCORE_FOREACH_SLAVE(lcoreid) {if (rte_eal_wait_lcore(lcoreid) < 0) {return -1;}}printf("Bye...\n");printf("Closing port %d...\n", port_id);/*停止端口网卡*/rte_eth_dev_stop(port_id);/*关闭端口网卡*/rte_eth_dev_close(port_id);return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com