DPDK系列之四十二DPDK应用网络编程UDP编程
一、UDP编程
UDP编程的应用和TCP编程的应用同样非常广泛,如果说真得想使用UDP编程,一般情况下还真得不至于运用DPDK这种重量级的框架。但一个框架的优秀与否,不仅仅在于自身的整体设计优秀,更重要的在于其对应用的支持更完善。
正如DPDK对TCP的支持一样,其实对于DPDK这种更侧重于底层的应用来说,实现UDP和TCP没有本质的区别,只是套的一层解析的协议不同罢了。同样,UDP与TCP的不同及其协议的内容如有不明白可自行查看相关资料,此处不再赘述。
二、DPDK实现UDP源码分析
下面看一下例程(代码来源与TCP相同):
1、数据结构和协议
// arp表的单个条目
struct arp_entry
{uint32_t ip;unsigned char hwaddr[RTE_ETHER_ADDR_LEN];unsigned char type;struct arp_entry *next;struct arp_entry *prev;
};// arp表结构
struct arp_table
{struct arp_entry *entries;int count;pthread_spinlock_t spinlock;
};
// udp control block
struct localhost
{int fd;//unsigned int status; //uint32_t localip; // ip --> macunsigned char localmac[RTE_ETHER_ADDR_LEN];uint16_t localport;unsigned char protocol;struct rte_ring *sndbuf;struct rte_ring *rcvbuf;struct localhost *prev;struct localhost *next;pthread_cond_t cond;pthread_mutex_t mutex;
};
//UDP的数据包
struct offload
{uint32_t sip;uint32_t dip;uint16_t sport;uint16_t dport;int protocol;unsigned char *data;uint16_t length;};
//common.c
int ng_arp_entry_insert(uint32_t ip, unsigned char *mac)
{struct arp_table *pstTbl = arp_table_instance();struct arp_entry *pstEntry = NULL;unsigned char *pstHwaddr = NULL;pstHwaddr = ng_get_dst_macaddr(ip);if(pstHwaddr == NULL){pstEntry = rte_malloc("arp_entry", sizeof(struct arp_entry), 0);if (pstEntry){memset(pstEntry, 0, sizeof(struct arp_entry));pstEntry->ip = ip;rte_memcpy(pstEntry->hwaddr, mac, RTE_ETHER_ADDR_LEN);pstEntry->type = 0;pthread_spin_lock(&pstTbl->spinlock);LL_ADD(pstEntry, pstTbl->entries);pstTbl->count ++;pthread_spin_unlock(&pstTbl->spinlock);}return 1;}return 0;
}static struct arp_table *arp_table_instance(void)
{if (g_pstArpTbl == NULL){g_pstArpTbl = rte_malloc("arp table", sizeof(struct arp_table), 0);if (g_pstArpTbl == NULL)rte_exit(EXIT_FAILURE, "rte_malloc arp table failed\n");memset(g_pstArpTbl, 0, sizeof(struct arp_table));pthread_spin_init(&g_pstArpTbl->spinlock, PTHREAD_PROCESS_SHARED);}return g_pstArpTbl;
}unsigned char* ng_get_dst_macaddr(uint32_t dip)
{struct arp_entry *pstIter;struct arp_table *pstTbl = arp_table_instance();int count = pstTbl->count;for (pstIter = pstTbl->entries; count-- != 0 && pstIter != NULL; pstIter = pstIter->next){if (dip == pstIter->ip)return pstIter->hwaddr;}return NULL;
}
UDP数据处理分为两类一类是控制包,一类是负载包也就是数据包。arp表的作用类似于快递小哥的作用,查找IP与MAC的映射并实现数据包的准确传输,即其有两个出口,一个是数据发送时的出口,保存发送时的IP和MAC(没有则先广播);一个是接收时的出口,用来保存收到的数据包对应的IP和MAC。代码没有什么难度,大家对照着相关的协议实现就明白了。
int nsocket(__attribute__((unused)) int domain, int type, __attribute__((unused)) int protocol)
{int iFd;struct localhost *pstHost;pthread_cond_t pctCond = PTHREAD_COND_INITIALIZER;pthread_mutex_t pmtMutex = PTHREAD_MUTEX_INITIALIZER;iFd = get_fd_frombitmap();if(type == SOCK_DGRAM) // udp{pstHost = rte_malloc("localhost", sizeof(struct localhost), 0);if(pstHost == NULL){printf("[%s][%d]: rte_malloc fail!\n", __FUNCTION__, __LINE__);return -1;}memset(pstHost, 0x00, sizeof(struct localhost));pstHost->fd = iFd;pstHost->protocol = IPPROTO_UDP;pstHost->rcvbuf = rte_ring_create("recv buffer", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);if (pstHost->rcvbuf == NULL){printf("[%s][%d]: rte_ring_create fail!\n", __FUNCTION__, __LINE__);rte_free(pstHost);return -1;}pstHost->sndbuf = rte_ring_create("send buffer", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);if (pstHost->sndbuf == NULL){printf("[%s][%d]: rte_ring_create fail!\n", __FUNCTION__, __LINE__);rte_ring_free(pstHost->rcvbuf);rte_free(pstHost);return -1;}rte_memcpy(&pstHost->cond, &pctCond, sizeof(pthread_cond_t));rte_memcpy(&pstHost->mutex, &pmtMutex, sizeof(pthread_mutex_t));LL_ADD(pstHost, g_pstHost);}else if(type == SOCK_STREAM) // tcp{struct tcp_stream *pstStream = rte_malloc("tcp_stream", sizeof(struct tcp_stream), 0);if (pstStream == NULL)return -1;memset(pstStream, 0, sizeof(struct tcp_stream));pstStream->fd = iFd;pstStream->protocol = IPPROTO_TCP;pstStream->next = pstStream->prev = NULL;pstStream->rcvbuf = rte_ring_create("tcp recv buffer", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);if (pstStream->rcvbuf == NULL){rte_free(pstStream);return -1;}pstStream->sndbuf = rte_ring_create("tcp send buffer", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);if (pstStream->sndbuf == NULL){rte_ring_free(pstStream->rcvbuf);rte_free(pstStream);return -1;}pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;rte_memcpy(&pstStream->cond, &blank_cond, sizeof(pthread_cond_t));pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;rte_memcpy(&pstStream->mutex, &blank_mutex, sizeof(pthread_mutex_t));g_pstTcpTbl = tcpInstance();LL_ADD(pstStream, g_pstTcpTbl->tcb_set); // todo :hash}return iFd;
}
int nbind(int sockfd, const struct sockaddr *addr, __attribute__((unused)) socklen_t addrlen)
{void *info = NULL;info = get_hostinfo_fromfd(sockfd);if(info == NULL)return -1;struct localhost *pstHostInfo = (struct localhost *)info;if(pstHostInfo->protocol == IPPROTO_UDP){const struct sockaddr_in *pstAddr = (const struct sockaddr_in *)addr;pstHostInfo->localport = pstAddr->sin_port;rte_memcpy(&pstHostInfo->localip, &pstAddr->sin_addr.s_addr, sizeof(uint32_t));rte_memcpy(pstHostInfo->localmac, &g_stCpuMac, RTE_ETHER_ADDR_LEN);}else if(pstHostInfo->protocol == IPPROTO_TCP){struct tcp_stream* pstStream = (struct tcp_stream*)pstHostInfo;const struct sockaddr_in *pstAddr = (const struct sockaddr_in *)addr;pstStream->dport = pstAddr->sin_port;rte_memcpy(&pstStream->dip, &pstAddr->sin_addr.s_addr, sizeof(uint32_t));rte_memcpy(pstStream->localmac, &g_stCpuMac, RTE_ETHER_ADDR_LEN);pstStream->status = TCP_STATUS_CLOSED;}return 0;
}
ssize_t nrecvfrom(int sockfd, void *buf, size_t len, __attribute__((unused)) int flags,struct sockaddr *src_addr, __attribute__((unused)) socklen_t *addrlen)
{struct localhost *pstHostInfo = NULL;struct offload *pstOffLoad = NULL;struct sockaddr_in *pstAddr = NULL;unsigned char *pucPtr = NULL;int iLen = 0;int iRet = -1;pstHostInfo = (struct localhost *)get_hostinfo_fromfd(sockfd);if(pstHostInfo == NULL)return -1;pthread_mutex_lock(&pstHostInfo->mutex);while((iRet = rte_ring_mc_dequeue(pstHostInfo->rcvbuf, (void**)&pstOffLoad)) < 0){pthread_cond_wait(&pstHostInfo->cond, &pstHostInfo->mutex);}pthread_mutex_unlock(&pstHostInfo->mutex);pstAddr = (struct sockaddr_in *)src_addr;pstAddr->sin_port = pstOffLoad->sport;rte_memcpy(&pstAddr->sin_addr.s_addr, &pstOffLoad->sip, sizeof(uint32_t));if(len < pstOffLoad->length){rte_memcpy(buf, pstOffLoad->data, len);pucPtr = rte_malloc("unsigned char *", pstOffLoad->length - len, 0);rte_memcpy(pucPtr, pstOffLoad->data + len, pstOffLoad->length - len);pstOffLoad->length -= len;rte_free(pstOffLoad->data);pstOffLoad->data = pucPtr;rte_ring_mp_enqueue(pstHostInfo->rcvbuf, pstOffLoad);return len;}iLen = pstOffLoad->length;rte_memcpy(buf, pstOffLoad->data, pstOffLoad->length);rte_free(pstOffLoad->data);rte_free(pstOffLoad);return iLen;
} ssize_t nsendto(int sockfd, const void *buf, size_t len, __attribute__((unused)) int flags,const struct sockaddr *dest_addr, __attribute__((unused)) socklen_t addrlen)
{struct localhost *pstHostInfo = NULL;struct offload *pstOffLoad = NULL;const struct sockaddr_in *pstAddr = (const struct sockaddr_in *)dest_addr;pstHostInfo = (struct localhost *)get_hostinfo_fromfd(sockfd);if(pstHostInfo == NULL)return -1;pstOffLoad = rte_malloc("offload", sizeof(struct offload), 0);if (pstOffLoad == NULL)return -1;pstOffLoad->dip = pstAddr->sin_addr.s_addr;pstOffLoad->dport = pstAddr->sin_port;pstOffLoad->sip = pstHostInfo->localip;pstOffLoad->sport = pstHostInfo->localport;pstOffLoad->length = len;struct in_addr addr;addr.s_addr = pstOffLoad->dip;printf("nsendto ---> src: %s:%d \n", inet_ntoa(addr), ntohs(pstOffLoad->dport));pstOffLoad->data = rte_malloc("unsigned char *", len, 0);if (pstOffLoad->data == NULL) {rte_free(pstOffLoad);return -1;}rte_memcpy(pstOffLoad->data, buf, len);puts("rte_ring_mp_enqueue before !");rte_ring_mp_enqueue(pstHostInfo->sndbuf, pstOffLoad);puts("rte_ring_mp_enqueue after !");return len;
}int nclose(int fd)
{void *info = NULL;info = (struct localhost *)get_hostinfo_fromfd(fd);if(info == NULL)return -1;struct localhost *pstHostInfo = (struct localhost *)info;if(pstHostInfo->protocol == IPPROTO_UDP){LL_REMOVE(pstHostInfo, g_pstHost);if (pstHostInfo->rcvbuf)rte_ring_free(pstHostInfo->rcvbuf);if (pstHostInfo->sndbuf)rte_ring_free(pstHostInfo->sndbuf);rte_free(pstHostInfo);set_fd_frombitmap(fd);}else if(pstHostInfo->protocol == IPPROTO_TCP){struct tcp_stream *pstStream = (struct tcp_stream*)info;if (pstStream->status != TCP_STATUS_LISTEN){struct tcp_fragment *pstFragment = rte_malloc("tcp_fragment", sizeof(struct tcp_fragment), 0);if (pstFragment == NULL)return -1;memset(pstFragment, 0x00, sizeof(struct tcp_fragment));pstFragment->data = NULL;pstFragment->length = 0;pstFragment->sport = pstStream->dport;pstFragment->dport = pstStream->sport;pstFragment->seqnum = pstStream->snd_nxt;pstFragment->acknum = pstStream->rcv_nxt;pstFragment->tcp_flags = RTE_TCP_FIN_FLAG | RTE_TCP_ACK_FLAG; // 发送FINpstFragment->windows = D_TCP_INITIAL_WINDOW;pstFragment->hdrlen_off = 0x50;rte_ring_mp_enqueue(pstStream->sndbuf, pstFragment);pstStream->status = TCP_STATUS_LAST_ACK;set_fd_frombitmap(fd);}else{LL_REMOVE(pstStream, g_pstTcpTbl->tcb_set);rte_free(pstStream);}}return 0;
}
2、接口实现
#include "udp.h"
int udp_process(struct rte_mbuf *pstUdpMbuf)
{struct rte_ipv4_hdr *pstIpHdr;struct rte_udp_hdr *pstUdpHdr;struct localhost *pstHost;struct offload *pstOffLoad;pstIpHdr = rte_pktmbuf_mtod_offset(pstUdpMbuf, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));pstUdpHdr = (struct rte_udp_hdr *)(pstIpHdr + 1);pstHost = get_hostinfo_fromip_port(pstIpHdr->dst_addr, pstUdpHdr->dst_port, pstIpHdr->next_proto_id);if (pstHost == NULL){rte_pktmbuf_free(pstUdpMbuf);return -3;}struct in_addr addr;addr.s_addr = pstIpHdr->src_addr;printf("udp_process ---> src: %s:%d \n", inet_ntoa(addr), ntohs(pstUdpHdr->src_port));pstOffLoad = rte_malloc("offload", sizeof(struct offload), 0);if (pstOffLoad == NULL){rte_pktmbuf_free(pstUdpMbuf);return -1;}pstOffLoad->dip = pstIpHdr->dst_addr;pstOffLoad->sip = pstIpHdr->src_addr;pstOffLoad->sport = pstUdpHdr->src_port;pstOffLoad->dport = pstUdpHdr->dst_port;pstOffLoad->protocol = IPPROTO_UDP;pstOffLoad->length = ntohs(pstUdpHdr->dgram_len);pstOffLoad->data = rte_malloc("unsigned char*", pstOffLoad->length - sizeof(struct rte_udp_hdr), 0);if (pstOffLoad->data == NULL){rte_pktmbuf_free(pstUdpMbuf);rte_free(pstOffLoad);return -2;}rte_memcpy(pstOffLoad->data, (unsigned char *)(pstUdpHdr+1), pstOffLoad->length - sizeof(struct rte_udp_hdr));rte_ring_mp_enqueue(pstHost->rcvbuf, pstOffLoad); // recv bufferpthread_mutex_lock(&pstHost->mutex);pthread_cond_signal(&pstHost->cond);pthread_mutex_unlock(&pstHost->mutex);rte_pktmbuf_free(pstUdpMbuf);return 0;
}static int ng_encode_udp_apppkt(uint8_t *msg, uint32_t sip, uint32_t dip,uint16_t sport, uint16_t dport, uint8_t *srcmac, uint8_t *dstmac,unsigned char *data, uint16_t total_len)
{struct rte_ether_hdr *pstEth;struct rte_ipv4_hdr *pstIp;struct rte_udp_hdr *pstUdp;// 1 ethhdrpstEth = (struct rte_ether_hdr *)msg;rte_memcpy(pstEth->s_addr.addr_bytes, srcmac, RTE_ETHER_ADDR_LEN);rte_memcpy(pstEth->d_addr.addr_bytes, dstmac, RTE_ETHER_ADDR_LEN);pstEth->ether_type = htons(RTE_ETHER_TYPE_IPV4);// 2 iphdrpstIp = (struct rte_ipv4_hdr *)(pstEth + 1);pstIp->version_ihl = 0x45;pstIp->type_of_service = 0;pstIp->total_length = htons(total_len - sizeof(struct rte_ether_hdr));pstIp->packet_id = 0;pstIp->fragment_offset = 0;pstIp->time_to_live = 64; // ttl = 64pstIp->next_proto_id = IPPROTO_UDP;pstIp->src_addr = sip;pstIp->dst_addr = dip;pstIp->hdr_checksum = 0;pstIp->hdr_checksum = rte_ipv4_cksum(pstIp);// 3 udphdrpstUdp = (struct rte_udp_hdr *)(pstIp + 1);pstUdp->src_port = sport;pstUdp->dst_port = dport;uint16_t udplen = total_len - sizeof(struct rte_ether_hdr) - sizeof(struct rte_ipv4_hdr);pstUdp->dgram_len = htons(udplen);rte_memcpy((uint8_t*)(pstUdp + 1), data, udplen);pstUdp->dgram_cksum = 0;pstUdp->dgram_cksum = rte_ipv4_udptcp_cksum(pstIp, pstUdp);return 0;
}static struct rte_mbuf * ng_udp_pkt(struct rte_mempool *mbuf_pool, uint32_t sip, uint32_t dip,uint16_t sport, uint16_t dport, unsigned char *srcmac, unsigned char *dstmac,unsigned char *data, uint16_t length)
{unsigned int uiTotalLen;struct rte_mbuf *pstMbuf;unsigned char *pucPktData;uiTotalLen = length + 42; // 42 = eth + ippstMbuf = rte_pktmbuf_alloc(mbuf_pool);if (!pstMbuf)rte_exit(EXIT_FAILURE, "rte_pktmbuf_alloc\n");pstMbuf->pkt_len = uiTotalLen;pstMbuf->data_len = uiTotalLen;pucPktData = rte_pktmbuf_mtod(pstMbuf, unsigned char*);ng_encode_udp_apppkt(pucPktData, sip, dip, sport, dport, srcmac, dstmac,data, uiTotalLen);return pstMbuf;
}int udp_out(struct rte_mempool *pstMbufPool)
{struct localhost *pstHost;for(pstHost = g_pstHost; pstHost != NULL; pstHost = pstHost->next){struct offload *pstOffLoad = NULL;int iSendCnt = rte_ring_mc_dequeue(pstHost->sndbuf, (void **)&pstOffLoad);if(iSendCnt < 0)continue;struct in_addr addr;addr.s_addr = pstOffLoad->dip;printf("udp_out ---> src: %s:%d \n", inet_ntoa(addr), ntohs(pstOffLoad->dport));unsigned char *dstmac = ng_get_dst_macaddr(pstOffLoad->dip); // 查询对端mac地址if (dstmac == NULL) // 先广播发个arp包确定对端mac地址{struct rte_mbuf *pstArpbuf = ng_send_arp(pstMbufPool, RTE_ARP_OP_REQUEST, g_aucDefaultArpMac,pstOffLoad->sip, pstOffLoad->dip);rte_ring_mp_enqueue_burst(g_pstRingIns->pstOutRing, (void **)&pstArpbuf, 1, NULL);rte_ring_mp_enqueue(pstHost->sndbuf, pstOffLoad); // 将取出的udp数据再次写入队列}else{struct rte_mbuf *pstUdpbuf = ng_udp_pkt(pstMbufPool, pstOffLoad->sip, pstOffLoad->dip,pstOffLoad->sport, pstOffLoad->dport, pstHost->localmac,dstmac, pstOffLoad->data, pstOffLoad->length);rte_ring_mp_enqueue_burst(g_pstRingIns->pstOutRing, (void **)&pstUdpbuf, 1, NULL);if (pstOffLoad->data != NULL)rte_free(pstOffLoad->data);rte_free(pstOffLoad);}}return 0;
}
机制和TCP类似。
3、上层应用
//netfamily.c
unsigned char g_aucDefaultArpMac[RTE_ETHER_ADDR_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};unsigned char g_ucFdTable[D_MAX_FD_COUNT] = {0};static struct St_InOut_Ring *ringInstance(void)
{if (g_pstRingIns == NULL){g_pstRingIns = rte_malloc("in/out ring", sizeof(struct St_InOut_Ring), 0);memset(g_pstRingIns, 0, sizeof(struct St_InOut_Ring));}return g_pstRingIns;
}void ng_init_port(struct rte_mempool *pstMbufPoolPub)
{unsigned int uiPortsNum;const int iRxQueueNum = 1;const int iTxQueueNum = 1;int iRet;struct rte_eth_dev_info stDevInfo;struct rte_eth_txconf stTxConf;struct rte_eth_conf stPortConf = // 端口配置信息{.rxmode = {.max_rx_pkt_len = 1518 } // RTE_ETHER_MAX_LEN = 1518};uiPortsNum = rte_eth_dev_count_avail();if (uiPortsNum == 0)rte_exit(EXIT_FAILURE, "No Supported eth found\n");rte_eth_dev_info_get(D_PORT_ID, &stDevInfo);// 配置以太网设备rte_eth_dev_configure(D_PORT_ID, iRxQueueNum, iTxQueueNum, &stPortConf);iRet = rte_eth_rx_queue_setup(D_PORT_ID, 0 , 1024, rte_eth_dev_socket_id(D_PORT_ID), NULL, pstMbufPoolPub);if(iRet < 0)rte_exit(EXIT_FAILURE, "Could not setup RX queue!\n");stTxConf = stDevInfo.default_txconf;stTxConf.offloads = stPortConf.txmode.offloads;iRet = rte_eth_tx_queue_setup(D_PORT_ID, 0 , 1024, rte_eth_dev_socket_id(D_PORT_ID), &stTxConf);if (iRet < 0)rte_exit(EXIT_FAILURE, "Could not setup TX queue\n");if (rte_eth_dev_start(D_PORT_ID) < 0 )rte_exit(EXIT_FAILURE, "Could not start\n");rte_eth_promiscuous_enable(D_PORT_ID);
}static int ng_config_network_if(uint16_t port_id, unsigned char if_up) {if (!rte_eth_dev_is_valid_port(port_id)) {return -EINVAL;}int ret = 0;if (if_up) {rte_eth_dev_stop(port_id);ret = rte_eth_dev_start(port_id);} else {rte_eth_dev_stop(port_id);}if (ret < 0) {printf("Failed to start port : %d\n", port_id);}return 0;
}static struct rte_kni *ng_alloc_kni(struct rte_mempool *mbuf_pool) {struct rte_kni *kni_hanlder = NULL;struct rte_kni_conf conf;memset(&conf, 0, sizeof(conf));snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u", D_PORT_ID);conf.group_id = D_PORT_ID;conf.mbuf_size = D_MAX_PACKET_SIZE;rte_eth_macaddr_get(D_PORT_ID, (struct rte_ether_addr *)conf.mac_addr);rte_eth_dev_get_mtu(D_PORT_ID, &conf.mtu);// print_ethaddr("ng_alloc_kni: ", (struct ether_addr *)conf.mac_addr);/*struct rte_eth_dev_info dev_info;memset(&dev_info, 0, sizeof(dev_info));rte_eth_dev_info_get(D_PORT_ID, &dev_info);*/struct rte_kni_ops ops;memset(&ops, 0, sizeof(ops));ops.port_id = D_PORT_ID;ops.config_network_if = ng_config_network_if;kni_hanlder = rte_kni_alloc(mbuf_pool, &conf, &ops);if (!kni_hanlder) {rte_exit(EXIT_FAILURE, "Failed to create kni for port : %d\n", D_PORT_ID);}return kni_hanlder;
}static int pkt_process(void *arg)
{struct rte_mempool *pstMbufPool;int iRxNum;int i;struct rte_ether_hdr *pstEthHdr;struct rte_ipv4_hdr *pstIpHdr;pstMbufPool = (struct rte_mempool *)arg;while(1){struct rte_mbuf *pstMbuf[32];iRxNum = rte_ring_mc_dequeue_burst(g_pstRingIns->pstInRing, (void**)pstMbuf, D_BURST_SIZE, NULL);if(iRxNum <= 0)continue;for(i = 0; i < iRxNum; ++i){pstEthHdr = rte_pktmbuf_mtod_offset(pstMbuf[i], struct rte_ether_hdr *, 0);if (pstEthHdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) //IPv4: 0800{pstIpHdr = rte_pktmbuf_mtod_offset(pstMbuf[i], struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));// 维护一个arp表ng_arp_entry_insert(pstIpHdr->src_addr, pstEthHdr->s_addr.addr_bytes);if(pstIpHdr->next_proto_id == IPPROTO_UDP) // udp{// udp processudp_process(pstMbuf[i]);}else if(pstIpHdr->next_proto_id == IPPROTO_TCP) // tcp{// printf("tcp_process ---\n");tcp_process(pstMbuf[i]);}else{rte_kni_tx_burst(g_pstKni, pstMbuf, iRxNum);// printf("tcp/udp --> rte_kni_handle_request\n");}}else{// ifconfig vEth0 192.168.181.169 uprte_kni_tx_burst(g_pstKni, pstMbuf, iRxNum);// printf("ip --> rte_kni_handle_request\n");} }rte_kni_handle_request(g_pstKni);// to sendudp_out(pstMbufPool);tcp_out(pstMbufPool);}return 0;
}int udp_server_entry(__attribute__((unused)) void *arg)
{ int iConnfd;struct sockaddr_in stLocalAddr, stClientAddr;socklen_t uiAddrLen = sizeof(stClientAddr);;char acBuf[D_UDP_BUFFER_SIZE] = {0};iConnfd = nsocket(AF_INET, SOCK_DGRAM, 0);if (iConnfd == -1){printf("nsocket failed\n");return -1;}memset(&stLocalAddr, 0, sizeof(struct sockaddr_in));stLocalAddr.sin_port = htons(8889);stLocalAddr.sin_family = AF_INET;stLocalAddr.sin_addr.s_addr = inet_addr("192.168.181.169");nbind(iConnfd, (struct sockaddr*)&stLocalAddr, sizeof(stLocalAddr));while (1){if (nrecvfrom(iConnfd, acBuf, D_UDP_BUFFER_SIZE, 0,(struct sockaddr*)&stClientAddr, &uiAddrLen) < 0){continue;}else{printf("recv from %s:%d, data:%s\n", inet_ntoa(stClientAddr.sin_addr),ntohs(stClientAddr.sin_port), acBuf);nsendto(iConnfd, acBuf, strlen(acBuf), 0,(struct sockaddr*)&stClientAddr, sizeof(stClientAddr));}}nclose(iConnfd);return 0;
}int main(int argc, char *argv[])
{struct rte_mempool *pstMbufPoolPub;struct St_InOut_Ring *pstRing;struct rte_mbuf *pstRecvMbuf[32] = {NULL};struct rte_mbuf *pstSendMbuf[32] = {NULL};int iRxNum;int iTotalNum;int iOffset;int iTxNum;unsigned int uiCoreId;if(rte_eal_init(argc, argv) < 0)rte_exit(EXIT_FAILURE, "Error with EAL init\n");pstMbufPoolPub = rte_pktmbuf_pool_create("MBUF_POOL_PUB", D_NUM_MBUFS, 0, 0,RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());if(pstMbufPoolPub == NULL){printf("rte_errno = %x, errmsg = %s\n", rte_errno, rte_strerror(rte_errno));return -1;}if (-1 == rte_kni_init(D_PORT_ID))rte_exit(EXIT_FAILURE, "kni init failed\n");ng_init_port(pstMbufPoolPub);g_pstKni = ng_alloc_kni(pstMbufPoolPub);// ng_init_port(pstMbufPoolPub);rte_eth_macaddr_get(D_PORT_ID, &g_stCpuMac);pstRing = ringInstance();if(pstRing == NULL)rte_exit(EXIT_FAILURE, "ring buffer init failed\n");pstRing->pstInRing = rte_ring_create("in ring", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);pstRing->pstOutRing = rte_ring_create("out ring", D_RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);uiCoreId = rte_lcore_id();uiCoreId = rte_get_next_lcore(uiCoreId, 1, 0);rte_eal_remote_launch(pkt_process, pstMbufPoolPub, uiCoreId);uiCoreId = rte_get_next_lcore(uiCoreId, 1, 0);rte_eal_remote_launch(udp_server_entry, pstMbufPoolPub, uiCoreId);uiCoreId = rte_get_next_lcore(uiCoreId, 1, 0);rte_eal_remote_launch(tcp_server_entry, pstMbufPoolPub, uiCoreId);while (1){// rxiRxNum = rte_eth_rx_burst(D_PORT_ID, 0, pstRecvMbuf, D_BURST_SIZE);if(iRxNum > 0)rte_ring_sp_enqueue_burst(pstRing->pstInRing, (void**)pstRecvMbuf, iRxNum, NULL);// txiTotalNum = rte_ring_sc_dequeue_burst(pstRing->pstOutRing, (void**)pstSendMbuf, D_BURST_SIZE, NULL);if(iTotalNum > 0){iOffset = 0;while(iOffset < iTotalNum){iTxNum = rte_eth_tx_burst(D_PORT_ID, 0, &pstSendMbuf[iOffset], iTotalNum - iOffset);if(iTxNum > 0)iOffset += iTxNum;}}}}
在前面的应用中,其实可以发现处理的TCP和UDP的数据有的是写在一个函数里了,这其实是从设计上看不是很好,以后不容易扩展,可它也有好处啊,容易让人明白啊。更多的依赖代码可以参看GITHUB上的开源地址。
四、总结
UDP其实在普通的网络应用中,基本用来做一些辅助的工作,如果地址发现、广播简单消息等等。但由于它的占用资源少,在一些场景下就能发挥很大作用。包括在前文提到的QUIC其实都借鉴了UDP的一些特点。所以是不是觉得,很多技术其实并不是完全隔离的,它们会不断的整合、独立再如此循环。所以以后别的公司会不会也会也一个类似于DPDK的框架呢?拭目以待!
相关文章:
DPDK系列之四十二DPDK应用网络编程UDP编程
一、UDP编程 UDP编程的应用和TCP编程的应用同样非常广泛,如果说真得想使用UDP编程,一般情况下还真得不至于运用DPDK这种重量级的框架。但一个框架的优秀与否,不仅仅在于自身的整体设计优秀,更重要的在于其对应用的支持更完善。 正…...
金三银四面试题(十九):MySQL中的锁
在MySQL中,锁是非常重要的,特别是在多用户并发访问数据库的环境中,因此也是面试中常问的话题。 请说说数据库的锁? 关于MySQL 的锁机制,可能会问很多问题,不过这也得看面试官在这方面的知识储备。 MySQL …...
【JavaScript】原型链/作用域/this指针/闭包
1.原型链 参考资料:Annotated ES5 ECMAScript起初并不支持如C、Smalltalk 或 Java 中“类”的形式创建对象,而是通过字面量表示法或者构造函数创建对象。每个构造函数都是一个具有名为“prototype”的属性的函数,该属性用于实现基于原型的继…...
Python的MATLAB使用
Python和MATLAB是两种不同的编程语言,它们各自拥有不同的生态系统和库。然而,你可以在Python中使用一些方法来实现与MATLAB类似的功能。以下是一些方法和库,可以帮助你在Python中实现MATLAB风格的编程: 1. NumPy: NumPy是Python中…...
文件输入/输出流(I/O)
文章目录 前言一、文件输入\输出流是什么?二、使用方法 1.FileInputStream与FileOutputStream类2.FileReader与FileWriter类总结 前言 对于文章I/O(输入/输出流的概述),有了下文。这篇文章将具体详细展述如何向磁盘文件中输入数据,或者读取磁…...
docker,schedule job和environment variables三者的含义与区别
这三个概念在软件开发和部署中扮演着不同的角色: Docker一般长这样:superlifestyle/sscp-api Schedule Job一般长这样:recorrect_ocr_receipt_status 、Sync2D365 Environment Variables一般长这样:D365_BATCH_OPERATION_SIZE ima…...
90天玩转Python—16—基础知识篇:面向对象知识详解
90天玩转Python系列文章目录 90天玩转Python—01—基础知识篇:C站最全Python标准库总结 90天玩转Python--02--基础知识篇:初识Python与PyCharm 90天玩转Python—03—基础知识篇:Python和PyCharm(语言特点、学习方法、工具安装) 90天玩转Python—04—基础知识篇:Pytho…...
python 标准库之openpyxl的常规操作
目录 openpyxl(Excel文件处理模块) 读sheet 读sheet中单元格 合并单元格 openpyxl模块基本用法 安装方法 基本使用 读取Excel文档 (一)获取工作表 (二)获取单元格 (三)获取…...
90天玩转Python—12—基础知识篇:Python自动化操作Email:发送邮件、收邮件与邮箱客户端操作全解析
90天玩转Python系列文章目录 90天玩转Python—01—基础知识篇:C站最全Python标准库总结 90天玩转Python--02--基础知识篇:初识Python与PyCharm 90天玩转Python—03—基础知识篇:Python和PyCharm(语言特点、学习方法、工具安装) 90天玩转Python—04—基础知识篇:Pytho…...
利用lidar_align来进行lidar和imu标定
文章目录 下载并安装lidar_align安装nlopt迁移NLOPTConfig.cmake修改loader.cpp文件编译并运行 下载并安装lidar_align mkdir -p lidar_align/src cd lidar_align/src git clone https://github.com/ethz-asl/lidar_align.git安装nlopt git clone http://github.com/steven…...
牛客NC93 设计LRU缓存结构【hard 链表,Map Java】
题目 题目链接: https://www.nowcoder.com/practice/5dfded165916435d9defb053c63f1e84 思路 双向链表map最新的数据放头结点,尾节点放最老的数据,没次移除尾巴节点本地考察链表的新增,删除,移动节点参考答案Java im…...
机器学习和深度学习 -- 李宏毅(笔记与个人理解1-6)
机器学习和深度学习教程 – 李宏毅(笔记与个人理解) day1 课程内容 什么是机器学习 找函数关键技术(深度学习) 函数 – 类神经网络来表示 ;输入输出可以是 向量或者矩阵等如何找到函数: supervised Lear…...
低功耗全极霍尔开关芯片 D02,磁性开关点精确,对工艺和温度变化不敏感
1、概述 D02 是一款低功耗全极霍尔开关,用于检测施加的磁通量密度,并提供一个数字输出,该输出指示所感测磁通量幅度的当前状态。这些应用的一个例子是翻盖手机中的 ON/OFF 开关。微功耗设计特别适合电池供电系统,如手机或笔记本电…...
初识--数据结构
什么是数据结构?我们为什么要学习数据结构呢....一系列的问题就促使我们不得不了解数据结构。我们不禁要问了,学习C语言不就够了吗?为什么还要学习数据结构呢?这是因为:数据结构能够解决C语言解决不了的问题࿰…...
人工智能前沿成科技竞争新高地
以下文章来源:经济参考报 近日,首届中国具身智能大会(CEAI 2024)在上海举行。作为人工智能领域的前沿热点,具身智能正逐步走进现实,成为当前全球科技竞争的新高地、未来产业的新赛道、经济发展的新引擎。 “…...
【算法刷题day23】Leetcode:669. 修剪二叉搜索树、108. 将有序数组转换为二叉搜索树、538. 把二叉搜索树转换为累加树
文章目录 Leetcode 669. 修剪二叉搜索树解题思路代码总结 Leetcode 108. 将有序数组转换为二叉搜索树解题思路代码总结 Leetcode 538. 把二叉搜索树转换为累加树解题思路代码总结 草稿图网站 java的Deque Leetcode 669. 修剪二叉搜索树 题目:669. 修剪二叉搜索树 解…...
设计一个会议管理系统100问?
会议管理系统的基本功能有哪些?如何确保会议管理系统的安全性?会议管理系统可以支持多少种不同类型的会议?是否有权限管理功能?是否支持会议室预订功能?会议管理系统可以导入外部参与者信息吗?是否支持多种…...
一文搞懂BI、ERP、MES、SCM、PLM、CRM、WMS、APS、SCADA、QMS
在企业信息化数字化过程中我们经常遇到很多系统,比如:MES、ERP、SCM、WMS、APS、SCADA、PLM、QMS、CRM、EAM、BI,这些都是什么系统?有什么功能和作用?它们之间的关系是怎样的? 今天就一文简单分享。 术语 …...
全量知识系统 程序详细设计 之 先验逻辑-实现:从“平凡”回到“平凡” (QA 百度搜索)
Q1. 思考:数学中的平凡,和程序中的平凡(比如POJO)、语言中的平凡(比如纯文本),数据中的平凡(比如 Number)。因为我设计中的全知系统将设计的三个方面刻画为语言设计、程序…...
注解(Annotation) --java学习笔记
注解 就是Java代码里的特殊标记,比如:Override、Test等,作用是:让其他程序根据注解信息来决定怎么执行该程序注意:注解可以用在类上、构造器上、方法上、成员变量上、参数上、等位置处 自定义注解 就是自己定义注解 自定义注解到底该怎么写:…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
