epoll+MMAP recv

来源:互联网 发布:时间管理书籍 知乎 编辑:程序博客网 时间:2024/06/01 14:27
#include <stdio.h>
#include <string.h> 
#include <stdlib.h> 


/* network */ 
#include <sys/socket.h> 
#include <linux/if_packet.h> 
#include <linux/if_ether.h> 
#include <linux/if_arp.h> 
#include <netdb.h> 
#include <linux/if.h> 
#include <errno.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/mman.h>


typedef unsigned char BYTE;
typedef unsigned short WORD16;
typedef unsigned int WORD32;


#define    MCS_IPV_AND_LEN             0x45
#define    MCS_UDP_TYPE                0x11  /* UDP 报文 */
#define    MCS_ETH_MAC_LEN             6    /* MAC*/


#define    MCS_ETH_TYPE                0x0800




typedef struct
{
    BYTE    ucDstMac[MCS_ETH_MAC_LEN];  /* Destination MAC Address */
    BYTE    ucSrcMac[MCS_ETH_MAC_LEN];  /* Source MAC Address */
    WORD16  wPktType;                   /* Packet Type */
}Mcs_Eth_Hd;


typedef struct
{
    BYTE  ip_vl;            /* ip version & header-len */
    BYTE  ip_tos;           /* type of service */
    WORD16  ip_len;         /* total length */
    WORD16  ip_id;          /* identification */
    WORD16  ip_off;         /* flag & fragment offset field */
    BYTE  ip_ttl;           /* time to live */
    BYTE  ip_pro;           /* protocol */
    WORD16  ip_cksum;       /* checksum */
    WORD32  ip_src;         /* source address */
    WORD32  ip_Dst;         /* destination address */
}Mcs_Ip_Hd;


typedef struct
{
    WORD16  udp_srcport;    /* udp source port */
    WORD16  udp_dstport;    /* udp source port */
    WORD16  udp_len;        /* udp length */
    WORD16  udp_cksum;      /* udp checksum */
}Mcs_Udp_Hd;




int  g_SendSockEth5  = -1;
int  g_EthIndex[7];
struct tpacket_req req;
const int BUFFER_SIZE = 1024*1024*16; //16MB的缓冲区
#define PER_PACKET_SIZE 2048
int socketinit(void)
{
    int Ret = -1;
    struct sockaddr_ll SendAddrll = {0};
    struct ifreq ifr;

    req.tp_block_size = 4096;
    req.tp_block_nr = BUFFER_SIZE/req.tp_block_size;
    req.tp_frame_size = PER_PACKET_SIZE;
    req.tp_frame_nr = BUFFER_SIZE/req.tp_frame_size;


    g_SendSockEth5 = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
    if(g_SendSockEth5  < 0)
    {
        printf("creat socke fail!\n");
        return Ret;
    }


    Ret = setsockopt(g_SendSockEth5, SOL_PACKET, PACKET_RX_RING, (void *)&req, sizeof(req));
    if(Ret<0)
    {
        perror("setsockopt");
        return Ret;
    }

    SendAddrll.sll_family = PF_PACKET;
    SendAddrll.sll_ifindex = if_nametoindex("eth0");                                                                  /**/
    SendAddrll.sll_protocol = htons(ETH_P_IP);


    if(0 > bind(g_SendSockEth5, (struct sockaddr *)&SendAddrll, sizeof(SendAddrll)))
    {
        close(g_SendSockEth5);
        printf("bind fail\n");
        return Ret;
    }


    memset(&ifr, 0, sizeof(struct ifreq));
    strncpy(ifr.ifr_name, "eth0",IFNAMSIZ);
    /* bind eth6 device 
    if(setsockopt(g_SendSockEth5, SOL_SOCKET, SO_BINDTODEVICE, (char *)&ifr, sizeof(ifr)) < 0)
    {
        close(g_SendSockEth5);
        printf("setsockopt fail\n ");
        return Ret;
    }*/


    return 0;
}


int q = 0;
int CallBackPacket(char *TempBuffer)
{
    Mcs_Eth_Hd *rcvpktethdr = NULL;
    Mcs_Ip_Hd *rcvpktiphdr = NULL;
    Mcs_Udp_Hd *rcvpktudphdr = NULL;
    char cIPdotdec[45] = {0};

printf("----------rcv pkts-----------\n");
/* eth 
rcvpktethdr = (Mcs_Eth_Hd *)&TempBuffer[0];
printf("DstMac[%x:%x:%x:%x:%x:%x]\n",rcvpktethdr->ucDstMac[0],rcvpktethdr->ucDstMac[1],rcvpktethdr->ucDstMac[2],rcvpktethdr->ucDstMac[3],rcvpktethdr->ucDstMac[4],rcvpktethdr->ucDstMac[5]);
printf("SrcMac[%x:%x:%x:%x:%x:%x]\n",rcvpktethdr->ucSrcMac[0],rcvpktethdr->ucSrcMac[1],rcvpktethdr->ucSrcMac[2],rcvpktethdr->ucSrcMac[3],rcvpktethdr->ucSrcMac[4],rcvpktethdr->ucSrcMac[5]);
printf("PktType:0x%x\n",ntohs(rcvpktethdr->wPktType));*/
/* ip */
rcvpktiphdr = (Mcs_Ip_Hd *)(TempBuffer);
inet_ntop(AF_INET, (void *)&(rcvpktiphdr->ip_src), cIPdotdec, 40);
printf("b0 SrcIp:%s\n",cIPdotdec);
inet_ntop(AF_INET, (void *)&(rcvpktiphdr->ip_Dst), cIPdotdec, 40);
printf("b0 DstIp:%s\n",cIPdotdec);
/* udp */
rcvpktudphdr = (Mcs_Udp_Hd *)(TempBuffer+sizeof(Mcs_Ip_Hd));
printf("b0 srcPort:%d\n",ntohs(rcvpktudphdr->udp_srcport));
printf("b0 dstPort:%d\n",ntohs(rcvpktudphdr->udp_dstport));
printf("TempBuffer[1000] = %d\n",TempBuffer[1000]);

if(5500 == ntohs(rcvpktudphdr->udp_dstport))
{
q++;
}

printf("q = %d\n",q);
}








#define MAX_EVEVENTS_NUM 20


int main(int argc, char **argv)
{
    struct  sockaddr_ll daddrll;
    int len = 0;
    char TempBuffer[1600] = {0};
struct msghdr msg;
int i =0 ,j = 0;
int epfd = 0;
int nfds = 0;
struct epoll_event ep_event;
struct epoll_event ep_events[MAX_EVEVENTS_NUM];
int ret = 0;
char *buff = NULL;
int nIndex=0;

    if(0 > socketinit())
    {
        return -1;
    }


buff = (char *)mmap(0, BUFFER_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, g_SendSockEth5, 0);
    if(buff == MAP_FAILED)
    {
        perror("mmap");
        return -1;
    }

epfd = epoll_create(MAX_EVEVENTS_NUM); /*size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。*/
ep_event.data.fd = g_SendSockEth5;
ep_event.events = EPOLLIN;//| EPOLLET;
ret = epoll_ctl(epfd,EPOLL_CTL_ADD,g_SendSockEth5,&ep_event);
if(ret == -1)
{
printf("epoll_ctl error");
return;
}


    while(1)
    {
nfds = epoll_wait(epfd,ep_events,MAX_EVEVENTS_NUM,-1);/*3-前需要监听的所有socket句柄数,4- -1单一线程保证效率*/
/*maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size*/
/*参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)*/
if (nfds <= 0)
{
printf("nfds = %d\n",nfds);
continue;
}
for(i=0;i<nfds;++i)
{
if(ep_events[i].events&EPOLLIN)
{
printf("i = %d,nfds = %d\n",i,nfds);


//尽力的去处理环形缓冲区中的数据frame,直到没有数据frame了
for(j=0; j<req.tp_frame_nr; j++)
{
struct tpacket_hdr* pHead = (struct tpacket_hdr*)(buff+ nIndex*PER_PACKET_SIZE);


//XXX: 由于frame都在一个环形缓冲区中,因此如果下一个frame中没有数据了,后面的frame也就没有frame了
if(pHead->tp_status == TP_STATUS_KERNEL)
break;


//处理数据frame
CallBackPacket((char*)pHead+pHead->tp_net);


//重新设置frame的状态为TP_STATUS_KERNEL
pHead->tp_len = 0;
pHead->tp_status = TP_STATUS_KERNEL;


//更新环形缓冲区的索引,指向下一个frame
nIndex++;
nIndex%=req.tp_frame_nr;
}

}
else
printf("events error\n");
}





    }

close(epfd);
close(g_SendSockEth5);

    return 0;
}